aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/044-specs/layout_time.c
blob: 3f2ce52dd77d87f1144986ff7ea12adcb778b65d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

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;
}