diff options
Diffstat (limited to 'research/experiments/005-direct-document-peer')
17 files changed, 1151 insertions, 0 deletions
diff --git a/research/experiments/005-direct-document-peer/README.md b/research/experiments/005-direct-document-peer/README.md new file mode 100644 index 0000000..d35ba20 --- /dev/null +++ b/research/experiments/005-direct-document-peer/README.md @@ -0,0 +1,60 @@ +# Authenticated document peer: direct versus Pure + +This experiment uses the real `editor.pure` document app over the same TLS +1.3/TCP transport and `pure-doc/1` wire format. `direct-peer.c` is a +research-only server that parses the same authored program and calls the same +store and evaluator directly. `pure-document-server` is the selected product +server build without its native client commands. The baseline is limited to +the tested synchronous document workflow: it has no Pure work tickets, +admission/lifetime contract, cross-domain forwarding, or cancellation. + +Both servers passed the existing `check_document_peer.py` suite: mutual TLS +identity, ALPN, source identity, external Python and native C clients, version +conflicts, one-byte replacement of a 1 MiB value, fragmented/invalid/truncated +frames, deadlines, and restart. The direct and Pure server builds passed the +same suite under ASan/UBSan. These checks do not establish full feature or +security equivalence under concurrent clients or failure networks. + +To run the selected product checks on Linux AArch64: + +```sh +make -C prototypes/connections check-document-peer +make -C prototypes/connections check-document-server +``` + +The first command checks the existing combined client/server executable; the +second checks the server-only code ceiling and runs the same peer scenarios +with the native C client from the combined executable. Both need permission +to bind a local TCP port. The server-only executable and budget are optional; +the default core and CLI do not link TLS. + +To reproduce the read comparison: + +```sh +python3 research/experiments/005-direct-document-peer/run.py \ + --body-bytes 8192 --reads 1024 --pairs 6 \ + --output research/experiments/005-direct-document-peer/results/local-8k.json +python3 research/experiments/005-direct-document-peer/run.py \ + --body-bytes 1048576 --reads 64 --pairs 4 \ + --output research/experiments/005-direct-document-peer/results/local-1m.json +``` + +The runner compiles both servers from source. Each trial creates and reads a +real stored document, then times repeated full reads on one authenticated, +persistent Python TLS connection. It verifies every response and the final +stored file hash. TLS handshake, certificate creation, file creation, and the +first cache-warming read are outside the timed batch. The server process RSS +is recorded before and after. Fresh stores and alternating order reduce some +order effects but do not control CPU frequency, OpenSSL or Python overhead, +network behavior beyond local loopback, or host scheduling. The JSON files +contain all individual samples, source hashes, compiler and OpenSSL versions. + +The short `before-nodelay` and `after-nodelay` artifacts repeat exactly 32 +authenticated 8 KiB reads through each server, differing only in the common +TLS transport's TCP_NODELAY option. This intervention supports a Nagle/delayed +ACK explanation for the prior ~44 ms per-request stall; no packet trace was +captured, so that exact kernel interaction remains an inference. The longer +post-change runs are for direct-versus-Pure cost, not a claim of Internet or +Kitex/QUIC performance. + +See [the result report](../../../docs/research/055-authenticated-peer-transport-cost.md). diff --git a/research/experiments/005-direct-document-peer/direct-peer-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/direct-peer-parity-linux-aarch64.json new file mode 100644 index 0000000..05c62cc --- /dev/null +++ b/research/experiments/005-direct-document-peer/direct-peer-parity-linux-aarch64.json @@ -0,0 +1,16 @@ +{ + "profile": "selected-authenticated-document-peer", + "checks": [ + "mutual certificate identity, server hostname and ALPN separation", + "program source identity checked before C client sends work", + "persistent external TLS client: create, read, conflict and recovery", + "native peer edit and local CLI use the same authored document and storage", + "fragmented 1 MiB direct ingress, demand-selected reply and one-byte splice", + "native C peer uploads 1 MiB and receives only the requested metadata", + "incomplete, invalid and oversized frames do not publish a change", + "idle authenticated session closes after deadline", + "server restart and native C peer preserve the committed large document" + ], + "server_idle_rss_kib": 7968, + "server_after_large_work_rss_kib": 9712 +} diff --git a/research/experiments/005-direct-document-peer/direct-peer-sanitized-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/direct-peer-sanitized-parity-linux-aarch64.json new file mode 100644 index 0000000..356128d --- /dev/null +++ b/research/experiments/005-direct-document-peer/direct-peer-sanitized-parity-linux-aarch64.json @@ -0,0 +1,16 @@ +{ + "profile": "selected-authenticated-document-peer", + "checks": [ + "mutual certificate identity, server hostname and ALPN separation", + "program source identity checked before C client sends work", + "persistent external TLS client: create, read, conflict and recovery", + "native peer edit and local CLI use the same authored document and storage", + "fragmented 1 MiB direct ingress, demand-selected reply and one-byte splice", + "native C peer uploads 1 MiB and receives only the requested metadata", + "incomplete, invalid and oversized frames do not publish a change", + "idle authenticated session closes after deadline", + "server restart and native C peer preserve the committed large document" + ], + "server_idle_rss_kib": 22528, + "server_after_large_work_rss_kib": 31632 +} diff --git a/research/experiments/005-direct-document-peer/direct-peer.c b/research/experiments/005-direct-document-peer/direct-peer.c new file mode 100644 index 0000000..437672d --- /dev/null +++ b/research/experiments/005-direct-document-peer/direct-peer.c @@ -0,0 +1,207 @@ +#define _POSIX_C_SOURCE 200809L +/* Research-only server. It shares the product peer's TLS transport, wire + * format, program parser/evaluator, and store, but calls the store directly. */ +#include "program.h" +#include "store.h" +#include "../../../prototypes/common/tls-link.h" +#include <openssl/err.h> +#include <openssl/sha.h> +#include <arpa/inet.h> +#include <errno.h> +#include <inttypes.h> +#include <poll.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +enum { REQUEST_BYTES = 14, REPLY_BYTES = 13, HELLO_BYTES = 40, + OP_READ = 1, OP_WRITE = 2, WANT_BODY = 0x80 }; +static const unsigned char alpn[] = {10, 'p','u','r','e','-','d','o','c','/','1'}; +static volatile sig_atomic_t stopping; +static void signal_stop(int signum) { (void)signum; stopping = 1; } + +typedef struct { PureResult status; int64_t revision; PureSpan body; } DirectOutcome; + +static uint32_t get32(const unsigned char *p) { + return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3]; +} +static uint64_t get64(const unsigned char *p) { + uint64_t value = 0; + for (unsigned i = 0; i < 8; ++i) value = (value << 8) | p[i]; + return value; +} +static void put32(unsigned char *p, uint32_t value) { + for (unsigned i = 0; i < 4; ++i) p[i] = (unsigned char)(value >> (24 - 8 * i)); +} +static void put64(unsigned char *p, uint64_t value) { + for (unsigned i = 0; i < 8; ++i) p[i] = (unsigned char)(value >> (56 - 8 * i)); +} +static bool number(const char *text, uint64_t limit, uint64_t *out) { + if (!text || !*text) return false; + uint64_t value = 0; + for (; *text; ++text) { + if (*text < '0' || *text > '9') return false; + unsigned digit = (unsigned)(*text - '0'); + if (digit > limit || value > (limit - digit) / 10) return false; + value = value * 10 + digit; + } + *out = value; + return true; +} +static bool valid_key(const char *key) { + if (!key || !*key) return false; + size_t size = 0; + for (; size <= 64 && key[size]; ++size) { + unsigned char c = (unsigned char)key[size]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '-' || c == '_')) return false; + } + return size <= 64; +} +static bool load_program(const char *path, PureProgram *program, unsigned char digest[32]) { + FILE *file = fopen(path, "rb"); + if (!file) return false; + char source[PURE_PROGRAM_LIMIT + 1]; + size_t length = fread(source, 1, sizeof(source), file); + bool bad = ferror(file) != 0; + if (fclose(file)) bad = true; + char error[160]; + if (bad || !pure_program_parse(source, length, program, error, sizeof(error))) { + if (!bad) fprintf(stderr, "Program: %s\n", error); + return false; + } + return SHA256((const unsigned char *)source, length, digest) != NULL; +} +static bool write_hello(PureLink *link, const unsigned char digest[32], uint64_t deadline) { + unsigned char hello[HELLO_BYTES] = {'P','D','O','C',1,0,0,0}; + memcpy(hello + 8, digest, 32); + return pure_link_write(link, hello, sizeof(hello), deadline); +} +static bool send_result(PureLink *link, DirectOutcome outcome, bool want_body, uint64_t deadline) { + if (outcome.status > PURE_ALLOCATION || outcome.revision < 0 || + outcome.body.size > PURE_OBJECT_LIMIT || (outcome.body.size && !outcome.body.data)) return false; + unsigned char header[REPLY_BYTES]; + header[0] = (unsigned char)outcome.status; + put64(header + 1, (uint64_t)outcome.revision); + put32(header + 9, (uint32_t)outcome.body.size); + return pure_link_write(link, header, sizeof(header), deadline) && + (!want_body || pure_link_write(link, outcome.body.data, outcome.body.size, deadline)); +} +static bool serve_request(PureLink *link, const PureProgram *program, const char *store, int *end) { + uint64_t deadline = pure_link_now_ms() + PURE_LINK_DEADLINE_MS; + unsigned char header[REQUEST_BYTES]; + int got = pure_link_read(link, header, sizeof(header), deadline); + if (!got) { *end = 1; return true; } + if (got != 1) return false; + unsigned op = header[0] & ~WANT_BODY, key_size = header[1]; + bool want_body = (header[0] & WANT_BODY) != 0; + uint64_t expected = get64(header + 2); + uint32_t body_size = get32(header + 10); + if ((op != OP_READ && op != OP_WRITE) || !key_size || key_size > 64 || + expected > INT64_MAX || body_size > PURE_OBJECT_LIMIT || + (op == OP_READ && (expected || body_size))) return false; + char key[65]; + if (pure_link_read(link, key, key_size, deadline) != 1) return false; + if (memchr(key, 0, key_size)) return false; + key[key_size] = 0; + if (!valid_key(key)) { + (void)send_result(link, (DirectOutcome){.status = PURE_INVALID}, want_body, deadline); + return false; + } + DirectOutcome outcome = {0}; + if (op == OP_READ) { + PureStoredView view = {0}; + outcome.status = pure_store_select(store, key, program->read.offset, + program->read.length, program->read.to_end, program->read_required, + 4u * 1024u * 1024u, &view); + if (outcome.status == PURE_OK) { + outcome.body = view.body; + outcome.revision = view.revision; + } + bool sent = send_result(link, outcome, want_body, deadline); + pure_store_release(&view); + return sent; + } + unsigned char *input = body_size ? malloc(body_size) : NULL; + if (body_size && !input) { + (void)send_result(link, (DirectOutcome){.status = PURE_ALLOCATION}, want_body, deadline); + return false; /* Body deliberately not consumed after rejected admission. */ + } + if (body_size && pure_link_read(link, input, body_size, deadline) != 1) { + free(input); + return false; + } + PureSpan value = {0}, reply = {0}, source = {input, body_size}; + size_t offset = 0, remove = 0; + if (!pure_program_input(program, source, &value, &reply) || + (program->splice && !pure_program_splice(program, source, &offset, &remove))) + outcome.status = PURE_LIMIT; + else if (program->splice) + outcome.status = pure_store_splice(store, key, (int64_t)expected, + offset, remove, value, &outcome.revision); + else outcome.status = pure_store_put(store, key, (int64_t)expected, value, + &outcome.revision); + if (outcome.status == PURE_OK) outcome.body = reply; + bool sent = send_result(link, outcome, want_body, deadline); + free(input); + return sent; +} +static bool serve_connection(PureLink *link, const PureProgram *program, const char *store, + const unsigned char digest[32]) { + if (!write_hello(link, digest, pure_link_now_ms() + PURE_LINK_DEADLINE_MS)) return false; + int end = 0; + while (!end) if (!serve_request(link, program, store, &end)) return false; + return true; +} +static int serve(const char *bind_address, const char *port, const char *program_path, + const char *store, const char *ca, const char *cert, + const char *key, const char *client_name, unsigned max_connections) { + if (!pure_link_port_number(port, true) || !client_name || !*client_name) return 2; + PureProgram program; + unsigned char digest[32]; + if (!load_program(program_path, &program, digest) || pure_store_init(store) != PURE_OK) return 2; + SSL_CTX *ctx = pure_link_context(true, ca, cert, key, alpn); + if (!ctx) { ERR_print_errors_fp(stderr); return 2; } + int listener = pure_link_listen_at(bind_address, port); + if (listener < 0) { perror("listen"); SSL_CTX_free(ctx); return 2; } + struct sockaddr_storage address; + socklen_t address_size = sizeof(address); + if (getsockname(listener, (struct sockaddr *)&address, &address_size)) abort(); + unsigned actual_port = address.ss_family == AF_INET + ? ntohs(((struct sockaddr_in *)&address)->sin_port) + : ntohs(((struct sockaddr_in6 *)&address)->sin6_port); + printf("READY %u\n", actual_port); fflush(stdout); + signal(SIGTERM, signal_stop); signal(SIGINT, signal_stop); + unsigned accepted = 0; + while (!stopping && (!max_connections || accepted < max_connections)) { + struct pollfd p = {.fd = listener, .events = POLLIN}; + int result = poll(&p, 1, 1000); + if (result < 0) { if (errno == EINTR) continue; break; } + if (!result) continue; + int fd = accept(listener, NULL, NULL); + if (fd < 0) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue; break; } + ++accepted; + PureLink link = {.fd = fd}; + bool healthy = pure_link_nonblocking(fd) && + pure_link_handshake(&link, ctx, true, client_name, alpn); + if (healthy) healthy = serve_connection(&link, &program, store, digest); + pure_link_close(&link, healthy); + } + close(listener); + SSL_CTX_free(ctx); + return 0; +} +int main(int argc, char **argv) { + signal(SIGPIPE, SIG_IGN); + if ((argc == 10 || argc == 11) && !strcmp(argv[1], "serve")) { + uint64_t limit = 0; + if (argc == 11 && (!number(argv[10], UINT32_MAX, &limit) || !limit)) return 2; + return serve(argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], + (unsigned)limit); + } + fputs("direct-peer serve BIND_IP PORT PROGRAM STORE_DIR CA CERT KEY CLIENT_DNS [MAX_CONNECTIONS]\n", stderr); + return 2; +} diff --git a/research/experiments/005-direct-document-peer/pure-peer-after-nodelay-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/pure-peer-after-nodelay-parity-linux-aarch64.json new file mode 100644 index 0000000..45598a9 --- /dev/null +++ b/research/experiments/005-direct-document-peer/pure-peer-after-nodelay-parity-linux-aarch64.json @@ -0,0 +1,16 @@ +{ + "profile": "selected-authenticated-document-peer", + "checks": [ + "mutual certificate identity, server hostname and ALPN separation", + "program source identity checked before C client sends work", + "persistent external TLS client: create, read, conflict and recovery", + "native peer edit and local CLI use the same authored document and storage", + "fragmented 1 MiB direct ingress, demand-selected reply and one-byte splice", + "native C peer uploads 1 MiB and receives only the requested metadata", + "incomplete, invalid and oversized frames do not publish a change", + "idle authenticated session closes after deadline", + "server restart and native C peer preserve the committed large document" + ], + "server_idle_rss_kib": 7952, + "server_after_large_work_rss_kib": 9696 +} diff --git a/research/experiments/005-direct-document-peer/pure-peer-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/pure-peer-parity-linux-aarch64.json new file mode 100644 index 0000000..05c62cc --- /dev/null +++ b/research/experiments/005-direct-document-peer/pure-peer-parity-linux-aarch64.json @@ -0,0 +1,16 @@ +{ + "profile": "selected-authenticated-document-peer", + "checks": [ + "mutual certificate identity, server hostname and ALPN separation", + "program source identity checked before C client sends work", + "persistent external TLS client: create, read, conflict and recovery", + "native peer edit and local CLI use the same authored document and storage", + "fragmented 1 MiB direct ingress, demand-selected reply and one-byte splice", + "native C peer uploads 1 MiB and receives only the requested metadata", + "incomplete, invalid and oversized frames do not publish a change", + "idle authenticated session closes after deadline", + "server restart and native C peer preserve the committed large document" + ], + "server_idle_rss_kib": 7968, + "server_after_large_work_rss_kib": 9712 +} diff --git a/research/experiments/005-direct-document-peer/pure-server-after-nodelay-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/pure-server-after-nodelay-parity-linux-aarch64.json new file mode 100644 index 0000000..05c62cc --- /dev/null +++ b/research/experiments/005-direct-document-peer/pure-server-after-nodelay-parity-linux-aarch64.json @@ -0,0 +1,16 @@ +{ + "profile": "selected-authenticated-document-peer", + "checks": [ + "mutual certificate identity, server hostname and ALPN separation", + "program source identity checked before C client sends work", + "persistent external TLS client: create, read, conflict and recovery", + "native peer edit and local CLI use the same authored document and storage", + "fragmented 1 MiB direct ingress, demand-selected reply and one-byte splice", + "native C peer uploads 1 MiB and receives only the requested metadata", + "incomplete, invalid and oversized frames do not publish a change", + "idle authenticated session closes after deadline", + "server restart and native C peer preserve the committed large document" + ], + "server_idle_rss_kib": 7968, + "server_after_large_work_rss_kib": 9712 +} diff --git a/research/experiments/005-direct-document-peer/pure-server-after-nodelay-profile-linux-aarch64.json b/research/experiments/005-direct-document-peer/pure-server-after-nodelay-profile-linux-aarch64.json new file mode 100644 index 0000000..0f6a3ce --- /dev/null +++ b/research/experiments/005-direct-document-peer/pure-server-after-nodelay-profile-linux-aarch64.json @@ -0,0 +1,23 @@ +{ + "passed": true, + "failures": [], + "profile": "linux-aarch64-gcc14-selected-document-server", + "scope": "Reviewed server-only TLS peer ceiling on Linux AArch64/GCC 14. Excludes linked native client code and shared OpenSSL/libc implementations; process RSS is measured separately.", + "budget_sha256": "deae8827323061543193779ada90d1bca4db3a6dda80293e4ad28a9526be9819", + "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41", + "binaries": { + "pure-document-server": { + "sections": { + "text": 43462, + "data": 1352, + "bss": 16 + }, + "needed": [ + "libc.so.6", + "libcrypto.so.3", + "libssl.so.3" + ], + "sha256": "728025cfbdbcdb6d212a2ec8297a0740967653c77ae8908d923c59ddd4ea0315" + } + } +} diff --git a/research/experiments/005-direct-document-peer/pure-server-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/pure-server-parity-linux-aarch64.json new file mode 100644 index 0000000..05c62cc --- /dev/null +++ b/research/experiments/005-direct-document-peer/pure-server-parity-linux-aarch64.json @@ -0,0 +1,16 @@ +{ + "profile": "selected-authenticated-document-peer", + "checks": [ + "mutual certificate identity, server hostname and ALPN separation", + "program source identity checked before C client sends work", + "persistent external TLS client: create, read, conflict and recovery", + "native peer edit and local CLI use the same authored document and storage", + "fragmented 1 MiB direct ingress, demand-selected reply and one-byte splice", + "native C peer uploads 1 MiB and receives only the requested metadata", + "incomplete, invalid and oversized frames do not publish a change", + "idle authenticated session closes after deadline", + "server restart and native C peer preserve the committed large document" + ], + "server_idle_rss_kib": 7968, + "server_after_large_work_rss_kib": 9712 +} diff --git a/research/experiments/005-direct-document-peer/pure-server-profile-linux-aarch64.json b/research/experiments/005-direct-document-peer/pure-server-profile-linux-aarch64.json new file mode 100644 index 0000000..c74cdf9 --- /dev/null +++ b/research/experiments/005-direct-document-peer/pure-server-profile-linux-aarch64.json @@ -0,0 +1,23 @@ +{ + "passed": true, + "failures": [], + "profile": "linux-aarch64-gcc14-selected-document-server", + "scope": "Reviewed server-only TLS peer ceiling on Linux AArch64/GCC 14. Excludes linked native client code and shared OpenSSL/libc implementations; process RSS is measured separately.", + "budget_sha256": "deae8827323061543193779ada90d1bca4db3a6dda80293e4ad28a9526be9819", + "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41", + "binaries": { + "pure-document-server": { + "sections": { + "text": 43462, + "data": 1352, + "bss": 16 + }, + "needed": [ + "libc.so.6", + "libcrypto.so.3", + "libssl.so.3" + ], + "sha256": "ea88a146270cea96faf976b548ceba7179c535042ec3a8656e92c3596372f9aa" + } + } +} diff --git a/research/experiments/005-direct-document-peer/pure-server-sanitized-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/pure-server-sanitized-parity-linux-aarch64.json new file mode 100644 index 0000000..161dd4f --- /dev/null +++ b/research/experiments/005-direct-document-peer/pure-server-sanitized-parity-linux-aarch64.json @@ -0,0 +1,16 @@ +{ + "profile": "selected-authenticated-document-peer", + "checks": [ + "mutual certificate identity, server hostname and ALPN separation", + "program source identity checked before C client sends work", + "persistent external TLS client: create, read, conflict and recovery", + "native peer edit and local CLI use the same authored document and storage", + "fragmented 1 MiB direct ingress, demand-selected reply and one-byte splice", + "native C peer uploads 1 MiB and receives only the requested metadata", + "incomplete, invalid and oversized frames do not publish a change", + "idle authenticated session closes after deadline", + "server restart and native C peer preserve the committed large document" + ], + "server_idle_rss_kib": 22640, + "server_after_large_work_rss_kib": 31776 +} diff --git a/research/experiments/005-direct-document-peer/results/2026-09-24-after-nodelay-8k-linux-aarch64.json b/research/experiments/005-direct-document-peer/results/2026-09-24-after-nodelay-8k-linux-aarch64.json new file mode 100644 index 0000000..f739b2b --- /dev/null +++ b/research/experiments/005-direct-document-peer/results/2026-09-24-after-nodelay-8k-linux-aarch64.json @@ -0,0 +1,109 @@ +{ + "profile": "authenticated-document-read-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "body_bytes": 8192, + "reads_per_trial": 32, + "pairs": 2, + "filesystem": { + "device": "/dev/mmcblk0p2", + "type": "ext4", + "mount": "/" + }, + "sections": { + "direct": { + "text": 37862, + "data": 1352, + "bss": 16 + }, + "pure": { + "text": 43454, + "data": 1352, + "bss": 16 + } + }, + "binary_sha256": { + "direct": "9177aebfae07d84af5b6f23fbfe28a3a89a494943b8d0c8d0d35b68593b1f704", + "pure": "be9f569ab444085c761a7f7c9aa91c2acd6581534059785964b3a94da89fff25" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 3028977, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "pure": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 2564107, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 3016236, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "direct": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 3019977, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 3.024477, + "server_rss_before_kib": 8632.0, + "server_rss_after_kib": 8632.0 + }, + "pure": { + "elapsed_ms": 2.7901715, + "server_rss_before_kib": 8640.0, + "server_rss_after_kib": 8640.0 + } + }, + "sources_sha256": { + "research/experiments/005-direct-document-peer/direct-peer.c": "b1ee6ba5ec3d09b842ac42d4ca158b339ec2d23b157d2c568180af2731737501", + "research/experiments/005-direct-document-peer/run.py": "b53539f0a915376397011713ebbc9438a9b289d3b4dd745ffab5d6039fccd958", + "prototypes/connections/document-peer.c": "0c72121a6929239f618f8a642247af13fb5b68cca2999c125e52de685e5b2e50", + "prototypes/connections/check_document_peer.py": "d9dc789148951baa6f5d47f8cdea4009fe4d2dcf67b9dfaf2e6501f8e66a0f5c", + "prototypes/common/tls-link.c": "5d47ce411d6a81ce1a29d288c93ff507539fda1f202f2f41c0ff5560564b4f5a", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/program.c": "d7186634d2bfd83bbaf87ea3bafdec8d14be8f556cbf3d9877d07e0074b4b389", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "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", + "openssl": "OpenSSL 3.6.4 25 Aug 2026 (Library: OpenSSL 3.6.4 25 Aug 2026)" + } +} diff --git a/research/experiments/005-direct-document-peer/results/2026-09-24-before-nodelay-8k-linux-aarch64.json b/research/experiments/005-direct-document-peer/results/2026-09-24-before-nodelay-8k-linux-aarch64.json new file mode 100644 index 0000000..019fd1e --- /dev/null +++ b/research/experiments/005-direct-document-peer/results/2026-09-24-before-nodelay-8k-linux-aarch64.json @@ -0,0 +1,109 @@ +{ + "profile": "authenticated-document-read-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "body_bytes": 8192, + "reads_per_trial": 32, + "pairs": 2, + "filesystem": { + "device": "/dev/mmcblk0p2", + "type": "ext4", + "mount": "/" + }, + "sections": { + "direct": { + "text": 37862, + "data": 1352, + "bss": 16 + }, + "pure": { + "text": 43454, + "data": 1352, + "bss": 16 + } + }, + "binary_sha256": { + "direct": "edce33757c398fb23f6bc9e70bb31072714320324912f21599d23469e8381816", + "pure": "22560da4dd66f91f8b899569cfc771835034d7e07a68dd4da44e1605edb46c45" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 1415860581, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "pure": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 1411832180, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 1411873125, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "direct": { + "reads": 32, + "body_bytes": 8192, + "elapsed_ns": 1407871983, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 1411.866282, + "server_rss_before_kib": 8632.0, + "server_rss_after_kib": 8632.0 + }, + "pure": { + "elapsed_ms": 1411.8526525, + "server_rss_before_kib": 8632.0, + "server_rss_after_kib": 8632.0 + } + }, + "sources_sha256": { + "research/experiments/005-direct-document-peer/direct-peer.c": "b1ee6ba5ec3d09b842ac42d4ca158b339ec2d23b157d2c568180af2731737501", + "research/experiments/005-direct-document-peer/run.py": "b53539f0a915376397011713ebbc9438a9b289d3b4dd745ffab5d6039fccd958", + "prototypes/connections/document-peer.c": "0c72121a6929239f618f8a642247af13fb5b68cca2999c125e52de685e5b2e50", + "prototypes/connections/check_document_peer.py": "d9dc789148951baa6f5d47f8cdea4009fe4d2dcf67b9dfaf2e6501f8e66a0f5c", + "prototypes/common/tls-link.c": "df259073c5e1cd911495b48d4ccd43c8346d955a19a8773964abe58089fbebf9", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/program.c": "d7186634d2bfd83bbaf87ea3bafdec8d14be8f556cbf3d9877d07e0074b4b389", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "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", + "openssl": "OpenSSL 3.6.4 25 Aug 2026 (Library: OpenSSL 3.6.4 25 Aug 2026)" + } +} diff --git a/research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-1m-linux-aarch64.json b/research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-1m-linux-aarch64.json new file mode 100644 index 0000000..9e65005 --- /dev/null +++ b/research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-1m-linux-aarch64.json @@ -0,0 +1,157 @@ +{ + "profile": "authenticated-document-read-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "body_bytes": 1048576, + "reads_per_trial": 64, + "pairs": 4, + "filesystem": { + "device": "/dev/mmcblk0p2", + "type": "ext4", + "mount": "/" + }, + "sections": { + "direct": { + "text": 37862, + "data": 1352, + "bss": 16 + }, + "pure": { + "text": 43454, + "data": 1352, + "bss": 16 + } + }, + "binary_sha256": { + "direct": "9177aebfae07d84af5b6f23fbfe28a3a89a494943b8d0c8d0d35b68593b1f704", + "pure": "be9f569ab444085c761a7f7c9aa91c2acd6581534059785964b3a94da89fff25" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 148966030, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + }, + "pure": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 149719696, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 149739381, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + }, + "direct": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 150962435, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 137503567, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + }, + "pure": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 155644335, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 155630742, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + }, + "direct": { + "reads": 64, + "body_bytes": 1048576, + "elapsed_ns": 155552075, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 1048600, + "stored_file_sha256": "a89fd11658d8139f5cb9723120126df35590f80d02f72fdfa46c1d7c6cf44383" + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 149.9642325, + "server_rss_before_kib": 8632.0, + "server_rss_after_kib": 8632.0 + }, + "pure": { + "elapsed_ms": 152.6850615, + "server_rss_before_kib": 8640.0, + "server_rss_after_kib": 8640.0 + } + }, + "sources_sha256": { + "research/experiments/005-direct-document-peer/direct-peer.c": "b1ee6ba5ec3d09b842ac42d4ca158b339ec2d23b157d2c568180af2731737501", + "research/experiments/005-direct-document-peer/run.py": "b53539f0a915376397011713ebbc9438a9b289d3b4dd745ffab5d6039fccd958", + "prototypes/connections/document-peer.c": "0c72121a6929239f618f8a642247af13fb5b68cca2999c125e52de685e5b2e50", + "prototypes/connections/check_document_peer.py": "d9dc789148951baa6f5d47f8cdea4009fe4d2dcf67b9dfaf2e6501f8e66a0f5c", + "prototypes/common/tls-link.c": "5d47ce411d6a81ce1a29d288c93ff507539fda1f202f2f41c0ff5560564b4f5a", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/program.c": "d7186634d2bfd83bbaf87ea3bafdec8d14be8f556cbf3d9877d07e0074b4b389", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "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", + "openssl": "OpenSSL 3.6.4 25 Aug 2026 (Library: OpenSSL 3.6.4 25 Aug 2026)" + } +} diff --git a/research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-8k-linux-aarch64.json b/research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-8k-linux-aarch64.json new file mode 100644 index 0000000..153842b --- /dev/null +++ b/research/experiments/005-direct-document-peer/results/2026-09-24-nodelay-8k-linux-aarch64.json @@ -0,0 +1,205 @@ +{ + "profile": "authenticated-document-read-direct-versus-pure", + "definition_sha256": "8ca6c191b98bf53a592ce24f2b5cd040e41f05c4ccdda7a072d5d7f79870c06a", + "body_bytes": 8192, + "reads_per_trial": 1024, + "pairs": 6, + "filesystem": { + "device": "/dev/mmcblk0p2", + "type": "ext4", + "mount": "/" + }, + "sections": { + "direct": { + "text": 37862, + "data": 1352, + "bss": 16 + }, + "pure": { + "text": 43454, + "data": 1352, + "bss": 16 + } + }, + "binary_sha256": { + "direct": "9177aebfae07d84af5b6f23fbfe28a3a89a494943b8d0c8d0d35b68593b1f704", + "pure": "be9f569ab444085c761a7f7c9aa91c2acd6581534059785964b3a94da89fff25" + }, + "samples": [ + { + "order": [ + "direct", + "pure" + ], + "direct": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 76563753, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "pure": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 93622467, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 90777415, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "direct": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 92965246, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 76165419, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "pure": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 92132358, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 80493468, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "direct": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 77076696, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + }, + { + "order": [ + "direct", + "pure" + ], + "direct": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 91795043, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "pure": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 90660842, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + }, + { + "order": [ + "pure", + "direct" + ], + "pure": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 93672077, + "server_rss_before_kib": 8640, + "server_rss_after_kib": 8640, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + }, + "direct": { + "reads": 1024, + "body_bytes": 8192, + "elapsed_ns": 116319172, + "server_rss_before_kib": 8624, + "server_rss_after_kib": 8624, + "stored_file_bytes": 8216, + "stored_file_sha256": "286cfc86e3ebe6d82dd923ef777eba30f748e8b1dd58d5c4352babbf85d0d64f" + } + } + ], + "medians": { + "direct": { + "elapsed_ms": 84.4358695, + "server_rss_before_kib": 8640.0, + "server_rss_after_kib": 8640.0 + }, + "pure": { + "elapsed_ms": 91.4548865, + "server_rss_before_kib": 8640.0, + "server_rss_after_kib": 8640.0 + } + }, + "sources_sha256": { + "research/experiments/005-direct-document-peer/direct-peer.c": "b1ee6ba5ec3d09b842ac42d4ca158b339ec2d23b157d2c568180af2731737501", + "research/experiments/005-direct-document-peer/run.py": "b53539f0a915376397011713ebbc9438a9b289d3b4dd745ffab5d6039fccd958", + "prototypes/connections/document-peer.c": "0c72121a6929239f618f8a642247af13fb5b68cca2999c125e52de685e5b2e50", + "prototypes/connections/check_document_peer.py": "d9dc789148951baa6f5d47f8cdea4009fe4d2dcf67b9dfaf2e6501f8e66a0f5c", + "prototypes/common/tls-link.c": "5d47ce411d6a81ce1a29d288c93ff507539fda1f202f2f41c0ff5560564b4f5a", + "prototypes/connections/store.c": "733ddcacaffd9e9ffbadfc385eadd4c9e62736a2b97b7bc048ad14845d83070f", + "prototypes/connections/store-port.c": "d6bf17878b325e039360816edcbf8d68e41b373cf3e6128d7795e11ae21fdd6f", + "prototypes/connections/program.c": "d7186634d2bfd83bbaf87ea3bafdec8d14be8f556cbf3d9877d07e0074b4b389", + "prototypes/connections/evaluate.c": "ee8b91efc733b9d305711b7cbb7b502ae5d3917e06f750b7f62b632ed0ea6d04", + "prototypes/connections/runtime.c": "1ced6734ee07f3688e5c9cc0a8af639da88a1741dc3e6f65ec109baa2d826e6f", + "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", + "openssl": "OpenSSL 3.6.4 25 Aug 2026 (Library: OpenSSL 3.6.4 25 Aug 2026)" + } +} diff --git a/research/experiments/005-direct-document-peer/run.py b/research/experiments/005-direct-document-peer/run.py new file mode 100644 index 0000000..ff4654f --- /dev/null +++ b/research/experiments/005-direct-document-peer/run.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +"""Compare authenticated document reads through direct and Pure server paths.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import platform +import statistics +import subprocess +import sys +import tempfile +import time + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[2] +CONNECTIONS = ROOT / 'prototypes/connections' +COMMON = ROOT / 'prototypes/common' +sys.path.insert(0, str(CONNECTIONS)) +import check_document_peer as peer # noqa: E402 + + +def sha(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def command(*args): + result = subprocess.run([str(arg) for arg in args], capture_output=True, text=True, + timeout=60) + if result.returncode: + raise AssertionError((result.args, result.returncode, result.stdout, result.stderr)) + return result.stdout + + +def compile_servers(directory, cc): + flags = [cc, '-O2', '-g', '-std=c17', '-Wall', '-Wextra', '-Wpedantic', '-Werror', + '-I' + str(CONNECTIONS)] + common = [COMMON / 'tls-link.c', CONNECTIONS / 'store.c', + CONNECTIONS / 'program.c', CONNECTIONS / 'evaluate.c'] + direct = directory / 'direct-peer' + pure = directory / 'pure-server' + command(*flags, HERE / 'direct-peer.c', *common, '-lssl', '-lcrypto', '-o', direct) + command(*flags, '-DPURE_PEER_SERVER_ONLY', CONNECTIONS / 'document-peer.c', + *common, CONNECTIONS / 'store-port.c', CONNECTIONS / 'runtime.c', + '-lssl', '-lcrypto', '-o', pure) + return {'direct': direct, 'pure': pure} + + +def sections(binary): + row = command('size', binary).splitlines()[1].split() + return dict(zip(('text', 'data', 'bss'), map(int, row[:3]))) + + +def trial(binary, program, root, cert_root, ca, body, reads): + store = root / 'store' + server, port = peer.start(binary, program, store, cert_root, ca) + try: + with peer.connect(cert_root, ca, port) as sock: + peer.hello(sock, program) + assert peer.request(sock, 2, 'bench', 0, peer.edit(0, 0, body), + want_body=False) == (0, 1, b'', len(body)) + assert peer.request(sock, 1, 'bench') == (0, 1, body, len(body)) + rss_before = peer.rss_kib(server.pid) + start = time.perf_counter_ns() + for _ in range(reads): + assert peer.request(sock, 1, 'bench') == (0, 1, body, len(body)) + elapsed_ns = time.perf_counter_ns() - start + rss_after = peer.rss_kib(server.pid) + finally: + peer.stop(server) + stored = store / 'obj-bench' + return {'reads': reads, 'body_bytes': len(body), 'elapsed_ns': elapsed_ns, + 'server_rss_before_kib': rss_before, 'server_rss_after_kib': rss_after, + 'stored_file_bytes': stored.stat().st_size, + 'stored_file_sha256': sha(stored)} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--cc', default=os.environ.get('CC', 'cc')) + parser.add_argument('--body-bytes', type=int, default=8192) + parser.add_argument('--reads', type=int, default=512) + parser.add_argument('--pairs', type=int, default=4) + parser.add_argument('--work-root', type=Path, default=HERE / 'results') + parser.add_argument('--output', type=Path) + args = parser.parse_args() + if not 1 <= args.body_bytes <= 1024 * 1024 or not 1 <= args.reads <= 4096 or not 1 <= args.pairs <= 16: + parser.error('body-bytes 1..1048576, reads 1..4096, pairs 1..16') + if args.output and args.output.exists(): + parser.error('evidence file already exists') + args.work_root.mkdir(parents=True, exist_ok=True) + filesystem = command('df', '-T', args.work_root).splitlines()[1].split() + program = CONNECTIONS / 'examples/editor.pure' + body = bytes((ord('a') + i % 26 for i in range(args.body_bytes))) + with tempfile.TemporaryDirectory(prefix='pure-direct-peer-', dir=args.work_root) as name: + root = Path(name) + binaries = compile_servers(root, args.cc) + sizes = {variant: sections(binary) for variant, binary in binaries.items()} + binary_hashes = {variant: sha(binary) for variant, binary in binaries.items()} + ca = peer.certificates(root) + 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: + path = root / f'{pair}-{variant}' + path.mkdir() + # The certificates live at the common root. The store is unique + # to each server so conditional revision 0 always means create. + entry[variant] = trial(binaries[variant], program, path, root, ca, + body, args.reads) + assert entry['direct']['stored_file_sha256'] == entry['pure']['stored_file_sha256'] + samples.append(entry) + medians = {variant: {'elapsed_ms': statistics.median( + sample[variant]['elapsed_ns'] for sample in samples) / 1e6, + 'server_rss_before_kib': statistics.median( + sample[variant]['server_rss_before_kib'] for sample in samples), + 'server_rss_after_kib': statistics.median( + sample[variant]['server_rss_after_kib'] for sample in samples)} + for variant in ('direct', 'pure')} + sources = [HERE / 'direct-peer.c', HERE / 'run.py', + CONNECTIONS / 'document-peer.c', CONNECTIONS / 'check_document_peer.py', + COMMON / 'tls-link.c', CONNECTIONS / 'store.c', + CONNECTIONS / 'store-port.c', CONNECTIONS / 'program.c', + CONNECTIONS / 'evaluate.c', CONNECTIONS / 'runtime.c', program] + result = {'profile': 'authenticated-document-read-direct-versus-pure', + 'definition_sha256': sha(program), 'body_bytes': args.body_bytes, + 'reads_per_trial': args.reads, 'pairs': args.pairs, + 'filesystem': {'device': filesystem[0], 'type': filesystem[1], + 'mount': filesystem[-1]}, + 'sections': sizes, 'binary_sha256': binary_hashes, + 'samples': samples, 'medians': medians, + 'sources_sha256': {str(path.relative_to(ROOT)): sha(path) for path in sources}, + 'environment': {'platform': platform.platform(), 'machine': platform.machine(), + 'compiler': command(args.cc, '--version').splitlines()[0], + 'openssl': command('openssl', 'version').strip()}} + output = json.dumps(result, indent=2) + '\n' + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(output) + print(output, end='') + + +if __name__ == '__main__': + main() diff --git a/research/experiments/005-direct-document-peer/scalar-peer-after-nodelay-parity-linux-aarch64.json b/research/experiments/005-direct-document-peer/scalar-peer-after-nodelay-parity-linux-aarch64.json new file mode 100644 index 0000000..1316e2e --- /dev/null +++ b/research/experiments/005-direct-document-peer/scalar-peer-after-nodelay-parity-linux-aarch64.json @@ -0,0 +1 @@ +{"alpn": "pass", "disconnect_release": "pass", "idle_deadline": "pass", "malformed_frames": "pass", "mutual_authentication": "pass", "persist_restart": "pass", "python_client": "pass", "revision_conflict": "pass", "server_connected_rss_kib": 8640, "server_hostname": "pass", "server_idle_rss_kib": 7968} |
