Skip to content

Commit 083a356

Browse files
committed
[WIP] towards better plots
1 parent 207e588 commit 083a356

25 files changed

+52705
-17
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
- name: Build benchmarks
8888
run: nix-shell --arg installHls 'false' --pure --run "cabal build -w ${{ env.ghc-exe }} linear-base:bench:bench"
8989
- name: Run benchmarks in isolation
90-
run: nix-shell --arg installHls 'false' --pure --run "echo $'=== Benchmarks (isolation) ===\n\n' > benchmark_${{ env.ghc-name }}.txt && cabal run -w ${{ env.ghc-exe }} -v0 linear-base:bench:bench -- -l | while read -r name; do cabal run -w ${{ env.ghc-exe }} -v0 linear-base:bench:bench -- --timeout 60 -p '"'$'"0 == \"'\""'$'"name\"'\"' 2>&1 | tee -a benchmark_${{ env.ghc-name }}.txt; done"
90+
run: nix-shell --arg installHls 'false' --pure --run "echo $'=== Benchmarks (isolation) ===\n\n' > benchmark_${{ env.ghc-name }}.txt && cabal run -w ${{ env.ghc-exe }} -v0 linear-base:bench:bench -- -l | while read -r name; do cabal run -w ${{ env.ghc-exe }} -v0 linear-base:bench:bench -- --timeout 1200 --stdev 10 -p '"'$'"0 == \"'\""'$'"name\"'\"' 2>&1 | tee -a benchmark_${{ env.ghc-name }}.txt; done"
9191
- name: Upload benchmark results
9292
uses: actions/upload-artifact@v4
9393
with:

avg_manual_bench_and_patch.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import sys
2+
import pandas as pd
3+
from decimal import Decimal, getcontext
4+
5+
def format_significant_decimal(x, digits=5):
6+
if pd.isna(x):
7+
return ''
8+
if x == 0:
9+
return "0"
10+
11+
getcontext().prec = digits + 5
12+
d = Decimal(str(x))
13+
14+
exponent = d.adjusted()
15+
shift = digits - exponent - 1
16+
rounded = d.scaleb(shift).quantize(Decimal('1')).scaleb(-shift)
17+
s = format(rounded.normalize(), 'f')
18+
return s
19+
20+
def format_dataframe_numbers(df, digits=5):
21+
for col in df.select_dtypes(include=["number"]).columns:
22+
df[col] = df[col].apply(lambda x: format_significant_decimal(x, digits))
23+
return df
24+
25+
def usage():
26+
print("Usage:")
27+
print(" python script.py manual_bench.csv averaged_manual_bench.csv")
28+
print(" OR")
29+
print(" python script.py manual_bench.csv averaged_manual_bench.csv auto_bench.csv updated_auto_bench.csv columns_to_patch")
30+
print(" where columns_to_patch is a comma-separated list like: AutoCol1:ManualCol1,AutoCol2:ManualCol2 or just AutoCol1,AutoCol2 (for identity mapping)")
31+
sys.exit(1)
32+
33+
if not (4 <= len(sys.argv) <= 6):
34+
usage()
35+
36+
manual_path = sys.argv[1]
37+
averaged_manual_path = sys.argv[2]
38+
39+
patching_enabled = False
40+
if len(sys.argv) == 6:
41+
patching_enabled = True
42+
auto_path = sys.argv[3]
43+
updated_auto_path = sys.argv[4]
44+
raw_column_mappings = sys.argv[5].split(",")
45+
46+
# Parse mappings like "A:B" or "A"
47+
column_mappings = []
48+
for entry in raw_column_mappings:
49+
if ':' in entry:
50+
auto_col, manual_col = entry.split(":", 1)
51+
else:
52+
auto_col = manual_col = entry
53+
column_mappings.append((auto_col.strip(), manual_col.strip()))
54+
elif len(sys.argv) == 3:
55+
column_mappings = []
56+
else:
57+
usage()
58+
59+
# Load and process manual benchmark
60+
manual_df = pd.read_csv(manual_path)
61+
if "Iteration" in manual_df.columns:
62+
manual_df = manual_df.drop(columns=["Iteration"])
63+
64+
aggregated = (
65+
manual_df
66+
.groupby(["Test Description", "Size", "Method"], as_index=False)
67+
.mean(numeric_only=True)
68+
)
69+
70+
aggregated = format_dataframe_numbers(aggregated, digits=5)
71+
aggregated.to_csv(averaged_manual_path, index=False)
72+
print(f"Averaged manual benchmark written to: {averaged_manual_path}")
73+
74+
if patching_enabled:
75+
auto_df = pd.read_csv(auto_path)
76+
77+
# Build lookup from aggregated benchmark
78+
lookup = {}
79+
for _, row in aggregated.iterrows():
80+
key = (row["Test Description"], row["Size"], row["Method"])
81+
lookup[key] = row
82+
83+
# Patch auto benchmark
84+
for idx, row in auto_df.iterrows():
85+
key = (row["Test Description"], row["Size"], row["Method"])
86+
if key in lookup:
87+
for auto_col, manual_col in column_mappings:
88+
if manual_col in lookup[key]:
89+
auto_df.at[idx, auto_col] = lookup[key][manual_col]
90+
else:
91+
print(f"Warning: Column '{manual_col}' not found for row {key}")
92+
else:
93+
print(f"Warning: No manual benchmark found for: {key}")
94+
95+
auto_df = format_dataframe_numbers(auto_df, digits=5)
96+
auto_df.to_csv(updated_auto_path, index=False)
97+
print(f"Patched auto benchmark written to: {updated_auto_path}")
Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
module Bench.Compact where
22

