diff options
| author | eunhhu <[email protected]> | 2026-10-08 07:46:32 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-08 07:46:32 +0100 |
| commit | 14c315da3f8238070c4ae665be1bc139975310be (patch) | |
| tree | 60c3713bbc7fabca65699e7e7caa5b7006460f38 /research/experiments/041-batches/search | |
| parent | 1f8faacd65b5dc85192d05b68bd75a780962c4a6 (diff) | |
| download | pure-14c315da3f8238070c4ae665be1bc139975310be.tar.gz pure-14c315da3f8238070c4ae665be1bc139975310be.zip | |
041 runs of b7 = 1f8faac (the suite three ways and counts: batches BFS 41.0 ms, the compiled code 7.66, alone 93.4; the others and the executor alone as b6's) and search/: the suite's graph loaded and one handler run again and again (frontier-run: each node's edges' to summed, 70 instructions an edge batched, 35 compiled; search-run: the BFS, 211 and 38.5), counted with icount (2026-10-08-search-counts.jsonl)
Diffstat (limited to 'research/experiments/041-batches/search')
| -rw-r--r-- | research/experiments/041-batches/search/frontier-run.c | 78 | ||||
| -rw-r--r-- | research/experiments/041-batches/search/frontier.aup | 77 | ||||
| -rw-r--r-- | research/experiments/041-batches/search/search-run.c | 81 |
3 files changed, 236 insertions, 0 deletions
diff --git a/research/experiments/041-batches/search/frontier-run.c b/research/experiments/041-batches/search/frontier-run.c new file mode 100644 index 0000000..2f740ae --- /dev/null +++ b/research/experiments/041-batches/search/frontier-run.c @@ -0,0 +1,78 @@ +/* research 130: the suite's graph (natural.h) loaded as suite-run.c loads it, then one handler run ROUNDS times. + * Built from the repository's root: cc -O2 -std=c17 -Icore -Iresearch/experiments/040-* (natural.h) FILE build/libpure.a -lm. + * frontier-run DIR ROUNDS: frontier.aup's frontier (each node's edges' to summed by a walk in a guarded loop); + * search-run DIR ROUNDS: graph.pmod's route (the suite's BFS), its best ms. Counted with 041's icount (ROUNDS 1 and 11). */ +/* Research 124's suite: kinds of work in one event each, Pure against C in + * this process on the same machine, the same inputs (each program draws them + * by MINSTD, x * 48271 mod 2^31 - 1, from seed 1, as its C does), the same + * answers (checked every round). The C is what one would write for the task: + * the same algorithm, its natural types (whole numbers as integers), qsort, + * a hash table with linear probing, snprintf. + * + * suite-run DIR ROUNDS [NAME...] a JSON line per round of each workload + * (Pure's ms, C's ms), then per workload the medians and Pure / C. DIR holds + * the workloads' modules (NAME.pmod; bfs: 033's graph.aup as graph.pmod; + * bfs_ahead: the same search against bfs.c read ahead, for context). + * Without NAMEs, all of them. PURE_JIT and PURE_AHEAD as the core reads them. */ +#define _POSIX_C_SOURCE 200809L +#include "module.h" +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +static double now_ms(void) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return (double)t.tv_sec * 1e3 + (double)t.tv_nsec / 1e6; +} + +static int compare_doubles(const void *a, const void *b) { + double x = *(const double *)a, y = *(const double *)b; + return (x > y) - (x < y); +} + +#include "natural.h" + +/* The graph into the app (size, then the edges as rows) and into C's adjacency. */ +static bool load_graph(PureApp *app, const PureModule *m, uint32_t nodes, size_t edges) { + uint64_t *keys; + size_t unique = graph_build(nodes, edges, &keys); + unsigned char four[4]; + memcpy(four, &nodes, 4); + if (pure_app_message(app, (uint32_t)pure_module_input(m, "size"), NULL, -1, (PureSpan){four, 4}) != PURE_OK) return false; + enum { CHUNK = 65536 }; + double *rows = malloc(CHUNK * 2 * sizeof(*rows)); + for (size_t at = 0; at < unique; at += CHUNK) { + size_t n = unique - at < CHUNK ? unique - at : CHUNK; + for (size_t k = 0; k < n; ++k) rows[2 * k] = (double)keys[at + k], rows[2 * k + 1] = (double)(keys[at + k] & 0xFFFFF); + if (pure_app_message(app, (uint32_t)pure_module_input(m, "load"), NULL, -1, (PureSpan){(unsigned char *)rows, n * 16}) != + PURE_OK) + return false; + } + free(rows), free(keys); + return true; +} + + +/* front-run DIR ROUNDS: the frontier walk (front.pmod) ROUNDS times after the graph's load (the first packs the edges): its ms and sum. */ +int main(int argc, char **argv) { + static PureModule module; + static PureApp app; + char error[200], path[512]; + snprintf(path, sizeof(path), "%s/frontier.pmod", argv[1]); + if (pure_module_read(path, &module, error, sizeof(error)) != PURE_OK) { fprintf(stderr, "%s\n", error); return 1; } + if (pure_app_start(&app, &module, (PureData){0}) != PURE_OK) return 1; + if (!load_graph(&app, &module, 100000, 1000000)) return 1; + int rounds = atoi(argv[2]), input = pure_module_input(&module, "frontier"), sum = pure_module_state(&module, "sum"); + for (int r = 0; r <= rounds; ++r) { + uint32_t args[2] = {0, 0}; + double a = now_ms(); + PureResult result = pure_app_message(&app, (uint32_t)input, NULL, -1, (PureSpan){(unsigned char *)args, 8}); + double ms = now_ms() - a; + if (result != PURE_OK) { fprintf(stderr, "failed %s\n", pure_fault_name(app.fault)); return 1; } + printf("{\"round\":%d,\"ms\":%.3f,\"sum\":%.17g,\"work\":%llu}\n", r, ms, app.values[sum].num, (unsigned long long)app.work); + } +} diff --git a/research/experiments/041-batches/search/frontier.aup b/research/experiments/041-batches/search/frontier.aup new file mode 100644 index 0000000..a9a460a --- /dev/null +++ b/research/experiments/041-batches/search/frontier.aup @@ -0,0 +1,77 @@ +# The graph of research 118 (docs/design/018 stage 2): edges keyed by +# from * 1048576 + to, loaded in bulk as rows; a breadth first search in +# one event, its queue and what it has seen the event's own lists. +record Edge { + link = 0 + to = 0 +} +record Seen { + node = 0 + depth = 0 +} +record Step { + node = 0 +} +state edges: list of Edge by link +state nodes = 0 +state hops = -1 +state reached = 0 +state sum = 0 +input size = message { + nodes: u32 +} +input load = message { + rows: list of Edge +} +input frontier = message { + from: u32 + to: u32 +} +input route = message { + from: u32 + to: u32 +} +on size { + nodes = size.nodes +} +on load { + for r in load.rows { + edges.add(Edge { ..r }) + } +} +on route { + let seen: list of Seen by node + let queue: list of Step + queue.append(Step { node: route.from }) + seen.add(Seen { node: route.from }) + for i in 0 .. nodes { + if i >= queue.count { break } + let q = queue.first + i + for e in edges[q.node * 1048576 .. q.node * 1048576 + 1048576] { + if !seen.has(e.to) { + seen.add(Seen { node: e.to, depth: seen[q.node].depth + 1 }) + queue.append(Step { node: e.to }) + } + } + } + hops = if seen.has(route.to) { seen[route.to].depth } else { -1 } + reached = seen.count +} +view far = hops +view found = reached + +on frontier { + let queue: list of Step + for v in 0 .. nodes { + queue.append(Step { node: v }) + } + let total = 0 + for i in 0 .. nodes { + if i >= queue.count { break } + let q = queue.first + i + for e in edges[q.node * 1048576 .. q.node * 1048576 + 1048576] { + total = total + e.to + } + } + sum = total +} diff --git a/research/experiments/041-batches/search/search-run.c b/research/experiments/041-batches/search/search-run.c new file mode 100644 index 0000000..ea2b9e9 --- /dev/null +++ b/research/experiments/041-batches/search/search-run.c @@ -0,0 +1,81 @@ +/* research 130: the suite's graph (natural.h) loaded as suite-run.c loads it, then one handler run ROUNDS times. + * Built from the repository's root: cc -O2 -std=c17 -Icore -Iresearch/experiments/040-* (natural.h) FILE build/libpure.a -lm. + * frontier-run DIR ROUNDS: frontier.aup's frontier (each node's edges' to summed by a walk in a guarded loop); + * search-run DIR ROUNDS: graph.pmod's route (the suite's BFS), its best ms. Counted with 041's icount (ROUNDS 1 and 11). */ +/* Research 124's suite: kinds of work in one event each, Pure against C in + * this process on the same machine, the same inputs (each program draws them + * by MINSTD, x * 48271 mod 2^31 - 1, from seed 1, as its C does), the same + * answers (checked every round). The C is what one would write for the task: + * the same algorithm, its natural types (whole numbers as integers), qsort, + * a hash table with linear probing, snprintf. + * + * suite-run DIR ROUNDS [NAME...] a JSON line per round of each workload + * (Pure's ms, C's ms), then per workload the medians and Pure / C. DIR holds + * the workloads' modules (NAME.pmod; bfs: 033's graph.aup as graph.pmod; + * bfs_ahead: the same search against bfs.c read ahead, for context). + * Without NAMEs, all of them. PURE_JIT and PURE_AHEAD as the core reads them. */ +#define _POSIX_C_SOURCE 200809L +#include "module.h" +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +static double now_ms(void) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return (double)t.tv_sec * 1e3 + (double)t.tv_nsec / 1e6; +} + +static int compare_doubles(const void *a, const void *b) { + double x = *(const double *)a, y = *(const double *)b; + return (x > y) - (x < y); +} + +#include "natural.h" + +/* The graph into the app (size, then the edges as rows) and into C's adjacency. */ +static bool load_graph(PureApp *app, const PureModule *m, uint32_t nodes, size_t edges) { + uint64_t *keys; + size_t unique = graph_build(nodes, edges, &keys); + unsigned char four[4]; + memcpy(four, &nodes, 4); + if (pure_app_message(app, (uint32_t)pure_module_input(m, "size"), NULL, -1, (PureSpan){four, 4}) != PURE_OK) return false; + enum { CHUNK = 65536 }; + double *rows = malloc(CHUNK * 2 * sizeof(*rows)); + for (size_t at = 0; at < unique; at += CHUNK) { + size_t n = unique - at < CHUNK ? unique - at : CHUNK; + for (size_t k = 0; k < n; ++k) rows[2 * k] = (double)keys[at + k], rows[2 * k + 1] = (double)(keys[at + k] & 0xFFFFF); + if (pure_app_message(app, (uint32_t)pure_module_input(m, "load"), NULL, -1, (PureSpan){(unsigned char *)rows, n * 16}) != + PURE_OK) + return false; + } + free(rows), free(keys); + return true; +} + + +/* search-run DIR ROUNDS: the suite's breadth first search (graph.pmod) ROUNDS times after the graph's load and one search + * (it packs the edges), from the nodes suite-run draws: hops and reached of the last. */ +int main(int argc, char **argv) { + static PureModule module; + static PureApp app; + char error[200], path[512]; + snprintf(path, sizeof(path), "%s/graph.pmod", argv[1]); + if (pure_module_read(path, &module, error, sizeof(error)) != PURE_OK) { fprintf(stderr, "%s\n", error); return 1; } + if (pure_app_start(&app, &module, (PureData){0}) != PURE_OK) return 1; + if (!load_graph(&app, &module, 100000, 1000000)) return 1; + int rounds = atoi(argv[2]), input = pure_module_input(&module, "route"), hops = pure_module_state(&module, "hops"), reached = pure_module_state(&module, "reached"); + double best = 1e9; + for (int r = 0; r <= rounds; ++r) { + uint32_t args[2] = {(uint32_t)(graph_next() % 100000), (uint32_t)(graph_next() % 100000)}; + double a = now_ms(); + PureResult result = pure_app_message(&app, (uint32_t)input, NULL, -1, (PureSpan){(unsigned char *)args, 8}); + double ms = now_ms() - a; + if (result != PURE_OK) { fprintf(stderr, "failed %s\n", pure_fault_name(app.fault)); return 1; } + if (r && ms < best) best = ms; + } + printf("{\"best_ms\":%.3f,\"hops\":%.17g,\"reached\":%.17g}\n", best, app.values[hops].num, app.values[reached].num); +} |
