Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c673be4
renovate: bump toolchain/nix, macOS support, dependency refresh (#509)
XiangpengHao Sep 1, 2026
1909d08
datafusion 55 (#510)
XiangpengHao Sep 1, 2026
4761d52
Make CI benchmark comparison robust (median warm metrics + filesystem…
XiangpengHao Sep 2, 2026
cc9ed21
fix(reader): do not apply limit before the row filter (#512)
eddietejeda Sep 2, 2026
a0dadd7
Morsel based parquet loader, part 1 (#513)
XiangpengHao Sep 3, 2026
7554acd
implement morsel reader (#514)
XiangpengHao Sep 3, 2026
feb0fd9
fix high impact bugs (#515)
XiangpengHao Sep 3, 2026
3c89a8d
Remove squeeze/hybrid storage path; replace with eviction-only flow a…
XiangpengHao Sep 4, 2026
bd2a4e3
Switch to cache vortex array (#517)
XiangpengHao Sep 4, 2026
7c4933f
update readme (#518)
XiangpengHao Sep 4, 2026
328e176
clean metadata cache (#519)
XiangpengHao Sep 4, 2026
0033b15
fix client and server lineage pushdown (#520)
XiangpengHao Sep 19, 2026
35636a3
feat(admission): footprint-based cache admission gate
anoop-narang Sep 23, 2026
749b6ef
test(reader): pin conjunct-pushdown correctness
anoop-narang Sep 23, 2026
007273a
fix(reader): read the cache in cache-sized batches
anoop-narang Sep 23, 2026
5fe914b
fix(optimizer): keep scans that need virtual columns on ParquetSource
anoop-narang Sep 23, 2026
0ad8622
fix(cache): serve entries only to their own file
anoop-narang Sep 23, 2026
e048b66
chore: add default CODEOWNERS
anoop-narang Sep 23, 2026
c70c6fd
fix(cache): reclaim disk a write strands when a key changes hands
anoop-narang Sep 23, 2026
5769ea1
fix(reader): keep column-free conjuncts in the row filter
anoop-narang Sep 23, 2026
fb7bf08
fix(cache): reclaim the disk copy a memory entry displaces
anoop-narang Sep 23, 2026
2846df1
chore: adopt upstream's history as our base
anoop-narang Sep 24, 2026
5450d04
fix(cache): release a disk copy this write overwrote in place
anoop-narang Sep 24, 2026
315f474
fix(dev-tools): teach the trace parser about disk_evict
anoop-narang Sep 24, 2026
be1797e
chore(cache): drop a stray doc line and test debug output
anoop-narang Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions .github/compare_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import json
import sys
import argparse
import statistics
from typing import Dict, List, Any


Expand Down Expand Up @@ -47,18 +48,22 @@ def get_cold_metrics(iteration_results: List[Dict[str, Any]]) -> Dict[str, float


def get_warm_metrics(iteration_results: List[Dict[str, Any]]) -> Dict[str, float]:
"""Calculate average metrics from warm iterations (excluding first)."""
"""Calculate median metrics from warm iterations (excluding first).

CI runners occasionally pause a process while it is being timed. A median
keeps one such pause from turning into a reported regression.
"""
warm_results = iteration_results[1:] if len(iteration_results) > 1 else iteration_results
if not warm_results:
return {"time_millis": 0, "cache_cpu_time": 0}

avg_time = sum(r["time_millis"] for r in warm_results) / len(warm_results)
avg_cpu_time = sum(r.get("cache_cpu_time", 0) for r in warm_results) / len(warm_results)
median_time = statistics.median(r["time_millis"] for r in warm_results)
median_cpu_time = statistics.median(r.get("cache_cpu_time", 0) for r in warm_results)
# No memory column in report

return {
"time_millis": avg_time,
"cache_cpu_time": avg_cpu_time,
"time_millis": median_time,
"cache_cpu_time": median_cpu_time,
}


Expand All @@ -79,15 +84,17 @@ def format_metric_with_baseline(current: float, baseline: float, formatter_func)
return f"{formatter_func(current)} *({formatter_func(baseline)})*"


def format_change_percentage(current: float, baseline: float, highlight_mode: str = "none") -> str:
def format_change_percentage(
current: float, baseline: float, threshold: float, highlight_mode: str = "none"
) -> str:
"""Format percentage change and optionally highlight when slower.

highlight_mode:
- "none": never bold
- "slower_only": bold only if current > baseline (i.e., slower) and ≥15%
- "slower_only": bold only if current exceeds baseline by the threshold
"""
change_pct = calculate_change(baseline, current)
if highlight_mode == "slower_only" and change_pct > 0 and abs(change_pct) >= 15.0:
if highlight_mode == "slower_only" and change_pct >= threshold:
return f"**{change_pct:+.1f}%**"
return f"{change_pct:+.1f}%"

Expand Down Expand Up @@ -195,21 +202,21 @@ def extract_mode(d: Dict[str, Any]) -> str:
comp['curr_cold_time'], comp['baseline_cold_time'], format_time
)
cold_change_str = format_change_percentage(
comp['curr_cold_time'], comp['baseline_cold_time'], highlight_mode="none"
comp['curr_cold_time'], comp['baseline_cold_time'], threshold, highlight_mode="none"
)

warm_time_str = format_metric_with_baseline(
comp['curr_warm_time'], comp['baseline_warm_time'], format_time
)
warm_change_str = format_change_percentage(
comp['curr_warm_time'], comp['baseline_warm_time'], highlight_mode="slower_only"
comp['curr_warm_time'], comp['baseline_warm_time'], threshold, highlight_mode="slower_only"
)

cpu_time_str = format_metric_with_baseline(
comp['curr_cpu_time'], comp['baseline_cpu_time'], format_time
)
cpu_change_str = format_change_percentage(
comp['curr_cpu_time'], comp['baseline_cpu_time'], highlight_mode="none"
comp['curr_cpu_time'], comp['baseline_cpu_time'], threshold, highlight_mode="none"
)

lines.append(
Expand All @@ -220,7 +227,7 @@ def extract_mode(d: Dict[str, Any]) -> str:
)

# Summary focused on LiquidCache being slower than DataFusion (warm time)
slower_warm = [c for c in comparison if c["warm_time_change"] > 0]
slower_warm = [c for c in comparison if c["warm_time_change"] >= threshold]
lines.append("")
if slower_warm:
lines.append(f"**⚠️ LiquidCache is slower on {len(slower_warm)} queries (warm)**")
Expand All @@ -230,18 +237,22 @@ def extract_mode(d: Dict[str, Any]) -> str:
slower_warm, key=lambda x: x["warm_time_change"], reverse=True
)
for c in slower_warm_sorted:
curr = c["curr_warm_time"]; base = c["baseline_warm_time"]
curr = c["curr_warm_time"]
base = c["baseline_warm_time"]
pct = calculate_change(base, curr)
lines.append(
f"- Q{c['query']}: warm {pct:+.1f}% "
f"({format_time(curr)} vs {format_time(base)})"
)
else:
lines.append("✅ LiquidCache is faster or equal on warm time for all queries")
lines.append(f"✅ No warm-time regression met the {threshold:.0f}% threshold")

lines.append("")
lines.append(f"*Compared {current_mode} vs {baseline_mode} on the same runner*")
lines.append("*Cold Time: first iteration; Warm Time: average of remaining iterations.*")
lines.append(
f"*Regressions: warm-time increases of at least {threshold:.0f}%. "
"Cold Time: first iteration; Warm Time: median of remaining iterations.*"
)

return "\n".join(lines)

Expand Down
38 changes: 38 additions & 0 deletions .github/test_compare_benchmarks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import importlib.util
import pathlib
import unittest


SCRIPT = pathlib.Path(__file__).with_name("compare_benchmarks.py")
SPEC = importlib.util.spec_from_file_location("compare_benchmarks", SCRIPT)
compare = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(compare)


class CompareBenchmarksTest(unittest.TestCase):
def test_warm_metrics_use_median(self):
iterations = [
{"time_millis": 100, "cache_cpu_time": 100},
{"time_millis": 10, "cache_cpu_time": 1},
{"time_millis": 11, "cache_cpu_time": 2},
{"time_millis": 500, "cache_cpu_time": 100},
]

self.assertEqual(
compare.get_warm_metrics(iterations),
{"time_millis": 11, "cache_cpu_time": 2},
)

def test_highlight_respects_configured_threshold(self):
self.assertEqual(
compare.format_change_percentage(114, 100, 15, "slower_only"),
"+14.0%",
)
self.assertEqual(
compare.format_change_percentage(114, 100, 10, "slower_only"),
"**+14.0%**",
)


if __name__ == "__main__":
unittest.main()
Loading
Loading