From 6215b1177e377b512aaa1296faa6311f809135f2 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Mon, 19 Oct 2026 06:18:49 +0100 Subject: 140's 8, box layout, written in aup with no new opcode (aup's examples/layout.aup: a subset of CSS Flexbox 1 written down first, one line, row or column, padding, gap, six justify-content, four align-items, grow and shrink with 9.7's loop, cross sizes stretched, nothing sized by content): 2,000 random trees (29,388 nodes; 7,659 items frozen at their padding by the loop) are Taffy 0.15's within 0.01 (layout_taffy, built by cargo; the worst 8.3e-5) and layout.py's bit for bit on the three paths, the paths the same; the single-pass subset first tried refused 66% of trees, so the loop came in; Taffy given min sizes equal to the padding (given 0 it floors at 0 while resolving lengths, where 9.7 floors the content box), the reference adding from the left (sum compensates since Python 3.12); module_specs in make check: 100 trees (tests/specs/layout); bars: 116 lines against Python's 54, 4,754 ns a node, Taffy's 644 (7.4x), pure Python 5,396 (Pure 1.14x faster), 1,494 instructions a node (9.7's loop 44%); four frictions; aup's rows read by position no longer copy (99,067 ns a node before); the suite table's row 8; CHECKPOINT's direction row --- research/experiments/044-specs/layout_time.c | 99 ++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 research/experiments/044-specs/layout_time.c (limited to 'research/experiments/044-specs/layout_time.c') diff --git a/research/experiments/044-specs/layout_time.c b/research/experiments/044-specs/layout_time.c new file mode 100644 index 0000000..3f2ce52 --- /dev/null +++ b/research/experiments/044-specs/layout_time.c @@ -0,0 +1,99 @@ +/* Research 140: box layout (a subset of CSS Flexbox 1) in aup (aup's examples/layout.aup, run by Pure), timed on the + * trees layout_check.py writes (each a u32 count, its nodes' rows of 14 binary64s in preorder, then their boxes, four + * binary64s each, as layout.py works them out). Each tree is a message; its boxes must be the file's, bit for bit; then + * the median of seven runs of ROUNDS passes. Taffy's time is layout_taffy --time's. + * + * cc -O2 -std=c17 -I../../../core layout_time.c ../../../build/libpure.a -lm -pthread -o layout_time + * ./layout_time layout.pmod TREES ROUNDS + * + * One JSON line: the trees and nodes, the nanoseconds per node, and the instructions Pure counted for one pass. */ +#define _POSIX_C_SOURCE 200809L +#include "module.h" +#include "module-list.h" +#include +#include +#include +#include + +static double now_ns(void) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return (double)t.tv_sec * 1e9 + (double)t.tv_nsec; +} + +static int by_value(const void *a, const void *b) { + double x = *(const double *)a, y = *(const double *)b; + return (x > y) - (x < y); +} + +static unsigned char *whole(const char *path, size_t *size) { + FILE *f = fopen(path, "rb"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + *size = (size_t)ftell(f); + rewind(f); + unsigned char *bytes = malloc(*size ? *size : 1); + if (fread(bytes, 1, *size, f) != *size) { free(bytes); bytes = NULL; } + fclose(f); + return bytes; +} + +typedef struct { uint32_t count; const unsigned char *rows; const unsigned char *boxes; } Tree; + +int main(int argc, char **argv) { + if (argc != 4) { fprintf(stderr, "usage: %s MODULE TREES ROUNDS\n", argv[0]); return 2; } + size_t size; + unsigned char *bytes = whole(argv[2], &size); + int rounds = atoi(argv[3]); + if (!bytes) { fprintf(stderr, "cannot read the trees\n"); return 1; } + static Tree trees[1 << 16]; + size_t count = 0, nodes = 0; + for (size_t at = 0; at + 4 <= size && count < sizeof(trees) / sizeof(*trees); ++count) { + Tree *t = &trees[count]; + memcpy(&t->count, bytes + at, 4); + t->rows = bytes + at + 4; + t->boxes = t->rows + 112 * (size_t)t->count; + at += 4 + 144 * (size_t)t->count; + nodes += t->count; + } + + PureModule module = {0}; + char error[160]; + if (pure_module_read(argv[1], &module, error, sizeof(error)) != PURE_OK) { fprintf(stderr, "%s\n", error); return 1; } + PureApp app; + if (pure_app_start(&app, &module, (PureData){0}) != PURE_OK) return 1; + int input = pure_module_input(&module, "tree"), boxes = pure_module_state(&module, "boxes"); + size_t right = 0; + uint64_t work = 0; + for (size_t k = 0; k < count; ++k) { + const Tree *t = &trees[k]; + bool same = pure_app_message(&app, (uint32_t)input, (PureSpan){t->rows, 112 * (size_t)t->count}) == PURE_OK && + pure_list_count(app.lists[boxes]) == t->count; + work += app.work; + for (uint32_t i = 0; same && i < t->count; ++i) + for (unsigned f = 0; same && f < 4; ++f) { + double want; + memcpy(&want, t->boxes + 32 * (size_t)i + 8 * f, 8); + same = pure_list_get(app.lists[boxes], i, f).num == want; + } + right += same; + } + if (right != count) { fprintf(stderr, "trees %zu: Pure right %zu\n", count, right); return 1; } + double runs[7]; + for (int r = 0; r < 7; ++r) { + double start = now_ns(); + for (int i = 0; i < rounds; ++i) + for (size_t k = 0; k < count; ++k) + if (pure_app_message(&app, (uint32_t)input, (PureSpan){trees[k].rows, 112 * (size_t)trees[k].count}) != PURE_OK) + return 1; + runs[r] = (now_ns() - start) / rounds; + } + qsort(runs, 7, sizeof(double), by_value); + const char *jit = getenv("PURE_JIT"), *batch = getenv("PURE_BATCH"); + printf("{\"trees\":%zu,\"nodes\":%zu,\"right\":%zu,\"jit\":\"%s\",\"batch\":\"%s\",\"pure_ns_per_node\":%.1f," + "\"instructions\":%llu,\"instructions_per_node\":%.1f}\n", + count, nodes, right, jit ? jit : "", batch ? batch : "", runs[3] / (double)nodes, (unsigned long long)work, + (double)work / (double)nodes); + pure_app_stop(&app); + return 0; +} -- cgit v1.2.3