diff options
| author | eunhhu <[email protected]> | 2026-10-05 13:33:32 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-05 13:33:32 +0100 |
| commit | ca1acb2be40c8c65af46ba83883cf20edb36c993 (patch) | |
| tree | d360cee25d3b0cf2c72c01660e6d869c6969c231 /research/experiments/040-against-c | |
| parent | a5f3f6c4f79245103299befe14126834f5bc4ecd (diff) | |
| download | pure-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/experiments/040-against-c')
| -rw-r--r-- | research/experiments/040-against-c/2026-10-05-floor.jsonl | 252 | ||||
| -rw-r--r-- | research/experiments/040-against-c/floor.c | 522 | ||||
| -rw-r--r-- | research/experiments/040-against-c/natural.h | 201 | ||||
| -rw-r--r-- | research/experiments/040-against-c/runs.sh | 10 | ||||
| -rw-r--r-- | research/experiments/040-against-c/suite-run.c | 188 |
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; |
