aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/004-direct-document-path/document-path.c
diff options
context:
space:
mode:
Diffstat (limited to 'research/experiments/004-direct-document-path/document-path.c')
-rw-r--r--research/experiments/004-direct-document-path/document-path.c207
1 files changed, 207 insertions, 0 deletions
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 <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#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;
+}