3-
import Bench.Compact.BFTraversal (bftraversalBenchgroup)
4-
import Bench.Compact.DList (dlistBenchgroup)
5-
import Bench.Compact.Map (mapBenchgroup)
6-
import Bench.Compact.Queue (queueBenchgroup)
7-
import Bench.Compact.SExpr (sexprBenchgroup)
3+
import Bench.Compact.BFTraversal (bftraversalBenchgroup, bftraversalLaunch)
4+
import Bench.Compact.DList (dlistBenchgroup, dlistLaunch)
5+
import Bench.Compact.Map (mapBenchgroup, mapLaunch)
6+
import Bench.Compact.Queue (queueBenchgroup, queueLaunch)
7+
import Bench.Compact.SExpr (sexprBenchgroup, sexprLaunch)
8+
import System.Exit (exitFailure)
89
import Test.Tasty.Bench
910

1011
benchmarks :: Benchmark
@@ -17,3 +18,26 @@ benchmarks =
1718
queueBenchgroup,
1819
sexprBenchgroup
1920
]
21+
22+
launchImpl' :: [(String -> Maybe (IO ()))] -> String -> IO ()
23+
launchImpl' launchers request = do
24+
let tryLaunch [] = Nothing
25+
tryLaunch (l : ls) = case l request of
26+
Just action -> Just action
27+
Nothing -> tryLaunch ls
28+
case tryLaunch launchers of
29+
Just action -> do
30+
action
31+
Nothing -> do
32+
putStrLn $ "Error"
33+
exitFailure
34+
35+
launchImpl :: String -> IO ()
36+
launchImpl =
37+
launchImpl'
38+
[ bftraversalLaunch,
39+
mapLaunch,
40+
dlistLaunch,
41+
queueLaunch,
42+
sexprLaunch
43+
]

bench-version-changes/ghc-dps-compact/after/Bench/Compact/BFTraversal.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ dataSets =
2323
else Node () (go (currentDepth + 1) maxDepth) (go (currentDepth + 1) maxDepth)
2424

2525
bftraversalBenchgroup :: Benchmark
26-
bftraversalBenchgroup = Utils.benchImpls "Breadth-first tree traversal" BFTraversal.impls dataSets
26+
bftraversalBenchgroup = benchImpls "Breadth-first tree traversal" BFTraversal.impls dataSets
27+
28+
bftraversalLaunch :: String -> Maybe (IO ())
29+
bftraversalLaunch = launchImpl "Breadth-first tree traversal" BFTraversal.impls dataSets

bench-version-changes/ghc-dps-compact/after/Bench/Compact/DList.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ dataSets =
2020

2121
dlistBenchgroup :: Benchmark
2222
dlistBenchgroup = benchImpls "List and DList concatenation" DList.impls dataSets
23+
24+
dlistLaunch :: String -> Maybe (IO ())
25+
dlistLaunch = launchImpl "List and DList concatenation" DList.impls dataSets

bench-version-changes/ghc-dps-compact/after/Bench/Compact/Map.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ dataSets =
2222

2323
mapBenchgroup :: Benchmark
2424
mapBenchgroup = benchImpls "map on List" Map.impls dataSets
25+
26+
mapLaunch :: String -> Maybe (IO ())
27+
mapLaunch = launchImpl "map on List" Map.impls dataSets

bench-version-changes/ghc-dps-compact/after/Bench/Compact/Queue.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ dataSets =
1919

2020
queueBenchgroup :: Benchmark
2121
queueBenchgroup = benchImpls "Queue enqueue operations" Queue.impls dataSets
22+
23+
queueLaunch :: String -> Maybe (IO ())
24+
queueLaunch = launchImpl "Queue enqueue operations" Queue.impls dataSets

bench-version-changes/ghc-dps-compact/after/Bench/Compact/SExpr.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@ dataSets =
2121
]
2222

