aboutsummaryrefslogtreecommitdiffstats
path: root/research
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-05 13:33:32 +0100
committereunhhu <[email protected]>2026-10-05 13:33:32 +0100
commitca1acb2be40c8c65af46ba83883cf20edb36c993 (patch)
treed360cee25d3b0cf2c72c01660e6d869c6969c231 /research
parenta5f3f6c4f79245103299befe14126834f5bc4ecd (diff)
downloadpure-ca1acb2be40c8c65af46ba83883cf20edb36c993.tar.gz
pure-ca1acb2be40c8c65af46ba83883cf20edb36c993.zip
Research 124's floors: each workload by hand as Pure could run it (its lists' representation, its meaning) bare, with the checks a compiler could hoist, and with every check where the meaning puts it: hoisted 0.64-1.02x C (0.90 by the geometric mean), naive up to 12x
Diffstat (limited to 'research')
-rw-r--r--research/experiments/040-against-c/2026-10-05-floor.jsonl252
-rw-r--r--research/experiments/040-against-c/floor.c522
-rw-r--r--research/experiments/040-against-c/natural.h201
-rw-r--r--research/experiments/040-against-c/runs.sh10
-rw-r--r--research/experiments/040-against-c/suite-run.c188
5 files changed, 987 insertions, 186 deletions
diff --git a/research/experiments/040-against-c/2026-10-05-floor.jsonl b/research/experiments/040-against-c/2026-10-05-floor.jsonl
new file mode 100644
index 0000000..5216449
--- /dev/null
+++ b/research/experiments/040-against-c/2026-10-05-floor.jsonl
@@ -0,0 +1,252 @@
+{"mode":0,"work":"roots","round":0,"floor_ms":6.801,"c_ms":6.859,"same":true}
+{"mode":0,"work":"roots","round":1,"floor_ms":6.837,"c_ms":6.861,"same":true}
+{"mode":0,"work":"roots","round":2,"floor_ms":6.809,"c_ms":6.851,"same":true}
+{"mode":0,"work":"roots","round":3,"floor_ms":6.802,"c_ms":6.854,"same":true}
+{"mode":0,"work":"roots","round":4,"floor_ms":6.841,"c_ms":6.868,"same":true}
+{"mode":0,"work":"roots","round":5,"floor_ms":6.792,"c_ms":6.870,"same":true}
+{"mode":0,"work":"roots","round":6,"floor_ms":6.798,"c_ms":6.860,"same":true}
+{"mode":0,"work":"roots","round":7,"floor_ms":6.792,"c_ms":6.849,"same":true}
+{"mode":0,"work":"roots","round":8,"floor_ms":6.795,"c_ms":6.857,"same":true}
+{"mode":0,"work":"roots","round":9,"floor_ms":6.796,"c_ms":6.856,"same":true}
+{"mode":0,"work":"roots","round":10,"floor_ms":6.794,"c_ms":6.860,"same":true}
+{"mode":0,"work":"roots","floor_median_ms":6.798,"c_median_ms":6.859,"ratio":0.991,"agree":true}
+{"mode":0,"work":"knapsack","round":0,"floor_ms":5.973,"c_ms":6.253,"same":true}
+{"mode":0,"work":"knapsack","round":1,"floor_ms":5.936,"c_ms":6.222,"same":true}
+{"mode":0,"work":"knapsack","round":2,"floor_ms":5.930,"c_ms":6.222,"same":true}
+{"mode":0,"work":"knapsack","round":3,"floor_ms":5.936,"c_ms":6.222,"same":true}
+{"mode":0,"work":"knapsack","round":4,"floor_ms":5.933,"c_ms":6.223,"same":true}
+{"mode":0,"work":"knapsack","round":5,"floor_ms":5.931,"c_ms":6.225,"same":true}
+{"mode":0,"work":"knapsack","round":6,"floor_ms":5.930,"c_ms":6.223,"same":true}
+{"mode":0,"work":"knapsack","round":7,"floor_ms":5.930,"c_ms":6.223,"same":true}
+{"mode":0,"work":"knapsack","round":8,"floor_ms":5.932,"c_ms":6.237,"same":true}
+{"mode":0,"work":"knapsack","round":9,"floor_ms":5.930,"c_ms":6.226,"same":true}
+{"mode":0,"work":"knapsack","round":10,"floor_ms":5.929,"c_ms":6.224,"same":true}
+{"mode":0,"work":"knapsack","floor_median_ms":5.931,"c_median_ms":6.223,"ratio":0.953,"agree":true}
+{"mode":0,"work":"sort","round":0,"floor_ms":16.532,"c_ms":26.888,"same":true}
+{"mode":0,"work":"sort","round":1,"floor_ms":16.328,"c_ms":26.798,"same":true}
+{"mode":0,"work":"sort","round":2,"floor_ms":16.278,"c_ms":26.741,"same":true}
+{"mode":0,"work":"sort","round":3,"floor_ms":16.293,"c_ms":26.735,"same":true}
+{"mode":0,"work":"sort","round":4,"floor_ms":16.300,"c_ms":26.767,"same":true}
+{"mode":0,"work":"sort","round":5,"floor_ms":16.314,"c_ms":26.770,"same":true}
+{"mode":0,"work":"sort","round":6,"floor_ms":16.298,"c_ms":33.648,"same":true}
+{"mode":0,"work":"sort","round":7,"floor_ms":23.018,"c_ms":37.775,"same":true}
+{"mode":0,"work":"sort","round":8,"floor_ms":21.080,"c_ms":26.728,"same":true}
+{"mode":0,"work":"sort","round":9,"floor_ms":16.296,"c_ms":26.759,"same":true}
+{"mode":0,"work":"sort","round":10,"floor_ms":16.282,"c_ms":26.831,"same":true}
+{"mode":0,"work":"sort","floor_median_ms":16.300,"c_median_ms":26.770,"ratio":0.609,"agree":true}
+{"mode":0,"work":"join","round":0,"floor_ms":28.504,"c_ms":30.082,"same":true}
+{"mode":0,"work":"join","round":1,"floor_ms":28.622,"c_ms":29.788,"same":true}
+{"mode":0,"work":"join","round":2,"floor_ms":28.239,"c_ms":29.781,"same":true}
+{"mode":0,"work":"join","round":3,"floor_ms":28.342,"c_ms":29.814,"same":true}
+{"mode":0,"work":"join","round":4,"floor_ms":28.480,"c_ms":32.227,"same":true}
+{"mode":0,"work":"join","round":5,"floor_ms":28.870,"c_ms":30.314,"same":true}
+{"mode":0,"work":"join","round":6,"floor_ms":28.730,"c_ms":29.611,"same":true}
+{"mode":0,"work":"join","round":7,"floor_ms":28.092,"c_ms":29.933,"same":true}
+{"mode":0,"work":"join","round":8,"floor_ms":29.349,"c_ms":30.048,"same":true}
+{"mode":0,"work":"join","round":9,"floor_ms":28.113,"c_ms":31.056,"same":true}
+{"mode":0,"work":"join","round":10,"floor_ms":27.706,"c_ms":30.712,"same":true}
+{"mode":0,"work":"join","floor_median_ms":28.480,"c_median_ms":30.048,"ratio":0.948,"agree":true}
+{"mode":0,"work":"words","round":0,"floor_ms":32.180,"c_ms":35.147,"same":true}
+{"mode":0,"work":"words","round":1,"floor_ms":32.194,"c_ms":34.985,"same":true}
+{"mode":0,"work":"words","round":2,"floor_ms":32.203,"c_ms":36.446,"same":true}
+{"mode":0,"work":"words","round":3,"floor_ms":32.070,"c_ms":34.304,"same":true}
+{"mode":0,"work":"words","round":4,"floor_ms":32.195,"c_ms":34.747,"same":true}
+{"mode":0,"work":"words","round":5,"floor_ms":32.408,"c_ms":35.000,"same":true}
+{"mode":0,"work":"words","round":6,"floor_ms":32.187,"c_ms":34.488,"same":true}
+{"mode":0,"work":"words","round":7,"floor_ms":32.675,"c_ms":35.912,"same":true}
+{"mode":0,"work":"words","round":8,"floor_ms":32.255,"c_ms":37.028,"same":true}
+{"mode":0,"work":"words","round":9,"floor_ms":34.416,"c_ms":34.602,"same":true}
+{"mode":0,"work":"words","round":10,"floor_ms":32.567,"c_ms":37.133,"same":true}
+{"mode":0,"work":"words","floor_median_ms":32.203,"c_median_ms":35.000,"ratio":0.920,"agree":true}
+{"mode":0,"work":"matmul","round":0,"floor_ms":7.204,"c_ms":7.543,"same":true}
+{"mode":0,"work":"matmul","round":1,"floor_ms":7.236,"c_ms":7.471,"same":true}
+{"mode":0,"work":"matmul","round":2,"floor_ms":7.194,"c_ms":7.502,"same":true}
+{"mode":0,"work":"matmul","round":3,"floor_ms":7.192,"c_ms":7.470,"same":true}
+{"mode":0,"work":"matmul","round":4,"floor_ms":7.190,"c_ms":7.486,"same":true}
+{"mode":0,"work":"matmul","round":5,"floor_ms":7.198,"c_ms":7.500,"same":true}
+{"mode":0,"work":"matmul","round":6,"floor_ms":7.203,"c_ms":7.508,"same":true}
+{"mode":0,"work":"matmul","round":7,"floor_ms":7.237,"c_ms":7.779,"same":true}
+{"mode":0,"work":"matmul","round":8,"floor_ms":7.237,"c_ms":7.879,"same":true}
+{"mode":0,"work":"matmul","round":9,"floor_ms":7.440,"c_ms":8.178,"same":true}
+{"mode":0,"work":"matmul","round":10,"floor_ms":7.284,"c_ms":7.611,"same":true}
+{"mode":0,"work":"matmul","floor_median_ms":7.204,"c_median_ms":7.508,"ratio":0.960,"agree":true}
+{"mode":0,"work":"bfs","round":0,"floor_ms":11.092,"c_ms":9.823,"same":true}
+{"mode":0,"work":"bfs","round":1,"floor_ms":11.118,"c_ms":9.847,"same":true}
+{"mode":0,"work":"bfs","round":2,"floor_ms":10.622,"c_ms":9.766,"same":true}
+{"mode":0,"work":"bfs","round":3,"floor_ms":10.682,"c_ms":9.851,"same":true}
+{"mode":0,"work":"bfs","round":4,"floor_ms":10.605,"c_ms":10.685,"same":true}
+{"mode":0,"work":"bfs","round":5,"floor_ms":10.689,"c_ms":9.926,"same":true}
+{"mode":0,"work":"bfs","round":6,"floor_ms":10.661,"c_ms":9.764,"same":true}
+{"mode":0,"work":"bfs","round":7,"floor_ms":10.843,"c_ms":9.780,"same":true}
+{"mode":0,"work":"bfs","round":8,"floor_ms":10.679,"c_ms":9.836,"same":true}
+{"mode":0,"work":"bfs","round":9,"floor_ms":10.671,"c_ms":10.038,"same":true}
+{"mode":0,"work":"bfs","round":10,"floor_ms":10.714,"c_ms":9.939,"same":true}
+{"mode":0,"work":"bfs","floor_median_ms":10.682,"c_median_ms":9.847,"ratio":1.085,"agree":true}
+{"mode":1,"work":"roots","round":0,"floor_ms":7.034,"c_ms":6.867,"same":true}
+{"mode":1,"work":"roots","round":1,"floor_ms":6.997,"c_ms":6.861,"same":true}
+{"mode":1,"work":"roots","round":2,"floor_ms":7.009,"c_ms":6.867,"same":true}
+{"mode":1,"work":"roots","round":3,"floor_ms":7.009,"c_ms":6.868,"same":true}
+{"mode":1,"work":"roots","round":4,"floor_ms":7.007,"c_ms":6.856,"same":true}
+{"mode":1,"work":"roots","round":5,"floor_ms":7.008,"c_ms":6.857,"same":true}
+{"mode":1,"work":"roots","round":6,"floor_ms":7.002,"c_ms":6.861,"same":true}
+{"mode":1,"work":"roots","round":7,"floor_ms":7.006,"c_ms":6.856,"same":true}
+{"mode":1,"work":"roots","round":8,"floor_ms":6.996,"c_ms":6.859,"same":true}
+{"mode":1,"work":"roots","round":9,"floor_ms":7.004,"c_ms":6.861,"same":true}
+{"mode":1,"work":"roots","round":10,"floor_ms":7.004,"c_ms":6.868,"same":true}
+{"mode":1,"work":"roots","floor_median_ms":7.006,"c_median_ms":6.861,"ratio":1.021,"agree":true}
+{"mode":1,"work":"knapsack","round":0,"floor_ms":6.087,"c_ms":6.244,"same":true}
+{"mode":1,"work":"knapsack","round":1,"floor_ms":6.046,"c_ms":6.221,"same":true}
+{"mode":1,"work":"knapsack","round":2,"floor_ms":6.045,"c_ms":6.220,"same":true}
+{"mode":1,"work":"knapsack","round":3,"floor_ms":6.044,"c_ms":6.220,"same":true}
+{"mode":1,"work":"knapsack","round":4,"floor_ms":6.043,"c_ms":6.218,"same":true}
+{"mode":1,"work":"knapsack","round":5,"floor_ms":6.042,"c_ms":6.218,"same":true}
+{"mode":1,"work":"knapsack","round":6,"floor_ms":6.046,"c_ms":6.216,"same":true}
+{"mode":1,"work":"knapsack","round":7,"floor_ms":6.047,"c_ms":6.218,"same":true}
+{"mode":1,"work":"knapsack","round":8,"floor_ms":6.050,"c_ms":6.217,"same":true}
+{"mode":1,"work":"knapsack","round":9,"floor_ms":6.047,"c_ms":6.235,"same":true}
+{"mode":1,"work":"knapsack","round":10,"floor_ms":6.053,"c_ms":6.215,"same":true}
+{"mode":1,"work":"knapsack","floor_median_ms":6.046,"c_median_ms":6.218,"ratio":0.972,"agree":true}
+{"mode":1,"work":"sort","round":0,"floor_ms":17.378,"c_ms":26.766,"same":true}
+{"mode":1,"work":"sort","round":1,"floor_ms":17.157,"c_ms":26.826,"same":true}
+{"mode":1,"work":"sort","round":2,"floor_ms":17.107,"c_ms":26.742,"same":true}
+{"mode":1,"work":"sort","round":3,"floor_ms":17.112,"c_ms":26.752,"same":true}
+{"mode":1,"work":"sort","round":4,"floor_ms":17.140,"c_ms":26.762,"same":true}
+{"mode":1,"work":"sort","round":5,"floor_ms":17.104,"c_ms":26.734,"same":true}
+{"mode":1,"work":"sort","round":6,"floor_ms":17.101,"c_ms":26.766,"same":true}
+{"mode":1,"work":"sort","round":7,"floor_ms":17.090,"c_ms":26.761,"same":true}
+{"mode":1,"work":"sort","round":8,"floor_ms":17.085,"c_ms":26.799,"same":true}
+{"mode":1,"work":"sort","round":9,"floor_ms":17.075,"c_ms":26.773,"same":true}
+{"mode":1,"work":"sort","round":10,"floor_ms":17.094,"c_ms":26.946,"same":true}
+{"mode":1,"work":"sort","floor_median_ms":17.104,"c_median_ms":26.766,"ratio":0.639,"agree":true}
+{"mode":1,"work":"join","round":0,"floor_ms":28.134,"c_ms":30.856,"same":true}
+{"mode":1,"work":"join","round":1,"floor_ms":29.150,"c_ms":30.801,"same":true}
+{"mode":1,"work":"join","round":2,"floor_ms":27.497,"c_ms":30.924,"same":true}
+{"mode":1,"work":"join","round":3,"floor_ms":29.344,"c_ms":35.384,"same":true}
+{"mode":1,"work":"join","round":4,"floor_ms":27.824,"c_ms":31.006,"same":true}
+{"mode":1,"work":"join","round":5,"floor_ms":28.559,"c_ms":30.934,"same":true}
+{"mode":1,"work":"join","round":6,"floor_ms":28.521,"c_ms":31.536,"same":true}
+{"mode":1,"work":"join","round":7,"floor_ms":27.643,"c_ms":30.660,"same":true}
+{"mode":1,"work":"join","round":8,"floor_ms":28.388,"c_ms":30.592,"same":true}
+{"mode":1,"work":"join","round":9,"floor_ms":27.968,"c_ms":31.220,"same":true}
+{"mode":1,"work":"join","round":10,"floor_ms":27.543,"c_ms":30.686,"same":true}
+{"mode":1,"work":"join","floor_median_ms":28.134,"c_median_ms":30.924,"ratio":0.910,"agree":true}
+{"mode":1,"work":"words","round":0,"floor_ms":31.794,"c_ms":36.801,"same":true}
+{"mode":1,"work":"words","round":1,"floor_ms":31.838,"c_ms":35.285,"same":true}
+{"mode":1,"work":"words","round":2,"floor_ms":31.832,"c_ms":35.489,"same":true}
+{"mode":1,"work":"words","round":3,"floor_ms":31.776,"c_ms":35.209,"same":true}
+{"mode":1,"work":"words","round":4,"floor_ms":31.798,"c_ms":37.366,"same":true}
+{"mode":1,"work":"words","round":5,"floor_ms":31.953,"c_ms":36.536,"same":true}
+{"mode":1,"work":"words","round":6,"floor_ms":32.213,"c_ms":34.794,"same":true}
+{"mode":1,"work":"words","round":7,"floor_ms":31.753,"c_ms":35.425,"same":true}
+{"mode":1,"work":"words","round":8,"floor_ms":32.174,"c_ms":36.623,"same":true}
+{"mode":1,"work":"words","round":9,"floor_ms":31.832,"c_ms":34.502,"same":true}
+{"mode":1,"work":"words","round":10,"floor_ms":31.956,"c_ms":35.347,"same":true}
+{"mode":1,"work":"words","floor_median_ms":31.832,"c_median_ms":35.425,"ratio":0.899,"agree":true}
+{"mode":1,"work":"matmul","round":0,"floor_ms":7.251,"c_ms":7.710,"same":true}
+{"mode":1,"work":"matmul","round":1,"floor_ms":7.237,"c_ms":7.625,"same":true}
+{"mode":1,"work":"matmul","round":2,"floor_ms":7.234,"c_ms":7.621,"same":true}
+{"mode":1,"work":"matmul","round":3,"floor_ms":7.237,"c_ms":7.635,"same":true}
+{"mode":1,"work":"matmul","round":4,"floor_ms":7.236,"c_ms":7.620,"same":true}
+{"mode":1,"work":"matmul","round":5,"floor_ms":7.238,"c_ms":7.662,"same":true}
+{"mode":1,"work":"matmul","round":6,"floor_ms":7.241,"c_ms":7.671,"same":true}
+{"mode":1,"work":"matmul","round":7,"floor_ms":7.240,"c_ms":7.643,"same":true}
+{"mode":1,"work":"matmul","round":8,"floor_ms":7.239,"c_ms":7.644,"same":true}
+{"mode":1,"work":"matmul","round":9,"floor_ms":7.235,"c_ms":7.632,"same":true}
+{"mode":1,"work":"matmul","round":10,"floor_ms":7.243,"c_ms":7.636,"same":true}
+{"mode":1,"work":"matmul","floor_median_ms":7.238,"c_median_ms":7.636,"ratio":0.948,"agree":true}
+{"mode":1,"work":"bfs","round":0,"floor_ms":9.630,"c_ms":9.831,"same":true}
+{"mode":1,"work":"bfs","round":1,"floor_ms":9.415,"c_ms":10.224,"same":true}
+{"mode":1,"work":"bfs","round":2,"floor_ms":9.312,"c_ms":9.828,"same":true}
+{"mode":1,"work":"bfs","round":3,"floor_ms":9.328,"c_ms":9.788,"same":true}
+{"mode":1,"work":"bfs","round":4,"floor_ms":9.325,"c_ms":9.823,"same":true}
+{"mode":1,"work":"bfs","round":5,"floor_ms":9.358,"c_ms":9.773,"same":true}
+{"mode":1,"work":"bfs","round":6,"floor_ms":11.301,"c_ms":9.910,"same":true}
+{"mode":1,"work":"bfs","round":7,"floor_ms":12.588,"c_ms":13.739,"same":true}
+{"mode":1,"work":"bfs","round":8,"floor_ms":11.051,"c_ms":9.838,"same":true}
+{"mode":1,"work":"bfs","round":9,"floor_ms":9.384,"c_ms":9.884,"same":true}
+{"mode":1,"work":"bfs","round":10,"floor_ms":9.456,"c_ms":9.906,"same":true}
+{"mode":1,"work":"bfs","floor_median_ms":9.415,"c_median_ms":9.838,"ratio":0.957,"agree":true}
+{"mode":2,"work":"roots","round":0,"floor_ms":12.111,"c_ms":6.868,"same":true}
+{"mode":2,"work":"roots","round":1,"floor_ms":12.126,"c_ms":6.872,"same":true}
+{"mode":2,"work":"roots","round":2,"floor_ms":12.126,"c_ms":6.861,"same":true}
+{"mode":2,"work":"roots","round":3,"floor_ms":12.129,"c_ms":6.858,"same":true}
+{"mode":2,"work":"roots","round":4,"floor_ms":12.127,"c_ms":6.843,"same":true}
+{"mode":2,"work":"roots","round":5,"floor_ms":12.095,"c_ms":6.865,"same":true}
+{"mode":2,"work":"roots","round":6,"floor_ms":12.121,"c_ms":6.860,"same":true}
+{"mode":2,"work":"roots","round":7,"floor_ms":12.133,"c_ms":6.857,"same":true}
+{"mode":2,"work":"roots","round":8,"floor_ms":12.101,"c_ms":6.858,"same":true}
+{"mode":2,"work":"roots","round":9,"floor_ms":12.135,"c_ms":6.861,"same":true}
+{"mode":2,"work":"roots","round":10,"floor_ms":12.135,"c_ms":6.841,"same":true}
+{"mode":2,"work":"roots","floor_median_ms":12.126,"c_median_ms":6.860,"ratio":1.768,"agree":true}
+{"mode":2,"work":"knapsack","round":0,"floor_ms":67.054,"c_ms":6.250,"same":true}
+{"mode":2,"work":"knapsack","round":1,"floor_ms":67.208,"c_ms":6.234,"same":true}
+{"mode":2,"work":"knapsack","round":2,"floor_ms":67.209,"c_ms":6.248,"same":true}
+{"mode":2,"work":"knapsack","round":3,"floor_ms":67.144,"c_ms":6.229,"same":true}
+{"mode":2,"work":"knapsack","round":4,"floor_ms":67.159,"c_ms":6.256,"same":true}
+{"mode":2,"work":"knapsack","round":5,"floor_ms":67.141,"c_ms":6.223,"same":true}
+{"mode":2,"work":"knapsack","round":6,"floor_ms":67.127,"c_ms":6.220,"same":true}
+{"mode":2,"work":"knapsack","round":7,"floor_ms":67.118,"c_ms":6.215,"same":true}
+{"mode":2,"work":"knapsack","round":8,"floor_ms":67.153,"c_ms":6.223,"same":true}
+{"mode":2,"work":"knapsack","round":9,"floor_ms":67.145,"c_ms":6.216,"same":true}
+{"mode":2,"work":"knapsack","round":10,"floor_ms":67.144,"c_ms":6.216,"same":true}
+{"mode":2,"work":"knapsack","floor_median_ms":67.144,"c_median_ms":6.223,"ratio":10.789,"agree":true}
+{"mode":2,"work":"sort","round":0,"floor_ms":22.851,"c_ms":26.827,"same":true}
+{"mode":2,"work":"sort","round":1,"floor_ms":22.175,"c_ms":26.771,"same":true}
+{"mode":2,"work":"sort","round":2,"floor_ms":22.278,"c_ms":26.728,"same":true}
+{"mode":2,"work":"sort","round":3,"floor_ms":22.265,"c_ms":26.726,"same":true}
+{"mode":2,"work":"sort","round":4,"floor_ms":22.188,"c_ms":26.749,"same":true}
+{"mode":2,"work":"sort","round":5,"floor_ms":22.149,"c_ms":26.731,"same":true}
+{"mode":2,"work":"sort","round":6,"floor_ms":22.244,"c_ms":26.820,"same":true}
+{"mode":2,"work":"sort","round":7,"floor_ms":22.167,"c_ms":26.822,"same":true}
+{"mode":2,"work":"sort","round":8,"floor_ms":22.169,"c_ms":27.076,"same":true}
+{"mode":2,"work":"sort","round":9,"floor_ms":22.181,"c_ms":26.738,"same":true}
+{"mode":2,"work":"sort","round":10,"floor_ms":22.229,"c_ms":26.803,"same":true}
+{"mode":2,"work":"sort","floor_median_ms":22.188,"c_median_ms":26.771,"ratio":0.829,"agree":true}
+{"mode":2,"work":"join","round":0,"floor_ms":53.906,"c_ms":30.142,"same":true}
+{"mode":2,"work":"join","round":1,"floor_ms":47.211,"c_ms":29.795,"same":true}
+{"mode":2,"work":"join","round":2,"floor_ms":47.131,"c_ms":29.763,"same":true}
+{"mode":2,"work":"join","round":3,"floor_ms":46.514,"c_ms":30.385,"same":true}
+{"mode":2,"work":"join","round":4,"floor_ms":45.907,"c_ms":29.843,"same":true}
+{"mode":2,"work":"join","round":5,"floor_ms":46.281,"c_ms":29.885,"same":true}
+{"mode":2,"work":"join","round":6,"floor_ms":48.798,"c_ms":30.104,"same":true}
+{"mode":2,"work":"join","round":7,"floor_ms":47.333,"c_ms":30.000,"same":true}
+{"mode":2,"work":"join","round":8,"floor_ms":45.997,"c_ms":29.918,"same":true}
+{"mode":2,"work":"join","round":9,"floor_ms":45.500,"c_ms":31.265,"same":true}
+{"mode":2,"work":"join","round":10,"floor_ms":49.097,"c_ms":30.452,"same":true}
+{"mode":2,"work":"join","floor_median_ms":47.131,"c_median_ms":30.000,"ratio":1.571,"agree":true}
+{"mode":2,"work":"words","round":0,"floor_ms":32.362,"c_ms":35.904,"same":true}
+{"mode":2,"work":"words","round":1,"floor_ms":32.211,"c_ms":36.401,"same":true}
+{"mode":2,"work":"words","round":2,"floor_ms":36.167,"c_ms":36.148,"same":true}
+{"mode":2,"work":"words","round":3,"floor_ms":32.371,"c_ms":35.347,"same":true}
+{"mode":2,"work":"words","round":4,"floor_ms":32.074,"c_ms":34.791,"same":true}
+{"mode":2,"work":"words","round":5,"floor_ms":32.081,"c_ms":34.546,"same":true}
+{"mode":2,"work":"words","round":6,"floor_ms":32.141,"c_ms":38.131,"same":true}
+{"mode":2,"work":"words","round":7,"floor_ms":32.153,"c_ms":34.490,"same":true}
+{"mode":2,"work":"words","round":8,"floor_ms":32.291,"c_ms":34.892,"same":true}
+{"mode":2,"work":"words","round":9,"floor_ms":32.225,"c_ms":35.059,"same":true}
+{"mode":2,"work":"words","round":10,"floor_ms":32.159,"c_ms":37.261,"same":true}
+{"mode":2,"work":"words","floor_median_ms":32.211,"c_median_ms":35.347,"ratio":0.911,"agree":true}
+{"mode":2,"work":"matmul","round":0,"floor_ms":91.662,"c_ms":7.577,"same":true}
+{"mode":2,"work":"matmul","round":1,"floor_ms":91.745,"c_ms":7.565,"same":true}
+{"mode":2,"work":"matmul","round":2,"floor_ms":91.900,"c_ms":7.570,"same":true}
+{"mode":2,"work":"matmul","round":3,"floor_ms":91.731,"c_ms":7.930,"same":true}
+{"mode":2,"work":"matmul","round":4,"floor_ms":91.744,"c_ms":7.558,"same":true}
+{"mode":2,"work":"matmul","round":5,"floor_ms":91.642,"c_ms":7.522,"same":true}
+{"mode":2,"work":"matmul","round":6,"floor_ms":91.683,"c_ms":7.558,"same":true}
+{"mode":2,"work":"matmul","round":7,"floor_ms":91.678,"c_ms":7.535,"same":true}
+{"mode":2,"work":"matmul","round":8,"floor_ms":91.683,"c_ms":7.592,"same":true}
+{"mode":2,"work":"matmul","round":9,"floor_ms":91.691,"c_ms":7.572,"same":true}
+{"mode":2,"work":"matmul","round":10,"floor_ms":91.666,"c_ms":7.527,"same":true}
+{"mode":2,"work":"matmul","floor_median_ms":91.683,"c_median_ms":7.565,"ratio":12.119,"agree":true}
+{"mode":2,"work":"bfs","round":0,"floor_ms":12.360,"c_ms":9.799,"same":true}
+{"mode":2,"work":"bfs","round":1,"floor_ms":12.236,"c_ms":9.791,"same":true}
+{"mode":2,"work":"bfs","round":2,"floor_ms":12.092,"c_ms":9.852,"same":true}
+{"mode":2,"work":"bfs","round":3,"floor_ms":13.119,"c_ms":10.050,"same":true}
+{"mode":2,"work":"bfs","round":4,"floor_ms":12.090,"c_ms":9.767,"same":true}
+{"mode":2,"work":"bfs","round":5,"floor_ms":12.013,"c_ms":9.881,"same":true}
+{"mode":2,"work":"bfs","round":6,"floor_ms":11.990,"c_ms":10.381,"same":true}
+{"mode":2,"work":"bfs","round":7,"floor_ms":12.140,"c_ms":10.075,"same":true}
+{"mode":2,"work":"bfs","round":8,"floor_ms":12.096,"c_ms":9.798,"same":true}
+{"mode":2,"work":"bfs","round":9,"floor_ms":12.115,"c_ms":10.025,"same":true}
+{"mode":2,"work":"bfs","round":10,"floor_ms":12.145,"c_ms":9.781,"same":true}
+{"mode":2,"work":"bfs","floor_median_ms":12.115,"c_median_ms":9.852,"ratio":1.230,"agree":true}
diff --git a/research/experiments/040-against-c/floor.c b/research/experiments/040-against-c/floor.c
new file mode 100644
index 0000000..5b82162
--- /dev/null
+++ b/research/experiments/040-against-c/floor.c
@@ -0,0 +1,522 @@
+/* Research 124's floors: each workload of the suite written by hand in C as
+ * Pure could run it at best, its representation Pure's (an event's lists as
+ * arrays: FLAT in order, DENSE at their keys with key bits, columns as
+ * narrow as their values; a list kept by sparse keys with a hash index,
+ * ordered when it is walked), its meaning Pure's, and what that meaning
+ * costs, by MODE:
+ * 0 bare no check at all: what the representation costs
+ * 1 hoisted the checks a compiler could leave: once a loop where it can
+ * bound what the loop does (its rounds against the work budget,
+ * its appends against the memory budget, a full DENSE list's
+ * bits, a column's width), none where the values' ranges prove
+ * them (whole numbers well inside 2^53 are finite and never
+ * -0), else where they fall
+ * 2 naive numbers as numbers (binary64, no ranges known) and every
+ * check where the meaning puts it: each result finite, no
+ * division by 0, each key a whole number (not -0) below 2^53,
+ * in its bits and below their reach, each position below the
+ * count, each cell fitting its column, each round against the
+ * work budget, each append against the memory budget
+ * In 0 and 1 whole numbers whose ranges are known are integers.
+ * A check that fails ends the event; which check and where is the
+ * executor's to say (a failed event is undone and may be run again by it),
+ * so a hoisted check only has to fail when some check would.
+ *
+ * floor-MODE ROUNDS [NAME...] per round the floor's ms and the natural
+ * C's (natural.h), the same answers, then the medians. */
+#define _POSIX_C_SOURCE 200809L
+#include <math.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "natural.h"
+
+#ifndef MODE
+#define MODE 1
+#endif
+
+static jmp_buf failed;
+static uint64_t work, budget = UINT64_C(1) << 28; /* executed instructions, as Pure counts them */
+static uint64_t grown, room = UINT64_C(1) << 28; /* bytes the event added */
+
+#define FAIL() longjmp(failed, 1)
+#define NAIVE(ok) \
+ do { \
+ if (MODE == 2 && __builtin_expect(!(ok), 0)) FAIL(); \
+ } while (0)
+#define HOISTED(ok) \
+ do { \
+ if (MODE >= 1 && __builtin_expect(!(ok), 0)) FAIL(); \
+ } while (0)
+#define FINITE(x) (fabs(x) < INFINITY)
+/* A round of `cost` instructions: counted each round (naive), or the rounds a loop may make at most checked
+ * as it opens (hoisted, ROUNDS_OPEN) and what it made counted as it ends (ROUNDS_DONE). */
+#define ROUND(cost) \
+ do { \
+ if (MODE == 2) { \
+ work += (cost); \
+ if (__builtin_expect(work > budget, 0)) FAIL(); \
+ } \
+ } while (0)
+#define ROUNDS_OPEN(most, cost) HOISTED(budget - work >= (uint64_t)(most) * (cost))
+#define ROUNDS_DONE(made, cost) \
+ do { \
+ if (MODE == 1) work += (uint64_t)(made) * (cost); \
+ } while (0)
+#define APPEND(bytes) \
+ do { \
+ if (MODE == 2) { \
+ grown += (bytes); \
+ if (__builtin_expect(grown > room, 0)) FAIL(); \
+ } \
+ } while (0)
+#define APPENDS_OPEN(most, bytes) \
+ do { \
+ if (MODE == 1) { \
+ grown += (uint64_t)(most) * (bytes); \
+ if (__builtin_expect(grown > room, 0)) FAIL(); \
+ } \
+ } while (0)
+
+static double now_ms(void) {
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return (double)t.tv_sec * 1e3 + (double)t.tv_nsec / 1e6;
+}
+
+static int compare_doubles(const void *a, const void *b) {
+ double x = *(const double *)a, y = *(const double *)b;
+ return (x > y) - (x < y);
+}
+
+/* What a number is: binary64 in the naive code (its results checked finite, its keys whole), else an
+ * integer where the values are whole (their ranges known). */
+#if MODE == 2
+typedef double num;
+static double fin(double x) {
+ NAIVE(FINITE(x));
+ return x;
+}
+static uint64_t key_of(double x) {
+ NAIVE(x >= 0 && x < 0x1p53 && x == floor(x) && !(x == 0 && signbit(x)));
+ return (uint64_t)x;
+}
+#define F(x) fin(x)
+#define KEY(x) key_of(x)
+#else
+typedef int64_t num;
+#define F(x) (x)
+#define KEY(x) ((uint64_t)(x))
+#endif
+
+/* A DENSE list's key bits: whether key is there. */
+static bool bit(const uint64_t *bits, uint64_t key) { return bits[key >> 6] >> (key & 63) & 1; }
+
+/* ---- roots: numbers; r >= 1 and i <= n bound every value, so the hoisted code checks none */
+
+static void f_roots(const uint32_t *in, double *out) {
+ uint32_t n = in[0];
+ double sum = 0, count = 0;
+ ROUNDS_OPEN(n, 12 * 64 + 10);
+ for (uint32_t i = 1; i <= n; ++i) {
+ double r = i;
+ int s = 0;
+ for (; s < 64; ++s) {
+ NAIVE(r != 0);
+ double q = i / r;
+ NAIVE(FINITE(q));
+ double t = r + q;
+ NAIVE(FINITE(t));
+ double next = t / 2;
+ NAIVE(FINITE(next));
+ ROUND(12);
+ if (next >= r) break;
+ r = next;
+ count += 1;
+ NAIVE(FINITE(count));
+ }
+ ROUNDS_DONE(s + 1, 12);
+ sum += r;
+ NAIVE(FINITE(sum));
+ ROUND(10);
+ }
+ ROUNDS_DONE(n, 10);
+ out[0] = sum, out[1] = count;
+}
+
+/* ---- knapsack: the items a FLAT list (weight, worth: four bytes), the table a DENSE list (best: four
+ * bytes, the key its place); whole numbers below 2^31 as integers; the table full (every key there),
+ * which the hoisted code checks once */
+
+static void f_knapsack(const uint32_t *in, double *out) {
+ uint32_t n = in[0], capacity = in[1];
+ uint32_t *weight = malloc(4 * (size_t)n), *worth = malloc(4 * (size_t)n), *best = calloc((size_t)capacity + 1, 4);
+ uint64_t *bits = calloc((size_t)capacity / 64 + 1, 8), x = 1, limit = ((size_t)capacity / 64 + 1) * 64;
+ APPENDS_OPEN(n, 16);
+ for (uint32_t i = 0; i < n; ++i) {
+ x = minstd(x), weight[i] = (uint32_t)(x % 1000) + 1;
+ x = minstd(x), worth[i] = (uint32_t)(x % 1000);
+ APPEND(16);
+ ROUND(30);
+ }
+ APPENDS_OPEN(capacity + 1, 8);
+ for (uint32_t c = 0; c <= capacity; ++c) {
+ NAIVE(!bit(bits, c));
+ bits[c >> 6] |= UINT64_C(1) << (c & 63);
+ APPEND(8);
+ ROUND(8);
+ }
+ bool full = true; /* hoisted: every key from 0 to the capacity there (its count is its keys' reach) */
+ HOISTED(full);
+ for (uint32_t i = 0; i < n; ++i) {
+ uint32_t w = weight[i], v = worth[i];
+ uint32_t rounds = capacity + 1 > w ? capacity + 1 - w : 0;
+ ROUNDS_OPEN(rounds, 20);
+ HOISTED((uint64_t)n * 1000 < (UINT64_C(1) << 32)); /* the best fits its column */
+ for (uint32_t k = 0; k < rounds; ++k) {
+ num c = F((num)capacity - (num)k);
+ uint64_t below = KEY(F(c - (num)w)), at = KEY(c);
+ NAIVE(below < limit && bit(bits, below));
+ num with = F((num)best[below] + (num)v);
+ NAIVE(at < limit && bit(bits, at));
+ if (with > (num)best[at]) {
+ NAIVE(with >= 0 && with < 0x1p32 && with == floor(with)); /* fits its column */
+ best[at] = (uint32_t)with;
+ }
+ ROUND(20);
+ }
+ ROUNDS_DONE(rounds, 20);
+ }
+ NAIVE(bit(bits, capacity));
+ out[0] = best[capacity];
+ free(weight), free(worth), free(best), free(bits);
+}
+
+/* ---- sort: the list kept by its numbers appended in order and sorted when walked (the runtime's
+ * sort: qsort as C's); a key twice is EXISTS: naive asks a set at each add, hoisted looks at
+ * neighbours once sorted */
+
+static uint32_t hash64(uint64_t x) { return (uint32_t)((x * UINT64_C(0x9E3779B97F4A7C15)) >> 32); }
+
+/* Numbers in order (none NaN): a quicksort, the median of three its pivot, small runs by insertion. */
+static void sort_numbers(double *v, size_t n) {
+ while (n > 24) {
+ double a = v[0], b = v[n / 2], c = v[n - 1];
+ double pivot = a < b ? (b < c ? b : a < c ? c : a) : (a < c ? a : b < c ? c : b);
+ size_t i = 0, j = n - 1;
+ for (;;) {
+ while (v[i] < pivot) ++i;
+ while (v[j] > pivot) --j;
+ if (i >= j) break;
+ double t = v[i];
+ v[i] = v[j], v[j] = t;
+ ++i, --j;
+ }
+ if (j + 1 < n - j - 1) sort_numbers(v, j + 1), v += j + 1, n -= j + 1; /* the smaller side first, the larger in the loop */
+ else sort_numbers(v + j + 1, n - j - 1), n = j + 1;
+ }
+ for (size_t i = 1; i < n; ++i) {
+ double t = v[i];
+ size_t j = i;
+ while (j && v[j - 1] > t) v[j] = v[j - 1], --j;
+ v[j] = t;
+ }
+}
+
+static void f_sort(const uint32_t *in, double *out) {
+ uint32_t n = in[0], size = 1;
+ while (size < 2 * n) size *= 2;
+ double *keys = malloc(8 * (size_t)n);
+ uint64_t *set = MODE == 2 ? calloc(size, 8) : NULL, x = 1;
+ APPENDS_OPEN(n, 8);
+ for (uint32_t i = 0; i < n; ++i) {
+ x = minstd(x);
+ if (MODE == 2) { /* EXISTS where it falls */
+ uint32_t at = hash64(x) & (size - 1);
+ while (set[at] && set[at] != x) at = (at + 1) & (size - 1);
+ if (set[at]) FAIL();
+ set[at] = x;
+ }
+ keys[i] = (double)x;
+ APPEND(8);
+ ROUND(14);
+ }
+ sort_numbers(keys, n); /* the runtime's sort: its comparison inline */
+ for (uint32_t i = 1; MODE == 1 && i < n; ++i) HOISTED(keys[i] != keys[i - 1]);
+ double before = -1, middle = 0, wrong = 0;
+ ROUNDS_OPEN(n, 16);
+ for (uint32_t at = 0; at < n; ++at) {
+ double k = keys[at];
+ if (k < before) wrong += 1;
+ if (at == n / 2) middle = k;
+ before = k;
+ ROUND(16);
+ }
+ ROUNDS_DONE(n, 16);
+ out[0] = keys[0], out[1] = middle, out[2] = keys[n - 1], out[3] = wrong;
+ free(keys), free(set);
+}
+
+/* ---- join: the customers a FLAT list (id: four bytes, its values below 2^32; region: one), byid a
+ * list kept by the sparse ids with a hash index whose slots hold a key and its position (keys of four
+ * bytes), totals a DENSE list of 16; a missing id is NOT_FOUND (the probe meets an empty slot). Whole
+ * x and a whole divisor: x - floor(x / d) * d is the integers' remainder (below 2^53, d > 0). */
+
+typedef struct { uint32_t key, at; } Slot; /* at: the position + 1, 0 when empty */
+
+static void f_join(const uint32_t *in, double *out) {
+ uint32_t customers = in[0], orders = in[1], size = 1;
+ while (size < 2 * customers) size *= 2;
+ uint32_t *ids = malloc(4 * (size_t)customers);
+ uint8_t *region = malloc(customers);
+ Slot *slots = calloc(size, sizeof(Slot));
+ uint64_t x = 1, totals[16] = {0}, totals_bits = 0;
+ APPENDS_OPEN(2 * (uint64_t)customers, 16);
+ for (uint32_t i = 0; i < customers; ++i) {
+ x = minstd(x), ids[i] = (uint32_t)x, region[i] = (uint8_t)(i % 16);
+ APPEND(16);
+ uint32_t at = hash32((uint32_t)x) & (size - 1);
+ while (slots[at].at && slots[at].key != x) at = (at + 1) & (size - 1);
+ NAIVE(!slots[at].at); /* EXISTS */
+ slots[at] = (Slot){(uint32_t)x, i + 1};
+ APPEND(16);
+ ROUND(40);
+ }
+ for (int r = 0; r < 16; ++r) totals_bits |= UINT64_C(1) << r;
+ HOISTED(totals_bits == 0xFFFF); /* totals full: a region below 16 is there */
+ ROUNDS_OPEN(orders, 40);
+ for (uint32_t o = 0; o < orders; ++o) {
+ x = minstd(x);
+#if MODE == 2
+ num xn = (num)x, cn = F(xn - F((num)floor(F(xn / customers)) * (num)customers));
+ num amount = F(xn - F((num)floor(F(xn / 1000)) * 1000));
+#else
+ num cn = (num)(x % customers), amount = (num)(x % 1000);
+#endif
+ uint64_t c = KEY(cn);
+ NAIVE(c < customers);
+ uint32_t id = ids[c], at = hash32(id) & (size - 1);
+ while (slots[at].key != id || !slots[at].at) {
+ if (!slots[at].at) FAIL(); /* NOT_FOUND: no id there (the probe needs it) */
+ at = (at + 1) & (size - 1);
+ }
+ uint32_t r = region[slots[at].at - 1];
+ HOISTED(r < 16);
+ NAIVE(totals_bits >> r & 1);
+ totals[r] = (uint64_t)F((num)totals[r] + amount);
+ ROUND(40);
+ }
+ ROUNDS_DONE(orders, 40);
+ uint64_t most = 0, sum = 0;
+ for (int r = 0; r < 16; ++r) most = totals[r] > most ? totals[r] : most, sum += totals[r];
+ out[0] = (double)most, out[1] = (double)sum;
+ free(ids), free(region), free(slots);
+}
+
+/* ---- words: "w{n}" written as Pure writes a whole number (its digits), counts a list kept by text with
+ * a hash index, ordered when walked (its words sorted, then the walk) */
+
+static uint32_t text_hash(const char *s, size_t n) {
+ uint32_t h = 2166136261u;
+ for (size_t i = 0; i < n; ++i) h = (h ^ (unsigned char)s[i]) * 16777619u;
+ return h;
+}
+
+static size_t digits(char *to, uint32_t v) {
+ char tmp[12];
+ size_t n = 0;
+ do tmp[n++] = (char)('0' + v % 10), v /= 10;
+ while (v);
+ for (size_t i = 0; i < n; ++i) to[i] = tmp[n - 1 - i];
+ return n;
+}
+
+typedef struct { char text[16]; uint32_t n; } Word;
+static int compare_words(const void *a, const void *b) { return strcmp(((const Word *)a)->text, ((const Word *)b)->text); }
+
+static void f_words(const uint32_t *in, double *out) {
+ uint32_t n = in[0], vocabulary = in[1], size = 1, distinct = 0;
+ while (size < 2 * vocabulary) size *= 2;
+ Word *words = malloc(sizeof(Word) * (size_t)vocabulary);
+ uint32_t *slots = calloc(size, 4); /* position + 1 */
+ uint64_t x = 1;
+ ROUNDS_OPEN(n, 30);
+ APPENDS_OPEN(vocabulary, 32);
+ for (uint32_t i = 0; i < n; ++i) {
+ x = minstd(x);
+ char w[16];
+ w[0] = 'w';
+ size_t len = 1 + digits(w + 1, (uint32_t)(x % vocabulary));
+ w[len] = 0;
+ uint32_t at = text_hash(w, len) & (size - 1);
+ while (slots[at] && strcmp(words[slots[at] - 1].text, w)) at = (at + 1) & (size - 1);
+ if (slots[at]) {
+ Word *c = &words[slots[at] - 1];
+ c->n += 1;
+ NAIVE(c->n < (UINT64_C(1) << 32)); /* its column */
+ } else {
+ memcpy(words[distinct].text, w, len + 1), words[distinct].n = 1;
+ slots[at] = ++distinct;
+ APPEND(32);
+ }
+ ROUND(30);
+ }
+ ROUNDS_DONE(n, 30);
+ qsort(words, distinct, sizeof(Word), compare_words); /* walked in the order of its keys */
+ uint32_t most = 0;
+ for (uint32_t i = 0; i < distinct; ++i) most = words[i].n > most ? words[i].n : most;
+ out[0] = distinct, out[1] = most;
+ free(words), free(slots);
+}
+
+/* ---- matmul: a and b DENSE lists, full, their cells one byte; whole numbers below 2^31 as integers (a
+ * product's sum below 200 * 99 * 99): none of it can be -0 or past 2^53 */
+
+static void f_matmul(const uint32_t *in, double *out) {
+ uint32_t n = in[0], cells = n * n;
+ uint8_t *a = malloc(cells), *b = malloc(cells);
+ uint64_t *abits = calloc(cells / 64 + 1, 8), *bbits = calloc(cells / 64 + 1, 8), x = 1, limit = ((uint64_t)cells / 64 + 1) * 64;
+ APPENDS_OPEN(2 * (uint64_t)cells, 8);
+ for (uint32_t i = 0; i < cells; ++i) {
+ x = minstd(x), a[i] = (uint8_t)(x % 100), abits[i >> 6] |= UINT64_C(1) << (i & 63);
+ x = minstd(x), b[i] = (uint8_t)(x % 100), bbits[i >> 6] |= UINT64_C(1) << (i & 63);
+ APPEND(16);
+ ROUND(40);
+ }
+ HOISTED(true); /* both full: every key below n * n there */
+ num total = 0, last = 0;
+ ROUNDS_OPEN((uint64_t)n * n * n, 24);
+ for (uint32_t i = 0; i < n; ++i)
+ for (uint32_t j = 0; j < n; ++j) {
+ num s = 0;
+ for (uint32_t k = 0; k < n; ++k) {
+ uint64_t ka = KEY(F(F((num)i * (num)n) + (num)k)), kb = KEY(F(F((num)k * (num)n) + (num)j));
+ NAIVE(ka < limit && bit(abits, ka));
+ NAIVE(kb < limit && bit(bbits, kb));
+ s = F(s + F((num)a[ka] * (num)b[kb]));
+ ROUND(24);
+ }
+ total = F(total + s), last = s;
+ }
+ ROUNDS_DONE((uint64_t)n * n * n, 24);
+ out[0] = (double)total, out[1] = (double)last;
+ free(a), free(b), free(abits), free(bbits);
+}
+
+/* ---- bfs: 022's narrow floor without reading ahead: edges a packed list (to: four bytes) with a bucket
+ * index (a node's slice its bucket), seen a DENSE list (bits, depth one byte), queue a FLAT list */
+
+static uint32_t *first; /* a node's first edge: the bucket index */
+static uint64_t *seen_bits;
+static uint8_t *seen_depth;
+static uint32_t *queue_node;
+
+static void f_bfs(const uint32_t *in, double *out) {
+ const Graph *g = &graph;
+ uint32_t nodes = g->nodes, limit = (nodes / 64 + 1) * 64;
+ memset(seen_bits, 0, (nodes / 64 + 1) * 8);
+ uint32_t count = 0, s = in[0];
+ queue_node[count++] = s;
+ seen_bits[s >> 6] |= UINT64_C(1) << (s & 63), seen_depth[s] = 0;
+ uint32_t reached = 1;
+ APPENDS_OPEN(2 * (uint64_t)nodes, 16);
+ ROUNDS_OPEN(nodes, 30);
+ uint32_t i = 0;
+ for (; i < nodes; ++i) {
+ if (i >= count) break;
+ NAIVE(i < count);
+ uint32_t q = queue_node[i];
+ uint32_t lo = first[q], hi = first[q + 1];
+ ROUNDS_OPEN(hi - lo, 17);
+ for (uint32_t e = lo; e < hi; ++e) {
+ uint32_t to = g->to[e];
+ NAIVE(to < limit);
+ if (!bit(seen_bits, to)) {
+ NAIVE(bit(seen_bits, q));
+ uint32_t d = seen_depth[q] + 1u;
+ HOISTED(d < 256); /* depth's column one byte: widened past it */
+ NAIVE(to < limit && !bit(seen_bits, to));
+ seen_bits[to >> 6] |= UINT64_C(1) << (to & 63), seen_depth[to] = (uint8_t)d;
+ queue_node[count++] = to;
+ ++reached;
+ APPEND(32);
+ }
+ ROUND(17);
+ }
+ ROUNDS_DONE(hi - lo, 17);
+ ROUND(30);
+ }
+ ROUNDS_DONE(i, 30);
+ uint32_t t = in[1];
+ out[0] = bit(seen_bits, t) ? seen_depth[t] : -1, out[1] = reached;
+}
+
+typedef struct {
+ const char *name;
+ uint32_t args[2];
+ unsigned answers;
+ void (*floor)(const uint32_t *, double *), (*c)(const uint32_t *, double *);
+} Work;
+
+static const Work works[] = {
+ {"roots", {100000}, 2, f_roots, c_roots},
+ {"knapsack", {100, 50000}, 1, f_knapsack, c_knapsack},
+ {"sort", {200000}, 4, f_sort, c_sort},
+ {"join", {100000, 1000000}, 2, f_join, c_join},
+ {"words", {300000, 50000}, 2, f_words, c_words},
+ {"matmul", {200}, 2, f_matmul, c_matmul},
+ {"bfs", {0, 0}, 2, f_bfs, c_bfs},
+};
+
+int main(int argc, char **argv) {
+ if (argc < 2) { fputs("floor ROUNDS [NAME...]\n", stderr); return 2; }
+ unsigned rounds = (unsigned)atoi(argv[1]);
+ (void)c_bfs_ahead; /* suite-run's */
+ uint64_t *keys;
+ size_t edges = graph_build(100000, 1000000, &keys);
+ first = calloc(100001 + 1, 4), seen_bits = calloc(100000 / 64 + 1, 8), seen_depth = malloc(100000), queue_node = malloc(4 * 100000);
+ for (size_t i = 0; i < edges; ++i) ++first[(keys[i] >> 20) + 1];
+ for (uint32_t v = 0; v < 100001; ++v) first[v + 1] += first[v];
+ free(keys);
+ int failed_any = 0;
+ for (size_t w = 0; w < sizeof(works) / sizeof(*works); ++w) {
+ bool wanted = argc == 2;
+ for (int a = 2; a < argc; ++a) wanted = wanted || !strcmp(argv[a], works[w].name);
+ if (!wanted) continue;
+ double *f_ms = malloc(rounds * 8), *c_ms = malloc(rounds * 8);
+ bool agree = true;
+ for (unsigned r = 0; r < rounds; ++r) {
+ uint32_t args[2] = {works[w].args[0], works[w].args[1]};
+ if (!strcmp(works[w].name, "bfs")) args[0] = (uint32_t)(graph_next() % 100000), args[1] = (uint32_t)(graph_next() % 100000);
+ double fo[4] = {0}, co[4] = {0};
+ work = grown = 0;
+ double a = now_ms();
+ if (setjmp(failed)) {
+ fprintf(stderr, "%s: failed\n", works[w].name);
+ return 1;
+ }
+ works[w].floor(args, fo);
+ double b = now_ms();
+ works[w].c(args, co);
+ double c = now_ms();
+ f_ms[r] = b - a, c_ms[r] = c - b;
+ bool same = true;
+ for (unsigned i = 0; i < works[w].answers; ++i) same = same && fo[i] == co[i];
+ agree = agree && same;
+ printf("{\"mode\":%d,\"work\":\"%s\",\"round\":%u,\"floor_ms\":%.3f,\"c_ms\":%.3f,\"same\":%s}\n", MODE, works[w].name, r,
+ f_ms[r], c_ms[r], same ? "true" : "false");
+ }
+ qsort(f_ms, rounds, 8, compare_doubles), qsort(c_ms, rounds, 8, compare_doubles);
+ printf("{\"mode\":%d,\"work\":\"%s\",\"floor_median_ms\":%.3f,\"c_median_ms\":%.3f,\"ratio\":%.3f,\"agree\":%s}\n", MODE,
+ works[w].name, f_ms[rounds / 2], c_ms[rounds / 2], f_ms[rounds / 2] / c_ms[rounds / 2], agree ? "true" : "false");
+ fflush(stdout);
+ failed_any |= !agree;
+ free(f_ms), free(c_ms);
+ }
+ return failed_any;
+}
diff --git a/research/experiments/040-against-c/natural.h b/research/experiments/040-against-c/natural.h
new file mode 100644
index 0000000..dfd8a44
--- /dev/null
+++ b/research/experiments/040-against-c/natural.h
@@ -0,0 +1,201 @@
+/* The C each workload of research 124's suite is held to (suite-run.c,
+ * floor.c): what one would write for the task, its answers in out[]. */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static uint64_t minstd(uint64_t x) { return x * 48271 % 2147483647; }
+
+/* ---- The C of each workload: its answers in out[] */
+
+static void c_roots(const uint32_t *in, double *out) {
+ double sum = 0, count = 0;
+ for (uint32_t i = 1; i <= in[0]; ++i) {
+ double r = i;
+ for (int s = 0; s < 64; ++s) {
+ double next = (r + i / r) / 2;
+ if (next >= r) break;
+ r = next;
+ ++count;
+ }
+ sum += r;
+ }
+ out[0] = sum, out[1] = count;
+}
+
+static void c_knapsack(const uint32_t *in, double *out) {
+ uint32_t n = in[0], capacity = in[1];
+ uint32_t *weight = malloc(4 * (size_t)n), *worth = malloc(4 * (size_t)n);
+ int64_t *best = calloc((size_t)capacity + 1, 8);
+ uint64_t x = 1;
+ for (uint32_t i = 0; i < n; ++i) {
+ x = minstd(x), weight[i] = (uint32_t)(x % 1000) + 1;
+ x = minstd(x), worth[i] = (uint32_t)(x % 1000);
+ }
+ for (uint32_t i = 0; i < n; ++i)
+ for (uint32_t c = capacity; c >= weight[i] && c <= capacity; --c) {
+ int64_t with = best[c - weight[i]] + worth[i];
+ if (with > best[c]) best[c] = with;
+ }
+ out[0] = (double)best[capacity];
+ free(weight), free(worth), free(best);
+}
+
+static int compare_u32(const void *a, const void *b) {
+ uint32_t x = *(const uint32_t *)a, y = *(const uint32_t *)b;
+ return (x > y) - (x < y);
+}
+
+static void c_sort(const uint32_t *in, double *out) {
+ uint32_t n = in[0];
+ uint32_t *keys = malloc(4 * (size_t)n);
+ uint64_t x = 1;
+ for (uint32_t i = 0; i < n; ++i) x = minstd(x), keys[i] = (uint32_t)x;
+ qsort(keys, n, 4, compare_u32);
+ uint32_t wrong = 0;
+ for (uint32_t i = 1; i < n; ++i) wrong += keys[i] < keys[i - 1];
+ out[0] = keys[0], out[1] = keys[n / 2], out[2] = keys[n - 1], out[3] = wrong;
+ free(keys);
+}
+
+static uint32_t hash32(uint32_t x) { return x * 2654435761u; }
+
+static void c_join(const uint32_t *in, double *out) {
+ uint32_t customers = in[0], orders = in[1], size = 1;
+ while (size < 2 * customers) size *= 2;
+ uint32_t *ids = malloc(4 * (size_t)customers), *keys = calloc(size, 4), *regions = malloc(4 * (size_t)size);
+ uint64_t x = 1, totals[16] = {0};
+ for (uint32_t i = 0; i < customers; ++i) {
+ x = minstd(x), ids[i] = (uint32_t)x;
+ uint32_t at = hash32(ids[i]) & (size - 1);
+ while (keys[at]) at = (at + 1) & (size - 1);
+ keys[at] = ids[i], regions[at] = i % 16;
+ }
+ for (uint32_t o = 0; o < orders; ++o) {
+ x = minstd(x);
+ uint32_t id = ids[x % customers], at = hash32(id) & (size - 1);
+ while (keys[at] != id) at = (at + 1) & (size - 1);
+ totals[regions[at]] += x % 1000;
+ }
+ uint64_t most = 0, sum = 0;
+ for (int r = 0; r < 16; ++r) most = totals[r] > most ? totals[r] : most, sum += totals[r];
+ out[0] = (double)most, out[1] = (double)sum;
+ free(ids), free(keys), free(regions);
+}
+
+static uint32_t fnv(const char *s) {
+ uint32_t h = 2166136261u;
+ for (; *s; ++s) h = (h ^ (unsigned char)*s) * 16777619u;
+ return h;
+}
+
+static void c_words(const uint32_t *in, double *out) {
+ uint32_t n = in[0], vocabulary = in[1], size = 1;
+ while (size < 2 * vocabulary) size *= 2;
+ char (*words)[16] = calloc(size, 16);
+ uint32_t *counts = calloc(size, 4), distinct = 0, most = 0;
+ uint64_t x = 1;
+ for (uint32_t i = 0; i < n; ++i) {
+ x = minstd(x);
+ char w[16];
+ snprintf(w, sizeof(w), "w%u", (unsigned)(x % vocabulary));
+ uint32_t at = fnv(w) & (size - 1);
+ while (counts[at] && strcmp(words[at], w)) at = (at + 1) & (size - 1);
+ if (!counts[at]) memcpy(words[at], w, sizeof(w)), ++distinct;
+ if (++counts[at] > most) most = counts[at];
+ }
+ out[0] = distinct, out[1] = most;
+ free(words), free(counts);
+}
+
+static void c_matmul(const uint32_t *in, double *out) {
+ uint32_t n = in[0];
+ double *a = malloc(8 * (size_t)n * n), *b = malloc(8 * (size_t)n * n), total = 0, last = 0;
+ uint64_t x = 1;
+ for (uint32_t i = 0; i < n * n; ++i) {
+ x = minstd(x), a[i] = (double)(x % 100);
+ x = minstd(x), b[i] = (double)(x % 100);
+ }
+ for (uint32_t i = 0; i < n; ++i)
+ for (uint32_t j = 0; j < n; ++j) {
+ double s = 0;
+ for (uint32_t k = 0; k < n; ++k) s += a[i * n + k] * b[k * n + j];
+ total += s, last = s;
+ }
+ out[0] = total, out[1] = last;
+ free(a), free(b);
+}
+
+/* ---- BFS: 033's graph (bfs-run's generator, seed 121), bfs.c's loop */
+
+typedef struct { uint32_t nodes, *start, *to, *queue; int32_t *depth; } Graph;
+static Graph graph;
+static uint64_t graph_state = 121;
+static uint64_t graph_next(void) {
+ graph_state ^= graph_state << 13, graph_state ^= graph_state >> 7, graph_state ^= graph_state << 17;
+ return graph_state;
+}
+static int compare_u64(const void *a, const void *b) {
+ uint64_t x = *(const uint64_t *)a, y = *(const uint64_t *)b;
+ return (x > y) - (x < y);
+}
+
+static void c_bfs(const uint32_t *in, double *out) {
+ const Graph *g = &graph;
+ for (uint32_t v = 0; v < g->nodes; ++v) g->depth[v] = -1;
+ uint32_t head = 0, tail = 0;
+ g->queue[tail++] = in[0];
+ g->depth[in[0]] = 0;
+ while (head < tail) {
+ uint32_t v = g->queue[head++];
+ for (uint32_t k = g->start[v]; k < g->start[v + 1]; ++k)
+ if (g->depth[g->to[k]] < 0) g->depth[g->to[k]] = g->depth[v] + 1, g->queue[tail++] = g->to[k];
+ }
+ out[0] = g->depth[in[1]], out[1] = tail;
+}
+
+/* bfs.c read ahead as 022's floor reads Pure's (bfsfloor's c_ahead): the
+ * offsets of the node 16 on in the queue, the first edge of the one 8 on
+ * (context for Pure's reading ahead, not the C it is held to). */
+static void c_bfs_ahead(const uint32_t *in, double *out) {
+ const Graph *g = &graph;
+ for (uint32_t v = 0; v < g->nodes; ++v) g->depth[v] = -1;
+ uint32_t head = 0, tail = 0;
+ g->queue[tail++] = in[0];
+ g->depth[in[0]] = 0;
+ while (head < tail) {
+ if (head + 16 < tail) __builtin_prefetch(&g->start[g->queue[head + 16]]);
+ if (head + 8 < tail) __builtin_prefetch(&g->to[g->start[g->queue[head + 8]]]);
+ uint32_t v = g->queue[head++];
+ for (uint32_t k = g->start[v]; k < g->start[v + 1]; ++k)
+ if (g->depth[g->to[k]] < 0) g->depth[g->to[k]] = g->depth[v] + 1, g->queue[tail++] = g->to[k];
+ }
+ out[0] = g->depth[in[1]], out[1] = tail;
+}
+
+
+/* 033's graph of nodes and edges (bfs-run's generator, seed 121): C's
+ * adjacency in graph, and the edges as from * 2^20 + to, sorted, each once,
+ * in *keys (the caller's to free); their count. */
+static size_t graph_build(uint32_t nodes, size_t edges, uint64_t **keys_out) {
+ graph_state = 121; /* the same graph each time */
+ free(graph.start), free(graph.to), free(graph.queue), free(graph.depth);
+ uint64_t *keys = malloc(edges * sizeof(*keys));
+ size_t count = 0;
+ while (count < edges) {
+ uint64_t a = graph_next() % nodes, b = graph_next() % nodes;
+ if (a != b) keys[count++] = a << 20 | b;
+ }
+ qsort(keys, count, sizeof(*keys), compare_u64);
+ size_t unique = 0;
+ for (size_t i = 0; i < count; ++i)
+ if (!unique || keys[i] != keys[unique - 1]) keys[unique++] = keys[i];
+ graph.nodes = nodes;
+ graph.start = calloc((size_t)nodes + 1, 4), graph.to = malloc(4 * unique), graph.queue = malloc(4ull * nodes);
+ graph.depth = malloc(4ull * nodes);
+ for (size_t i = 0; i < unique; ++i) ++graph.start[(keys[i] >> 20) + 1], graph.to[i] = (uint32_t)(keys[i] & 0xFFFFF);
+ for (uint32_t v = 0; v < nodes; ++v) graph.start[v + 1] += graph.start[v];
+ *keys_out = keys;
+ return unique;
+}
diff --git a/research/experiments/040-against-c/runs.sh b/research/experiments/040-against-c/runs.sh
index 3c83cf4..8c1620b 100644
--- a/research/experiments/040-against-c/runs.sh
+++ b/research/experiments/040-against-c/runs.sh
@@ -7,6 +7,8 @@
# Linux AArch64), the executor alone
# (PURE_JIT=0), and BFS without reading ahead
# (PURE_AHEAD=0)
+# runs.sh floor ROUNDS floor.c's three modes (bare, hoisted, naive),
+# each against natural.h's C
set -e
here=research/experiments/040-against-c
builds=$HOME/.cache/pure-040
@@ -33,6 +35,12 @@ suite)
quiet
PURE_AHEAD=0 python3 research/guard.py --most 3072 -- "$builds/$2/suite-run" "$builds/$2" "$3" bfs bfs_ahead \
| sed "s/^{/{\"version\":\"$2\",\"jit\":1,\"ahead\":0,/" >> "$here/$day-suite.jsonl" ;;
-*) echo "runs.sh build NAME COMMIT | suite NAME ROUNDS" >&2
+floor)
+ for mode in 0 1 2; do
+ cc -O2 -std=c17 -DMODE=$mode -o "$builds/floor-$mode" $here/floor.c -lm
+ quiet
+ python3 research/guard.py --most 2048 -- "$builds/floor-$mode" "$2" >> "$here/$day-floor.jsonl"
+ done ;;
+*) echo "runs.sh build NAME COMMIT | suite NAME ROUNDS | floor ROUNDS" >&2
exit 2 ;;
esac
diff --git a/research/experiments/040-against-c/suite-run.c b/research/experiments/040-against-c/suite-run.c
index 3efbf94..f8c03e7 100644
--- a/research/experiments/040-against-c/suite-run.c
+++ b/research/experiments/040-against-c/suite-run.c
@@ -30,194 +30,12 @@ static int compare_doubles(const void *a, const void *b) {
return (x > y) - (x < y);
}
-static uint64_t minstd(uint64_t x) { return x * 48271 % 2147483647; }
-
-/* ---- The C of each workload: its answers in out[] */
-
-static void c_roots(const uint32_t *in, double *out) {
- double sum = 0, count = 0;
- for (uint32_t i = 1; i <= in[0]; ++i) {
- double r = i;
- for (int s = 0; s < 64; ++s) {
- double next = (r + i / r) / 2;
- if (next >= r) break;
- r = next;
- ++count;
- }
- sum += r;
- }
- out[0] = sum, out[1] = count;
-}
-
-static void c_knapsack(const uint32_t *in, double *out) {
- uint32_t n = in[0], capacity = in[1];
- uint32_t *weight = malloc(4 * (size_t)n), *worth = malloc(4 * (size_t)n);
- int64_t *best = calloc((size_t)capacity + 1, 8);
- uint64_t x = 1;
- for (uint32_t i = 0; i < n; ++i) {
- x = minstd(x), weight[i] = (uint32_t)(x % 1000) + 1;
- x = minstd(x), worth[i] = (uint32_t)(x % 1000);
- }
- for (uint32_t i = 0; i < n; ++i)
- for (uint32_t c = capacity; c >= weight[i] && c <= capacity; --c) {
- int64_t with = best[c - weight[i]] + worth[i];
- if (with > best[c]) best[c] = with;
- }
- out[0] = (double)best[capacity];
- free(weight), free(worth), free(best);
-}
-
-static int compare_u32(const void *a, const void *b) {
- uint32_t x = *(const uint32_t *)a, y = *(const uint32_t *)b;
- return (x > y) - (x < y);
-}
-
-static void c_sort(const uint32_t *in, double *out) {
- uint32_t n = in[0];
- uint32_t *keys = malloc(4 * (size_t)n);
- uint64_t x = 1;
- for (uint32_t i = 0; i < n; ++i) x = minstd(x), keys[i] = (uint32_t)x;
- qsort(keys, n, 4, compare_u32);
- uint32_t wrong = 0;
- for (uint32_t i = 1; i < n; ++i) wrong += keys[i] < keys[i - 1];
- out[0] = keys[0], out[1] = keys[n / 2], out[2] = keys[n - 1], out[3] = wrong;
- free(keys);
-}
-
-static uint32_t hash32(uint32_t x) { return x * 2654435761u; }
-
-static void c_join(const uint32_t *in, double *out) {
- uint32_t customers = in[0], orders = in[1], size = 1;
- while (size < 2 * customers) size *= 2;
- uint32_t *ids = malloc(4 * (size_t)customers), *keys = calloc(size, 4), *regions = malloc(4 * (size_t)size);
- uint64_t x = 1, totals[16] = {0};
- for (uint32_t i = 0; i < customers; ++i) {
- x = minstd(x), ids[i] = (uint32_t)x;
- uint32_t at = hash32(ids[i]) & (size - 1);
- while (keys[at]) at = (at + 1) & (size - 1);
- keys[at] = ids[i], regions[at] = i % 16;
- }
- for (uint32_t o = 0; o < orders; ++o) {
- x = minstd(x);
- uint32_t id = ids[x % customers], at = hash32(id) & (size - 1);
- while (keys[at] != id) at = (at + 1) & (size - 1);
- totals[regions[at]] += x % 1000;
- }
- uint64_t most = 0, sum = 0;
- for (int r = 0; r < 16; ++r) most = totals[r] > most ? totals[r] : most, sum += totals[r];
- out[0] = (double)most, out[1] = (double)sum;
- free(ids), free(keys), free(regions);
-}
-
-static uint32_t fnv(const char *s) {
- uint32_t h = 2166136261u;
- for (; *s; ++s) h = (h ^ (unsigned char)*s) * 16777619u;
- return h;
-}
-
-static void c_words(const uint32_t *in, double *out) {
- uint32_t n = in[0], vocabulary = in[1], size = 1;
- while (size < 2 * vocabulary) size *= 2;
- char (*words)[16] = calloc(size, 16);
- uint32_t *counts = calloc(size, 4), distinct = 0, most = 0;
- uint64_t x = 1;
- for (uint32_t i = 0; i < n; ++i) {
- x = minstd(x);
- char w[16];
- snprintf(w, sizeof(w), "w%u", (unsigned)(x % vocabulary));
- uint32_t at = fnv(w) & (size - 1);
- while (counts[at] && strcmp(words[at], w)) at = (at + 1) & (size - 1);
- if (!counts[at]) memcpy(words[at], w, sizeof(w)), ++distinct;
- if (++counts[at] > most) most = counts[at];
- }
- out[0] = distinct, out[1] = most;
- free(words), free(counts);
-}
-
-static void c_matmul(const uint32_t *in, double *out) {
- uint32_t n = in[0];
- double *a = malloc(8 * (size_t)n * n), *b = malloc(8 * (size_t)n * n), total = 0, last = 0;
- uint64_t x = 1;
- for (uint32_t i = 0; i < n * n; ++i) {
- x = minstd(x), a[i] = (double)(x % 100);
- x = minstd(x), b[i] = (double)(x % 100);
- }
- for (uint32_t i = 0; i < n; ++i)
- for (uint32_t j = 0; j < n; ++j) {
- double s = 0;
- for (uint32_t k = 0; k < n; ++k) s += a[i * n + k] * b[k * n + j];
- total += s, last = s;
- }
- out[0] = total, out[1] = last;
- free(a), free(b);
-}
-
-/* ---- BFS: 033's graph (bfs-run's generator, seed 121), bfs.c's loop */
-
-typedef struct { uint32_t nodes, *start, *to, *queue; int32_t *depth; } Graph;
-static Graph graph;
-static uint64_t graph_state = 121;
-static uint64_t graph_next(void) {
- graph_state ^= graph_state << 13, graph_state ^= graph_state >> 7, graph_state ^= graph_state << 17;
- return graph_state;
-}
-static int compare_u64(const void *a, const void *b) {
- uint64_t x = *(const uint64_t *)a, y = *(const uint64_t *)b;
- return (x > y) - (x < y);
-}
-
-static void c_bfs(const uint32_t *in, double *out) {
- const Graph *g = &graph;
- for (uint32_t v = 0; v < g->nodes; ++v) g->depth[v] = -1;
- uint32_t head = 0, tail = 0;
- g->queue[tail++] = in[0];
- g->depth[in[0]] = 0;
- while (head < tail) {
- uint32_t v = g->queue[head++];
- for (uint32_t k = g->start[v]; k < g->start[v + 1]; ++k)
- if (g->depth[g->to[k]] < 0) g->depth[g->to[k]] = g->depth[v] + 1, g->queue[tail++] = g->to[k];
- }
- out[0] = g->depth[in[1]], out[1] = tail;
-}
-
-/* bfs.c read ahead as 022's floor reads Pure's (bfsfloor's c_ahead): the
- * offsets of the node 16 on in the queue, the first edge of the one 8 on
- * (context for Pure's reading ahead, not the C it is held to). */
-static void c_bfs_ahead(const uint32_t *in, double *out) {
- const Graph *g = &graph;
- for (uint32_t v = 0; v < g->nodes; ++v) g->depth[v] = -1;
- uint32_t head = 0, tail = 0;
- g->queue[tail++] = in[0];
- g->depth[in[0]] = 0;
- while (head < tail) {
- if (head + 16 < tail) __builtin_prefetch(&g->start[g->queue[head + 16]]);
- if (head + 8 < tail) __builtin_prefetch(&g->to[g->start[g->queue[head + 8]]]);
- uint32_t v = g->queue[head++];
- for (uint32_t k = g->start[v]; k < g->start[v + 1]; ++k)
- if (g->depth[g->to[k]] < 0) g->depth[g->to[k]] = g->depth[v] + 1, g->queue[tail++] = g->to[k];
- }
- out[0] = g->depth[in[1]], out[1] = tail;
-}
+#include "natural.h"
/* The graph into the app (size, then the edges as rows) and into C's adjacency. */
static bool load_graph(PureApp *app, const PureModule *m, uint32_t nodes, size_t edges) {
- graph_state = 121; /* the same graph each time */
- free(graph.start), free(graph.to), free(graph.queue), free(graph.depth);
- uint64_t *keys = malloc(edges * sizeof(*keys));
- size_t count = 0;
- while (count < edges) {
- uint64_t a = graph_next() % nodes, b = graph_next() % nodes;
- if (a != b) keys[count++] = a << 20 | b;
- }
- qsort(keys, count, sizeof(*keys), compare_u64);
- size_t unique = 0;
- for (size_t i = 0; i < count; ++i)
- if (!unique || keys[i] != keys[unique - 1]) keys[unique++] = keys[i];
- graph.nodes = nodes;
- graph.start = calloc((size_t)nodes + 1, 4), graph.to = malloc(4 * unique), graph.queue = malloc(4ull * nodes);
- graph.depth = malloc(4ull * nodes);
- for (size_t i = 0; i < unique; ++i) ++graph.start[(keys[i] >> 20) + 1], graph.to[i] = (uint32_t)(keys[i] & 0xFFFFF);
- for (uint32_t v = 0; v < nodes; ++v) graph.start[v + 1] += graph.start[v];
+ uint64_t *keys;
+ size_t unique = graph_build(nodes, edges, &keys);
unsigned char four[4];
memcpy(four, &nodes, 4);
if (pure_app_message(app, (uint32_t)pure_module_input(m, "size"), NULL, -1, (PureSpan){four, 4}) != PURE_OK) return false;