diff options
| author | eunhhu <[email protected]> | 2026-10-19 14:47:10 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-19 14:47:10 +0100 |
| commit | 415bc5af13f6658ac44a0d135bb0f290191a44f6 (patch) | |
| tree | 2f14be1edb5d11bda28b3e644b8c426503e3e361 /research/experiments/047-leaf-commit/packet.c | |
| parent | a93891ee72bb1793eb1845d6d49369cb9f1e8806 (diff) | |
| download | pure-415bc5af13f6658ac44a0d135bb0f290191a44f6.tar.gz pure-415bc5af13f6658ac44a0d135bb0f290191a44f6.zip | |
047: "the leaf is the commit" measured against the bars set before: appends pass, sparse updates do not, as said beforehand that means two units, not one: list bytes a message at 1 / 16 / 256 a commit, entries' appends today 65.5 / 15.7 / 12.4, leaves cow 1,636 / 102 / 14.5, leaves with appends in place past every kept snapshot's count (tail) 25.9 / 10.9 / 10.0; the bank's transfers today 122 / 35.6 / 30.1 (bars 244 / 71 / 60), cow 7,428 / 2,098 / 996, tail 4,826 / 1,975 / 992, cells written in place 186 / 126 / 122 but seen by kept snapshots (933,888 / 542,208 / 10,848 times); after the run, a leaf with room for changes (delta, 16 B each, the leaf written anew when full): 739 / 545 / 292 with room 8, 363 / 268 / 176 with 32, 249 / 161 / 136 with 128, every one over (today's log puts a change in about 7.5 B: varint places, zigzag wholes, one frame a commit); no kept snapshot sees a write of cow, tail or delta; rows as leaves (packet.c): 44.4 machine instructions a row to check and take 32-row column chunks against pure_list_read's 1,318 (cycles 14.2 against 372), every cell the same
Diffstat (limited to 'research/experiments/047-leaf-commit/packet.c')
| -rw-r--r-- | research/experiments/047-leaf-commit/packet.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/research/experiments/047-leaf-commit/packet.c b/research/experiments/047-leaf-commit/packet.c new file mode 100644 index 0000000..b079a4a --- /dev/null +++ b/research/experiments/047-leaf-commit/packet.c @@ -0,0 +1,104 @@ +/* Research 047: a message's rows as leaves (feedback 009: packet bytes = memory leaf), against today's reading. + * The rows of UAX #14's load message (6 fields of 8 bytes, 2,787 rows: tests/specs/linebreak/ranges.bin): + * today pure_list_read into an event's list, as message() does (the list made a tree, each row decoded and + * inserted) + * leaves the same rows sent as leaves: chunks of 32 rows, each field's 32 cells together (the memory leaf's + * columns, core/module-list.c), checked as decode_cell checks them (each number finite, each bool 0 or 1) + * and taken where they are: a chunk's address kept, nothing copied + * Machine instructions and cycles a row (perf_event_open, user mode), ROUNDS passes each. + * + * cc -O2 -std=c17 -I../../../core packet.c ../../../build/libpure.a -lm -pthread -o packet + * ./packet ranges.bin ROUNDS one JSON line */ +#define _GNU_SOURCE +#include "module-list.h" +#include <linux/perf_event.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/syscall.h> +#include <unistd.h> + +enum { FIELDS = 6, RUN = 32 }; +static const uint8_t kinds[FIELDS] = {PURE_CELL_NUMBER, PURE_CELL_NUMBER, PURE_CELL_BOOL, PURE_CELL_BOOL, PURE_CELL_BOOL, PURE_CELL_BOOL}; + +static int counter(uint64_t config) { + struct perf_event_attr a; + memset(&a, 0, sizeof(a)); + a.size = sizeof(a), a.type = PERF_TYPE_HARDWARE, a.config = config, a.disabled = 1, a.exclude_kernel = 1, a.exclude_hv = 1; + return (int)syscall(SYS_perf_event_open, &a, 0, -1, -1, 0); +} +static uint64_t read_counter(int fd) { + uint64_t v = 0; + if (read(fd, &v, sizeof(v)) != sizeof(v)) return 0; + return v; +} + +/* A leaf as memory keeps one: a 40-byte header (unused here but laid out), then each field's 32 cells. */ +typedef struct { unsigned char header[40]; double cells[FIELDS][RUN]; } Leaf; + +/* The leaves taken: each checked, then its address kept. */ +static __attribute__((noinline)) size_t take_leaves(const Leaf *leaves, size_t chunks, size_t rows, const Leaf **kept) { + for (size_t c = 0; c < chunks; ++c) { + const Leaf *leaf = &leaves[c]; + size_t n = c + 1 < chunks ? RUN : rows - c * RUN; + for (unsigned f = 0; f < FIELDS; ++f) { + const double *column = leaf->cells[f]; + if (kinds[f] == PURE_CELL_BOOL) { + for (size_t i = 0; i < n; ++i) if (!(column[i] == 0 || column[i] == 1)) return SIZE_MAX; + } else + for (size_t i = 0; i < n; ++i) if (!isfinite(column[i])) return SIZE_MAX; + } + kept[c] = leaf; + } + return rows; +} + +int main(int argc, char **argv) { + if (argc != 3) { fprintf(stderr, "usage: %s ranges.bin ROUNDS\n", argv[0]); return 2; } + FILE *f = fopen(argv[1], "rb"); + if (!f) return 1; + static unsigned char bytes[1 << 20]; + size_t size = fread(bytes, 1, sizeof(bytes), f); + fclose(f); + int rounds = atoi(argv[2]); + size_t rows = size / (8 * FIELDS), chunks = (rows + RUN - 1) / RUN; + /* The same rows laid out as leaves (what a sender would put on the wire). */ + Leaf *leaves = calloc(chunks, sizeof(Leaf)); + for (size_t r = 0; r < rows; ++r) + for (unsigned g = 0; g < FIELDS; ++g) memcpy(&leaves[r / RUN].cells[g][r % RUN], bytes + 8 * (FIELDS * r + g), 8); + const Leaf **kept = calloc(chunks, sizeof(*kept)); + PureList *list = pure_list_new(FIELDS, kinds); + if (!list || !pure_list_arrays(list)) return 1; + /* Both read the same: each row's fields. */ + if (pure_list_read(list, bytes, size) != PURE_OK || pure_list_count(list) != rows || + take_leaves(leaves, chunks, rows, kept) != rows) + return 1; + for (size_t r = 0; r < rows; ++r) + for (unsigned g = 0; g < FIELDS; ++g) + if (pure_list_get(list, r, g).num != kept[r / RUN]->cells[g][r % RUN]) { fprintf(stderr, "row %zu differs\n", r); return 1; } + int ins = counter(PERF_COUNT_HW_INSTRUCTIONS), cyc = counter(PERF_COUNT_HW_CPU_CYCLES); + if (ins < 0 || cyc < 0) return 1; + uint64_t today_ins = 0, today_cyc = 0, leaves_ins = 0, leaves_cyc = 0; + for (int k = 0; k < rounds; ++k) { + pure_list_clear(list); + ioctl(ins, PERF_EVENT_IOC_RESET, 0), ioctl(cyc, PERF_EVENT_IOC_RESET, 0); + ioctl(ins, PERF_EVENT_IOC_ENABLE, 0), ioctl(cyc, PERF_EVENT_IOC_ENABLE, 0); + PureResult read = pure_list_read(list, bytes, size); + ioctl(ins, PERF_EVENT_IOC_DISABLE, 0), ioctl(cyc, PERF_EVENT_IOC_DISABLE, 0); + if (read != PURE_OK) return 1; + today_ins += read_counter(ins), today_cyc += read_counter(cyc); + ioctl(ins, PERF_EVENT_IOC_RESET, 0), ioctl(cyc, PERF_EVENT_IOC_RESET, 0); + ioctl(ins, PERF_EVENT_IOC_ENABLE, 0), ioctl(cyc, PERF_EVENT_IOC_ENABLE, 0); + size_t taken = take_leaves(leaves, chunks, rows, kept); + ioctl(ins, PERF_EVENT_IOC_DISABLE, 0), ioctl(cyc, PERF_EVENT_IOC_DISABLE, 0); + if (taken != rows) return 1; + leaves_ins += read_counter(ins), leaves_cyc += read_counter(cyc); + } + double n = (double)rows * rounds; + printf("{\"rows\":%zu,\"fields\":%d,\"chunks\":%zu,\"rounds\":%d,\"today_instructions_per_row\":%.1f,\"today_cycles_per_row\":%.1f," + "\"leaves_instructions_per_row\":%.1f,\"leaves_cycles_per_row\":%.1f,\"answers\":\"every cell the same both ways\"}\n", + rows, FIELDS, chunks, rounds, today_ins / n, today_cyc / n, leaves_ins / n, leaves_cyc / n); + return 0; +} |