2323
sexprBenchgroup :: Benchmark
24-
sexprBenchgroup = Utils.benchImpls "S-expression parser" SExpr.impls dataSets
24+
sexprBenchgroup = benchImpls "S-expression parser" SExpr.impls dataSets
25+
26+
sexprLaunch :: String -> Maybe (IO ())
27+
sexprLaunch = launchImpl "S-expression parser" SExpr.impls dataSets

bench-version-changes/ghc-dps-compact/after/Bench/Compact/Utils.hs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module Bench.Compact.Utils where
1212
import Control.DeepSeq
1313
import Control.Exception (evaluate)
1414
import Data.Functor ((<&>))
15+
import Data.List (find)
16+
import qualified Data.List.Split as Split
1517
import GHC.Compact (compact, getCompact)
1618
import Test.Tasty (testGroup)
1719
import Test.Tasty.Bench
@@ -82,3 +84,42 @@ benchImpls name impls datasets = do
8284
-- go' [] = error ("requested size '" ++ requestedSize ++ "' not found")
8385
-- go' ((loadSampleData, sizeName):_) | sizeName == requestedSize = loadSampleData
8486
-- go' (_:xs) = go' xs
87+
88+
launchImpl ::
89+
forall m a r.
90+
(NFData a, NFData r) =>
91+
String -> -- expected testName
92+
[(a %m -> r, String, Bool)] ->
93+
[(IO a, String)] ->
94+
String -> -- full request string
95+
Maybe (IO ())
96+
launchImpl name impls datasets request =
97+
let segments = Split.splitOn "." request
98+
in case segments of
99+
("All" : "DPS interface for compact regions" : testName : sizeName : rest)
100+
| testName == name ->
101+
case rest of
102+
[implFullName] -> matchImpl sizeName implFullName Nothing
103+
[implBase, suffix] -> matchImpl sizeName implBase (Just suffix)
104+
_ -> Nothing
105+
| otherwise -> Nothing
106+
_ -> Nothing
107+
where
108+
matchImpl sizeName implBaseName suffix = do
109+
(loadData, _) <- find ((== sizeName) . snd) datasets
110+
(implFn, _, isLazy) <- find (\(_, n, _) -> n == implBaseName) impls
111+
case (isLazy, suffix) of
112+
(False, Nothing) ->
113+
Just $
114+
loadData >>= \sampleData ->
115+
evaluate $ rwhnf $ implFn sampleData
116+
(True, Just "force") ->
117+
Just $
118+
loadData >>= \sampleData ->
119+
evaluate $ rwhnf $ force $ implFn sampleData
120+
(True, Just "copyCR") ->
121+
Just $
122+
loadData >>= \sampleData ->
123+
compact (implFn sampleData) >>= \res ->
124+
evaluate $ rwhnf $ getCompact res
125+
_ -> Nothing
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module Main where
2+
3+
import qualified Bench.Compact as Compact
4+
import qualified Bench.Data.Array.Mutable as Array
5+
import qualified Bench.Data.Array.Mutable.Quicksort as Quicksort
6+
import qualified Bench.Data.HashMap.Mutable as HashMap
7+
import System.Environment (getArgs)
8+
import Test.Tasty.Bench (defaultMain)
9+
10+
main :: IO ()
11+
main = do
12+
args <- getArgs
13+
case args of
14+
["manualLaunch", request] ->
15+
Compact.launchImpl request
16+
_ ->
17+
defaultMain
18+
[ Array.benchmarks,
19+
Quicksort.benchmarks,
20+
HashMap.benchmarks,
21+
Compact.benchmarks
22+
]
23+
24+
-- nix-shell --arg installHls 'false' --pure --run "
25+
-- echo \$'=== Benchmarks (manual launch, filtered) ===\n\n' > benchmark_ghc-dps-compact-95615577d7_prof.txt && \
26+
-- cabal run -w /home/thomas/tweag/linear-base/ghc-dps-compact-95615577d7/bin/ghc -v0 linear-base:bench:bench -- -l | \
27+
-- while read -r name; do \
28+
-- case \"\$name\" in \
29+
-- All.DPS\ interface\ for\ compact\ regions*) \
30+
-- for i in {1..5}; do \
31+
-- echo \"=== Running \$name (iteration \$i) ===\" | tee -a benchmark_ghc-dps-compact-95615577d7_prof.txt; \
32+
-- cabal run -w /home/thomas/tweag/linear-base/ghc-dps-compact-95615577d7/bin/ghc -v0 linear-base:bench:bench -- manualLaunch \"\$name\" +RTS -T -S -RTS 2>&1 | tee -a benchmark_ghc-dps-compact-95615577d7_prof.txt; \
33+
-- done \
34+
-- ;; \
35+
-- *) ;; \
36+
-- esac; \
37+
-- done
38+
-- "
File renamed without changes.

0 commit comments

Comments
 (0)