From b1ce1927022d374198441afafeb27199fc8a052a Mon Sep 17 00:00:00 2001 From: eunhhu Date: Thu, 24 Sep 2026 17:14:54 +0100 Subject: Establish native micro runtime checkpoint --- .../experiments/004-direct-document-path/README.md | 50 ++++ .../004-direct-document-path/document-path.c | 207 +++++++++++++ .../004-direct-document-path/peak-launcher.c | 26 ++ .../results/2026-09-24-ext4-1m-linux-aarch64.json | 251 ++++++++++++++++ .../results/2026-09-24-ext4-8k-linux-aarch64.json | 251 ++++++++++++++++ ...6-09-24-tmpfs-1m-preliminary-linux-aarch64.json | 238 +++++++++++++++ ...6-09-24-tmpfs-8k-preliminary-linux-aarch64.json | 322 +++++++++++++++++++++ .../experiments/004-direct-document-path/run.py | 160 ++++++++++ 8 files changed, 1505 insertions(+) create mode 100644 research/experiments/004-direct-document-path/README.md create mode 100644 research/experiments/004-direct-document-path/document-path.c create mode 100644 research/experiments/004-direct-document-path/peak-launcher.c create mode 100644 research/experiments/004-direct-document-path/results/2026-09-24-ext4-1m-linux-aarch64.json create mode 100644 research/experiments/004-direct-document-path/results/2026-09-24-ext4-8k-linux-aarch64.json create mode 100644 research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-1m-preliminary-linux-aarch64.json create mode 100644 research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-8k-preliminary-linux-aarch64.json create mode 100644 research/experiments/004-direct-document-path/run.py (limited to 'research/experiments/004-direct-document-path') diff --git a/research/experiments/004-direct-document-path/README.md b/research/experiments/004-direct-document-path/README.md new file mode 100644 index 0000000..ae4e3b8 --- /dev/null +++ b/research/experiments/004-direct-document-path/README.md @@ -0,0 +1,50 @@ +# Direct store calls versus the Pure document path + +This is an end-to-end comparison for the existing `editor.pure` document app. +Both variants perform, for each document, a conditional create, a full read, +a conditional one-byte replacement at the end, and another full read. They +check revisions and complete bodies and compute the same digest. Both use the +same `store.c`: a write creates and synchronizes a replacement file, renames +it, then synchronizes the directory. The harness verifies equal digests and +equal final file bytes for each pair of runs. + +The direct executable calls `pure_store_splice` and `pure_store_select`; the +Pure executable runs the prepared `editor.pure` plan through `PureRuntime` and +`pure_store_host`. The direct executable mirrors only this valid-operation +workflow. It does **not** provide the runtime's admission, ownership, budget, +completion, or cancellation contract and is not a feature-equivalent runtime. +The comparison isolates the cost of choosing that contract for this app path; +it does not compare two complete products. + +Run on a Linux host with a C17 compiler, Python 3, GNU `size`, and `df`: + +```sh +python3 research/experiments/004-direct-document-path/run.py \ + --documents 256 --body-bytes 8192 --pairs 4 \ + --output research/experiments/004-direct-document-path/results/local-8k.json +python3 research/experiments/004-direct-document-path/run.py \ + --documents 32 --body-bytes 1048576 --pairs 4 \ + --output research/experiments/004-direct-document-path/results/local-1m.json +``` + +The runner uses a fresh temporary directory for each variant and alternates +direct/Pure order between pairs. Its default `--work-root` is this directory's +`results/`, so the store is on the repository filesystem. It records the +filesystem type, compiler, source hashes, executable hashes, linked sections, +operation counts, four stage times, wall time, user/system CPU time, and peak +child RSS. A small native parent collects RSS because a Python process can +otherwise contaminate the reported child high-water mark across `exec`. +Warmups are untimed, and the runner refuses to compare against a changed +`editor.pure` definition until the direct mirror is reviewed. + +The dated `ext4` JSON files are the report's evidence. The dated `tmpfs` +files are exploratory runs made before filesystem recording was added; their +store was under `/tmp` on this host. They exercised the same store calls but +cannot represent persistent-media latency. One run, even with alternating +order, cannot control host I/O scheduling, cache state, storage firmware, or +clock changes. The byte digest proves this workload's result equality, not +full behavioral equivalence in failures or concurrency. + +The [result and interpretation](../../../docs/research/054-direct-document-path.md) +distinguish linked executable size from the 5,908-byte standalone core and +avoid a speed claim unsupported by the samples. diff --git a/research/experiments/004-direct-document-path/document-path.c b/research/experiments/004-direct-document-path/document-path.c new file mode 100644 index 0000000..b4d6464 --- /dev/null +++ b/research/experiments/004-direct-document-path/document-path.c @@ -0,0 +1,207 @@ +#define _POSIX_C_SOURCE 200809L +/* Two separately linked versions of one durable document workload. The direct + * build invokes the same store primitives that the Pure host uses; the Pure + * build runs the prepared editor.pure program through its execution core. */ +#include "store.h" +#ifndef DIRECT_PATH +#include "runtime.h" +#include "store-port.h" +#include "embedded-plan.h" +#endif +#include +#include +#include +#include +#include + +#ifndef PURE_BENCH_BODY_BYTES +#define PURE_BENCH_BODY_BYTES 8192 +#endif +enum { BODY_BYTES = PURE_BENCH_BODY_BYTES }; +static uint64_t digest = UINT64_C(14695981039346656037); + +static void fail(const char *message) { + fprintf(stderr, "%s\n", message); + exit(1); +} + +static void check(int condition, const char *message) { + if (!condition) fail(message); +} + +static void hash_body(const unsigned char *body, size_t size, int64_t revision) { + for (size_t i = 0; i < size; ++i) { + digest ^= body[i]; + digest *= UINT64_C(1099511628211); + } + digest ^= (uint64_t)revision; + digest *= UINT64_C(1099511628211); +} + +static uint64_t nanoseconds(void) { + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now)) fail("clock_gettime failed"); + return (uint64_t)now.tv_sec * UINT64_C(1000000000) + (uint64_t)now.tv_nsec; +} + +static void pattern(unsigned char *body, unsigned document) { + for (size_t i = 0; i < BODY_BYTES; ++i) + body[i] = (unsigned char)('a' + (document + i) % 26); +} + +#ifdef DIRECT_PATH +static void create(const char *store, const char *key, unsigned document, + const unsigned char *expected) { + unsigned char *input = malloc(BODY_BYTES); + check(input != NULL, "direct input allocation failed"); + pattern(input, document); + check(!memcmp(input, expected, BODY_BYTES), "direct input mismatch"); + int64_t revision = 0; + PureResult status = pure_store_splice(store, key, 0, 0, 0, + (PureSpan){input, BODY_BYTES}, &revision); + check(status == PURE_OK && revision == 1, "direct create failed"); + free(input); +} + +static void edit_last(const char *store, const char *key, unsigned char replacement) { + unsigned char *input = malloc(1); + check(input != NULL, "direct edit allocation failed"); + *input = replacement; + int64_t revision = 0; + PureResult status = pure_store_splice(store, key, 1, BODY_BYTES - 1, 1, + (PureSpan){input, 1}, &revision); + check(status == PURE_OK && revision == 2, "direct edit failed"); + free(input); +} + +static void read_checked(const char *store, const char *key, + const unsigned char *expected, int64_t revision) { + PureStoredView view = {0}; + PureResult status = pure_store_select(store, key, 0, 0, true, 0, + 4u * 1024u * 1024u, &view); + check(status == PURE_OK && view.revision == revision && + view.body.size == BODY_BYTES && !memcmp(view.body.data, expected, BODY_BYTES), + "direct read mismatch"); + hash_body(view.body.data, view.body.size, revision); + pure_store_release(&view); +} +#else +static PureRuntime runtime; +static PureWork work[1]; + +static PureOutcome completed(PureTicket ticket) { + PureOutcome result; + while (!pure_runtime_result(ticket, &result)) + check(pure_runtime_step(&runtime), "runtime made no progress"); + return result; +} + +static void write_checked(const char *key, int64_t expected_revision, + size_t offset, size_t remove, size_t insert_size, + unsigned document, unsigned char replacement, + const unsigned char *expected_reply, int64_t revision) { + PureTicket ticket; + check(pure_runtime_accept(&runtime, PURE_INPUT_WRITE, key, expected_revision, + 8 + insert_size, &ticket) == PURE_OK, "runtime write admission failed"); + size_t size = 0; + unsigned char *input = pure_runtime_ingress(ticket, &size); + check(input != NULL && size == 8 + insert_size, "runtime input loan failed"); + for (unsigned i = 0; i < 4; ++i) { + input[i] = (unsigned char)(offset >> (8 * i)); + input[4 + i] = (unsigned char)(remove >> (8 * i)); + } + if (insert_size == BODY_BYTES) pattern(input + 8, document); + else input[8] = replacement; + check(pure_runtime_ready(ticket), "runtime publication failed"); + PureOutcome result = completed(ticket); + check(result.status == PURE_OK && result.revision == revision && + result.body.size == insert_size && + !memcmp(result.body.data, expected_reply, insert_size), + "runtime write result mismatch"); + check(pure_runtime_release(ticket) && !runtime.active && !runtime.retained, + "runtime write resource not released"); +} + +static void read_checked(const char *store, const char *key, + const unsigned char *expected, int64_t revision) { + (void)store; + PureTicket ticket; + check(pure_runtime_accept(&runtime, PURE_INPUT_READ, key, 0, 0, &ticket) == PURE_OK, + "runtime read admission failed"); + PureOutcome result = completed(ticket); + check(result.status == PURE_OK && result.revision == revision && + result.body.size == BODY_BYTES && !memcmp(result.body.data, expected, BODY_BYTES), + "runtime read mismatch"); + hash_body(result.body.data, result.body.size, revision); + check(pure_runtime_release(ticket) && !runtime.active && !runtime.retained, + "runtime read resource not released"); +} + +static void create(const char *store, const char *key, unsigned document, + const unsigned char *expected) { + (void)store; + write_checked(key, 0, 0, 0, BODY_BYTES, document, 0, expected, 1); +} + +static void edit_last(const char *store, const char *key, unsigned char replacement) { + (void)store; + write_checked(key, 1, BODY_BYTES - 1, 1, 1, 0, replacement, &replacement, 2); +} +#endif + +int main(int argc, char **argv) { + if (argc != 3) fail("usage: document-path STORE DOCUMENT_COUNT"); + char *end = NULL; + unsigned long parsed = strtoul(argv[2], &end, 10); + check(end && !*end && parsed > 0 && parsed <= 4096, + "DOCUMENT_COUNT must be 1..4096"); + unsigned documents = (unsigned)parsed; + check(pure_store_init(argv[1]) == PURE_OK, "store initialization failed"); + unsigned char *expected = malloc(BODY_BYTES); + check(expected != NULL, "expected document allocation failed"); +#ifndef DIRECT_PATH + check(pure_embedded_program.splice && pure_embedded_program.write.offset == 8 && + pure_embedded_program.splice_offset.value == 0 && + pure_embedded_program.splice_remove.value == 4 && + pure_embedded_program.reply_input && pure_embedded_program.read.to_end, + "prepared editor.pure no longer matches direct baseline"); + check(pure_runtime_init(&runtime, work, 1, &pure_embedded_program, + pure_store_host(argv[1]), 4u * 1024u * 1024u), + "runtime initialization failed"); +#endif + uint64_t start = nanoseconds(); + uint64_t stages[4] = {0}; + for (unsigned document = 0; document < documents; ++document) { + char key[16]; + snprintf(key, sizeof(key), "doc%04u", document); + pattern(expected, document); + uint64_t before = nanoseconds(); + create(argv[1], key, document, expected); + stages[0] += nanoseconds() - before; + before = nanoseconds(); + read_checked(argv[1], key, expected, 1); + stages[1] += nanoseconds() - before; + unsigned char replacement = (unsigned char)('A' + document % 26); + before = nanoseconds(); + edit_last(argv[1], key, replacement); + stages[2] += nanoseconds() - before; + expected[BODY_BYTES - 1] = replacement; + before = nanoseconds(); + read_checked(argv[1], key, expected, 2); + stages[3] += nanoseconds() - before; + } + uint64_t elapsed = nanoseconds() - start; + free(expected); + printf("{\"variant\":\"%s\",\"documents\":%u,\"operations\":%u," + "\"body_bytes\":%u,\"elapsed_ns\":%" PRIu64 "," + "\"stage_ns\":[%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 "]," + "\"digest\":\"%016" PRIx64 "\"}\n", +#ifdef DIRECT_PATH + "direct", +#else + "pure", +#endif + documents, documents * 4, (unsigned)BODY_BYTES, elapsed, + stages[0], stages[1], stages[2], stages[3], digest); + return 0; +} diff --git a/research/experiments/004-direct-document-path/peak-launcher.c b/research/experiments/004-direct-document-path/peak-launcher.c new file mode 100644 index 0000000..e9f6c00 --- /dev/null +++ b/research/experiments/004-direct-document-path/peak-launcher.c @@ -0,0 +1,26 @@ +#define _GNU_SOURCE +/* Fork from a small native launcher so a Python orchestrator's pre-exec RSS + * cannot become the measured worker's high-water mark. */ +#include +#include +#include +#include + +int main(int argc, char **argv) { + if (argc < 2) return 2; + pid_t child = fork(); + if (child < 0) return 2; + if (!child) { + execvp(argv[1], argv + 1); + _exit(127); + } + struct rusage usage; + int status; + if (wait4(child, &status, 0, &usage) < 0 || !WIFEXITED(status) || + WEXITSTATUS(status)) return 1; + long user_ns = usage.ru_utime.tv_sec * 1000000000L + usage.ru_utime.tv_usec * 1000L; + long system_ns = usage.ru_stime.tv_sec * 1000000000L + usage.ru_stime.tv_usec * 1000L; + fprintf(stderr, "PURE_PEAK_RSS_KIB=%ld\nPURE_USER_NS=%ld\nPURE_SYSTEM_NS=%ld\n", + usage.ru_maxrss, user_ns, system_ns); + return 0; +} diff --git a/research/experiments/004-direct-document-path/results/2026-09-24-ext4-1m-linux-aarch64.json b/research/experiments/004-direct-document-path/results/2026-09-24-ext4-1m-linux-aarch64.json new file mode 100644 index 0000000..80ae293 --- /dev/null +++ b/research/experiments/004-direct-document-path/results/2026-09-24-ext4-1m-linux-aarch64.json @@ -0,0 +1,251 @@ +{ + "profile": "same-durable-document-app-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "prepared_sha256": "8e93861539a87c2a091f55afea1bf310fc3d1f1474521d8f8318bfacac3b0649", + "documents_per_variant": 32, + "operations_per_variant": 128, + "body_bytes": 1048576, + "pairs": 4, + "filesystem": { + "device": "/dev/mmcblk0p2", + "type": "ext4", + "mount": "/home/eunhhu/work/pure" + }, + "sections": { + "direct": { + "text": 10364, + "data": 856, + "bss": 16 + }, + "pure": { + "text": 18640, + "data": 864, + "bss": 376 + } + }, + "binary_sha256": { + "direct": "1389e7d95e74e93ab5d9e8c8146d0b8fc5288e7a356df231c3c018d3db9d0e05", + "pure": "9a45a3d92eed499b849560a2c949f24da7d09deb5ec9bd956ab78ae4e8273e54" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 4764012642, + "stage_ns": [ + 2307602623, + 136599863, + 2096570951, + 135696119 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4128, + "user_ns": 428385000, + "system_ns": 98253000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + }, + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 7285595814, + "stage_ns": [ + 5142956601, + 117263079, + 1831606853, + 117247619 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4128, + "user_ns": 382221000, + "system_ns": 78088000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 3766595455, + "stage_ns": [ + 1765437679, + 95183882, + 1750125337, + 95840266 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4176, + "user_ns": 290416000, + "system_ns": 85416000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + }, + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 8005833380, + "stage_ns": [ + 2106116647, + 136885177, + 5531253084, + 139444468 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4160, + "user_ns": 454253000, + "system_ns": 79693000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 10918834225, + "stage_ns": [ + 2175551809, + 106558403, + 8462573310, + 106534958 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4576, + "user_ns": 344275000, + "system_ns": 70420000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + }, + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 12394010470, + "stage_ns": [ + 7847769422, + 133123427, + 4192297149, + 133678298 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4128, + "user_ns": 443388000, + "system_ns": 78475000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 19998681944, + "stage_ns": [ + 6699663505, + 105108979, + 13020834566, + 105960162 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4144, + "user_ns": 361242000, + "system_ns": 54606000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + }, + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 9851837474, + "stage_ns": [ + 6446916975, + 136390388, + 3043885903, + 137363089 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4592, + "user_ns": 460200000, + "system_ns": 68626000, + "stored_file_bytes": 33555200, + "stored_files_sha256": "cd78e6871643d5ce1ae9f3b0c96985b36996ac23053f49c140c4224516d6e77a" + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 8928.835427, + "peak_rss_kib": 4368.0, + "user_ms": 441.319, + "system_ms": 75.0565, + "stages_ms": [ + 2241.577216, + 136.4951255, + 4287.5694935, + 136.529604 + ] + }, + "pure": { + "elapsed_ms": 9839.803142, + "peak_rss_kib": 4136.0, + "user_ms": 371.7315, + "system_ms": 78.2815, + "stages_ms": [ + 5921.310053, + 111.186029, + 3011.952001, + 111.6038905 + ] + } + }, + "median_elapsed_ratio_pure_over_direct": 1.102025367411893, + "sources_sha256": { + "research/experiments/004-direct-document-path/document-path.c": "032d8688db330c355082d150865aa7ade3e5402cc1bbe090b361fd55cb6325fc", + "research/experiments/004-direct-document-path/peak-launcher.c": "fc730205d564b60a4f9e44aa1b880df0debeb1338b86b27eabe3bde1508f3f74", + "research/experiments/004-direct-document-path/run.py": "ab9eaed05ed47afc29cb94e72759dad6534531b52b34a8ba2c5b3be6ded1cac1", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/examples/editor.pure": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a" + }, + "environment": { + "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41", + "machine": "aarch64", + "compiler": "cc (Debian 14.2.0-19) 14.2.0", + "size": "GNU size (GNU Binutils for Debian) 2.44" + } +} diff --git a/research/experiments/004-direct-document-path/results/2026-09-24-ext4-8k-linux-aarch64.json b/research/experiments/004-direct-document-path/results/2026-09-24-ext4-8k-linux-aarch64.json new file mode 100644 index 0000000..2f80581 --- /dev/null +++ b/research/experiments/004-direct-document-path/results/2026-09-24-ext4-8k-linux-aarch64.json @@ -0,0 +1,251 @@ +{ + "profile": "same-durable-document-app-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "prepared_sha256": "8e93861539a87c2a091f55afea1bf310fc3d1f1474521d8f8318bfacac3b0649", + "documents_per_variant": 256, + "operations_per_variant": 1024, + "body_bytes": 8192, + "pairs": 4, + "filesystem": { + "device": "/dev/mmcblk0p2", + "type": "ext4", + "mount": "/home/eunhhu/work/pure" + }, + "sections": { + "direct": { + "text": 10364, + "data": 856, + "bss": 16 + }, + "pure": { + "text": 18640, + "data": 864, + "bss": 376 + } + }, + "binary_sha256": { + "direct": "d265f60f9126da701369c26a6263474164171349867e11079a63b6d9bd1dbbc8", + "pure": "8e71e09bb7923de0e945f4d51c5514dd9ab07a4ff9c0982db712ab895fc54fa0" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 7241112354, + "stage_ns": [ + 3772530510, + 14877249, + 3434303263, + 14649999 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1072, + "user_ns": 21367000, + "system_ns": 89743000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + }, + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 7649396059, + "stage_ns": [ + 3825503100, + 15159869, + 3788264822, + 15324464 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1088, + "user_ns": 40394000, + "system_ns": 72710000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 7398701348, + "stage_ns": [ + 3647571585, + 15698014, + 3714469244, + 15678618 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1088, + "user_ns": 32058000, + "system_ns": 85489000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + }, + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 7807026262, + "stage_ns": [ + 4021085986, + 16197799, + 3747754749, + 16384614 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1072, + "user_ns": 23055000, + "system_ns": 96831000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 8124328908, + "stage_ns": [ + 3722508954, + 16393229, + 4364924440, + 15319853 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1072, + "user_ns": 45910000, + "system_ns": 67099000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + }, + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 8689792618, + "stage_ns": [ + 3995126011, + 17471391, + 4653440218, + 17835468 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1088, + "user_ns": 47096000, + "system_ns": 83726000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 7553568836, + "stage_ns": [ + 3612364797, + 15354345, + 3905074609, + 15629392 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1088, + "user_ns": 23794000, + "system_ns": 90418000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + }, + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 7932443534, + "stage_ns": [ + 4056315266, + 15670869, + 3839621816, + 15698993 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1072, + "user_ns": 23088000, + "system_ns": 92353000, + "stored_file_bytes": 2103296, + "stored_files_sha256": "93b837ad585f9633f60c4e3fc6c52e403238e5a4cc4ec3f6d248b3981a8f425b" + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 7869.734898, + "peak_rss_kib": 1072.0, + "user_ms": 23.0715, + "system_ms": 91.048, + "stages_ms": [ + 3896.808248, + 15.934334, + 3793.6882825, + 15.509423 + ] + }, + "pure": { + "elapsed_ms": 7601.4824475, + "peak_rss_kib": 1088.0, + "user_ms": 36.226, + "system_ms": 84.6075, + "stages_ms": [ + 3736.5373425, + 15.5261795, + 3846.6697155, + 15.654005 + ] + } + }, + "median_elapsed_ratio_pure_over_direct": 0.9659134069982239, + "sources_sha256": { + "research/experiments/004-direct-document-path/document-path.c": "032d8688db330c355082d150865aa7ade3e5402cc1bbe090b361fd55cb6325fc", + "research/experiments/004-direct-document-path/peak-launcher.c": "fc730205d564b60a4f9e44aa1b880df0debeb1338b86b27eabe3bde1508f3f74", + "research/experiments/004-direct-document-path/run.py": "ab9eaed05ed47afc29cb94e72759dad6534531b52b34a8ba2c5b3be6ded1cac1", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/examples/editor.pure": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a" + }, + "environment": { + "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41", + "machine": "aarch64", + "compiler": "cc (Debian 14.2.0-19) 14.2.0", + "size": "GNU size (GNU Binutils for Debian) 2.44" + } +} diff --git a/research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-1m-preliminary-linux-aarch64.json b/research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-1m-preliminary-linux-aarch64.json new file mode 100644 index 0000000..ab088e1 --- /dev/null +++ b/research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-1m-preliminary-linux-aarch64.json @@ -0,0 +1,238 @@ +{ + "profile": "same-durable-document-app-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "prepared_sha256": "8e93861539a87c2a091f55afea1bf310fc3d1f1474521d8f8318bfacac3b0649", + "documents_per_variant": 32, + "operations_per_variant": 128, + "body_bytes": 1048576, + "pairs": 4, + "sections": { + "direct": { + "text": 10364, + "data": 856, + "bss": 16 + }, + "pure": { + "text": 18640, + "data": 864, + "bss": 376 + } + }, + "binary_sha256": { + "direct": "1389e7d95e74e93ab5d9e8c8146d0b8fc5288e7a356df231c3c018d3db9d0e05", + "pure": "9a45a3d92eed499b849560a2c949f24da7d09deb5ec9bd956ab78ae4e8273e54" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 340081183, + "stage_ns": [ + 75261818, + 94500701, + 17506146, + 93772922 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 3872, + "user_ns": 308146000, + "system_ns": 32015000, + "stored_file_bytes": 33555200 + }, + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 341099421, + "stage_ns": [ + 79567567, + 92146853, + 17844589, + 92596187 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4336, + "user_ns": 305409000, + "system_ns": 36166000, + "stored_file_bytes": 33555200 + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 338271706, + "stage_ns": [ + 77352220, + 91696577, + 18131571, + 92186762 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 3872, + "user_ns": 310861000, + "system_ns": 27897000, + "stored_file_bytes": 33555200 + }, + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 348018886, + "stage_ns": [ + 79216590, + 98943059, + 17171958, + 93268609 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 3856, + "user_ns": 302866000, + "system_ns": 40382000, + "stored_file_bytes": 33555200 + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 337466024, + "stage_ns": [ + 74499100, + 93470034, + 17836179, + 92757726 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 3872, + "user_ns": 309997000, + "system_ns": 28181000, + "stored_file_bytes": 33555200 + }, + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 342399605, + "stage_ns": [ + 79958272, + 92458203, + 18037399, + 92971374 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 4336, + "user_ns": 311005000, + "system_ns": 31898000, + "stored_file_bytes": 33555200 + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 340585627, + "stage_ns": [ + 79565343, + 91822001, + 17952418, + 92354743 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 3872, + "user_ns": 301411000, + "system_ns": 39659000, + "stored_file_bytes": 33555200 + }, + "direct": { + "variant": "direct", + "documents": 32, + "operations": 128, + "body_bytes": 1048576, + "elapsed_ns": 336890525, + "stage_ns": [ + 74695433, + 93087459, + 17564345, + 92612242 + ], + "digest": "b74d25a65553ee41", + "peak_rss_kib": 3856, + "user_ns": 309291000, + "system_ns": 28117000, + "stored_file_bytes": 33555200 + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 338.7736035, + "peak_rss_kib": 3864.0, + "user_ms": 308.7185, + "system_ms": 30.098, + "stages_ms": [ + 74.9786255, + 93.9853675, + 17.5352455, + 93.0131675 + ] + }, + "pure": { + "elapsed_ms": 340.842524, + "peak_rss_kib": 4104.0, + "user_ms": 308.135, + "system_ms": 34.032, + "stages_ms": [ + 79.566455, + 91.984427, + 17.9949085, + 92.475465 + ] + } + }, + "median_elapsed_ratio_pure_over_direct": 1.0061070888600092, + "sources_sha256": { + "research/experiments/004-direct-document-path/document-path.c": "032d8688db330c355082d150865aa7ade3e5402cc1bbe090b361fd55cb6325fc", + "research/experiments/004-direct-document-path/peak-launcher.c": "fc730205d564b60a4f9e44aa1b880df0debeb1338b86b27eabe3bde1508f3f74", + "research/experiments/004-direct-document-path/run.py": "1919287fb5dc0f884cecf038c21e0d4326f2e826ee7a48eb65f5d480e27bacb7", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/examples/editor.pure": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a" + }, + "environment": { + "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41", + "machine": "aarch64", + "compiler": "cc (Debian 14.2.0-19) 14.2.0", + "size": "GNU size (GNU Binutils for Debian) 2.44" + } +} diff --git a/research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-8k-preliminary-linux-aarch64.json b/research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-8k-preliminary-linux-aarch64.json new file mode 100644 index 0000000..e8a9f5b --- /dev/null +++ b/research/experiments/004-direct-document-path/results/2026-09-24-tmpfs-8k-preliminary-linux-aarch64.json @@ -0,0 +1,322 @@ +{ + "profile": "same-durable-document-app-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "prepared_sha256": "8e93861539a87c2a091f55afea1bf310fc3d1f1474521d8f8318bfacac3b0649", + "documents_per_variant": 256, + "operations_per_variant": 1024, + "body_bytes": 8192, + "pairs": 6, + "sections": { + "direct": { + "text": 10364, + "data": 856, + "bss": 16 + }, + "pure": { + "text": 18640, + "data": 864, + "bss": 376 + } + }, + "binary_sha256": { + "direct": "d265f60f9126da701369c26a6263474164171349867e11079a63b6d9bd1dbbc8", + "pure": "8e71e09bb7923de0e945f4d51c5514dd9ab07a4ff9c0982db712ab895fc54fa0" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32443218, + "stage_ns": [ + 8080321, + 7513546, + 5301851, + 7787666 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 24559000, + "system_ns": 8186000, + "stored_file_bytes": 2103296 + }, + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32009201, + "stage_ns": [ + 8131949, + 7162898, + 5457529, + 7487020 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 16183000, + "system_ns": 16183000, + "stored_file_bytes": 2103296 + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 31919941, + "stage_ns": [ + 8166312, + 7144299, + 5410416, + 7352779 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1040, + "user_ns": 20228000, + "system_ns": 12137000, + "stored_file_bytes": 2103296 + }, + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32205996, + "stage_ns": [ + 7984983, + 7590856, + 5294584, + 7570908 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1040, + "user_ns": 16322000, + "system_ns": 16322000, + "stored_file_bytes": 2103296 + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32589218, + "stage_ns": [ + 8207096, + 7581051, + 5347608, + 7656447 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 16529000, + "system_ns": 16529000, + "stored_file_bytes": 2103296 + }, + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 31973552, + "stage_ns": [ + 8280075, + 7151887, + 5368944, + 7359984 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 24311000, + "system_ns": 8103000, + "stored_file_bytes": 2103296 + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32080256, + "stage_ns": [ + 8156569, + 7173028, + 5460655, + 7396332 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 28408000, + "system_ns": 4058000, + "stored_file_bytes": 2103296 + }, + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32571829, + "stage_ns": [ + 8142638, + 7715426, + 5329257, + 7625344 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1040, + "user_ns": 12350000, + "system_ns": 20584000, + "stored_file_bytes": 2103296 + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32290107, + "stage_ns": [ + 8045372, + 7543971, + 5349434, + 7600138 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 20455000, + "system_ns": 12273000, + "stored_file_bytes": 2103296 + }, + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32067756, + "stage_ns": [ + 8225774, + 7252055, + 5472276, + 7359265 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 4064000, + "system_ns": 28454000, + "stored_file_bytes": 2103296 + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "variant": "pure", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32372144, + "stage_ns": [ + 8141941, + 7171001, + 5629064, + 7673781 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 21685000, + "system_ns": 10842000, + "stored_file_bytes": 2103296 + }, + "direct": { + "variant": "direct", + "documents": 256, + "operations": 1024, + "body_bytes": 8192, + "elapsed_ns": 32314570, + "stage_ns": [ + 8063229, + 7512835, + 5380849, + 7585506 + ], + "digest": "b8a08a9615b2eb3b", + "peak_rss_kib": 1056, + "user_ns": 16375000, + "system_ns": 16375000, + "stored_file_bytes": 2103296 + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 32.378894, + "peak_rss_kib": 1056.0, + "user_ms": 16.452, + "system_ms": 16.3485, + "stages_ms": [ + 8.071775, + 7.562511, + 5.3384325, + 7.612741 + ] + }, + "pure": { + "elapsed_ms": 32.0384785, + "peak_rss_kib": 1056.0, + "user_ms": 20.9565, + "system_ms": 11.4895, + "stages_ms": [ + 8.1614405, + 7.1669495, + 5.459092, + 7.378158 + ] + } + }, + "median_elapsed_ratio_pure_over_direct": 0.9894865000638995, + "sources_sha256": { + "research/experiments/004-direct-document-path/document-path.c": "032d8688db330c355082d150865aa7ade3e5402cc1bbe090b361fd55cb6325fc", + "research/experiments/004-direct-document-path/peak-launcher.c": "fc730205d564b60a4f9e44aa1b880df0debeb1338b86b27eabe3bde1508f3f74", + "research/experiments/004-direct-document-path/run.py": "1919287fb5dc0f884cecf038c21e0d4326f2e826ee7a48eb65f5d480e27bacb7", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/examples/editor.pure": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a" + }, + "environment": { + "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41", + "machine": "aarch64", + "compiler": "cc (Debian 14.2.0-19) 14.2.0", + "size": "GNU size (GNU Binutils for Debian) 2.44" + } +} diff --git a/research/experiments/004-direct-document-path/run.py b/research/experiments/004-direct-document-path/run.py new file mode 100644 index 0000000..4987fd7 --- /dev/null +++ b/research/experiments/004-direct-document-path/run.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +"""Compare the same durable document job through Pure and direct store calls.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import platform +import statistics +import subprocess +import tempfile + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[2] +CONNECTIONS = ROOT / 'prototypes/connections' +EDITOR_SHA256 = '8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a' + + +def sha(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def run(command, timeout=180): + result = subprocess.run([str(item) for item in command], capture_output=True, + text=True, timeout=timeout) + if result.returncode: + raise AssertionError((result.args, result.returncode, + result.stdout[-2000:], result.stderr[-2000:])) + return result + + +def compile_all(directory, cc, body_bytes): + flags = [cc, '-O2', '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', + f'-DPURE_BENCH_BODY_BYTES={body_bytes}'] + compiler = directory / 'pure-compile' + run([*flags, CONNECTIONS / 'compile-program.c', CONNECTIONS / 'program.c', + '-o', compiler]) + header = directory / 'embedded-plan.h' + run([compiler, CONNECTIONS / 'examples/editor.pure', header]) + common = [HERE / 'document-path.c', CONNECTIONS / 'store.c'] + direct = directory / 'direct' + pure = directory / 'pure' + run([*flags, '-DDIRECT_PATH', '-I' + str(CONNECTIONS), *common, '-o', direct]) + run([*flags, '-I' + str(CONNECTIONS), '-I' + str(directory), *common, + CONNECTIONS / 'store-port.c', CONNECTIONS / 'runtime.c', + CONNECTIONS / 'evaluate.c', '-o', pure]) + launcher = directory / 'peak-launcher' + run([*flags, HERE / 'peak-launcher.c', '-o', launcher]) + return {'direct': direct, 'pure': pure}, launcher, header + + +def sections(binary): + fields = run(['size', binary]).stdout.splitlines()[1].split() + return {'text': int(fields[0]), 'data': int(fields[1]), 'bss': int(fields[2])} + + +def trial(binary, launcher, store, documents): + result = run([launcher, binary, store, documents]) + measured = json.loads(result.stdout) + resources = dict(line.split('=', 1) for line in result.stderr.splitlines()) + measured['peak_rss_kib'] = int(resources['PURE_PEAK_RSS_KIB']) + measured['user_ns'] = int(resources['PURE_USER_NS']) + measured['system_ns'] = int(resources['PURE_SYSTEM_NS']) + objects = sorted(store.glob('obj-*')) + assert len(objects) == documents, len(objects) + measured['stored_file_bytes'] = sum(path.stat().st_size for path in objects) + final_files = hashlib.sha256() + for path in objects: + content = path.read_bytes() + final_files.update(path.name.encode('ascii')) + final_files.update(len(content).to_bytes(8, 'little')) + final_files.update(content) + measured['stored_files_sha256'] = final_files.hexdigest() + assert measured['documents'] == documents and measured['operations'] == 4 * documents + return measured + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--cc', default=os.environ.get('CC', 'cc')) + parser.add_argument('--documents', type=int, default=256) + parser.add_argument('--body-bytes', type=int, default=8192) + parser.add_argument('--pairs', type=int, default=4) + parser.add_argument('--work-root', type=Path, default=HERE / 'results', + help='filesystem for fresh stores; default is the repository filesystem') + parser.add_argument('--output', type=Path) + args = parser.parse_args() + if (not 1 <= args.documents <= 4096 or not 1 <= args.pairs <= 16 or + not 1 <= args.body_bytes <= 1024 * 1024): + parser.error('documents must be 1..4096, pairs 1..16 and body-bytes 1..1048576') + if args.output and args.output.exists(): + parser.error('evidence file already exists') + if platform.system() != 'Linux': + parser.error('this release RSS apparatus uses Linux wait4 units') + definition = CONNECTIONS / 'examples/editor.pure' + assert sha(definition) == EDITOR_SHA256, 'direct baseline needs review after editor.pure changes' + args.work_root.mkdir(parents=True, exist_ok=True) + filesystem = run(['df', '-T', args.work_root]).stdout.splitlines()[1].split() + with tempfile.TemporaryDirectory(prefix='pure-direct-document-', dir=args.work_root) as name: + root = Path(name) + binaries, launcher, prepared = compile_all(root, args.cc, args.body_bytes) + sizes = {name: sections(binary) for name, binary in binaries.items()} + binaries_sha = {name: sha(binary) for name, binary in binaries.items()} + # One small untimed run per path loads shared code and catches a broken + # comparison before the measured alternating, fresh-store trials. + warmups = {name: trial(binary, launcher, root / ('warm-' + name), 4) + for name, binary in binaries.items()} + assert warmups['direct']['digest'] == warmups['pure']['digest'] + samples = [] + for pair in range(args.pairs): + order = ('direct', 'pure') if pair % 2 == 0 else ('pure', 'direct') + entry = {'order': list(order)} + for variant in order: + entry[variant] = trial(binaries[variant], launcher, + root / f'{pair}-{variant}', args.documents) + assert entry['direct']['digest'] == entry['pure']['digest'] + assert entry['direct']['stored_file_bytes'] == entry['pure']['stored_file_bytes'] + assert entry['direct']['stored_files_sha256'] == entry['pure']['stored_files_sha256'] + samples.append(entry) + prepared_sha = sha(prepared) + medians = {variant: {'elapsed_ms': statistics.median( + sample[variant]['elapsed_ns'] for sample in samples) / 1e6, + 'peak_rss_kib': statistics.median( + sample[variant]['peak_rss_kib'] for sample in samples), + 'user_ms': statistics.median( + sample[variant]['user_ns'] for sample in samples) / 1e6, + 'system_ms': statistics.median( + sample[variant]['system_ns'] for sample in samples) / 1e6, + 'stages_ms': [statistics.median( + sample[variant]['stage_ns'][i] for sample in samples) / 1e6 + for i in range(4)]} + for variant in ('direct', 'pure')} + result = {'profile': 'same-durable-document-app-direct-versus-pure', + 'definition_sha256': sha(definition), 'prepared_sha256': prepared_sha, + 'documents_per_variant': args.documents, + 'operations_per_variant': args.documents * 4, + 'body_bytes': args.body_bytes, 'pairs': args.pairs, + 'filesystem': {'device': filesystem[0], 'type': filesystem[1], + 'mount': filesystem[-1]}, + 'sections': sizes, 'binary_sha256': binaries_sha, + 'samples': samples, 'medians': medians, + 'median_elapsed_ratio_pure_over_direct': + medians['pure']['elapsed_ms'] / medians['direct']['elapsed_ms'], + 'sources_sha256': {str(path.relative_to(ROOT)): sha(path) + for path in [HERE / 'document-path.c', HERE / 'peak-launcher.c', + HERE / 'run.py', CONNECTIONS / 'store.c', + CONNECTIONS / 'runtime.c', CONNECTIONS / 'evaluate.c', + CONNECTIONS / 'store-port.c', definition]}, + 'environment': {'platform': platform.platform(), 'machine': platform.machine(), + 'compiler': run([args.cc, '--version']).stdout.splitlines()[0], + 'size': run(['size', '--version']).stdout.splitlines()[0]}} + body = json.dumps(result, indent=2) + '\n' + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(body) + print(body, end='') + + +if __name__ == '__main__': + main() -- cgit v1.2.3