tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

performance_smoke.c (6711B)


      1 /* Correctness-checked ABI v2 performance smoke. No compiler execution.
      2  * Times exclude context/kernel loading; process peak RSS includes everything.
      3  * The library's optimization mode is specified by the Nix package, not gcc. */
      4 #define _POSIX_C_SOURCE 200809L
      5 #ifndef ARB_HOST_MODE
      6 #error "Set ARB_HOST_MODE to the linked runtime's optimization mode"
      7 #endif
      8 #include "arboricx.h"
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <stdint.h>
     12 #include <string.h>
     13 #include <time.h>
     14 #include <sys/resource.h>
     15 #include <unistd.h>
     16 
     17 #define CHECK(x) do { if (!(x)) { fprintf(stderr, "failed: %s:%d: %s\n", __FILE__, __LINE__, #x); exit(1); } } while (0)
     18 #define OK(x) CHECK((x) == ARB_OK)
     19 static double now(void) {
     20     struct timespec t; CHECK(clock_gettime(CLOCK_MONOTONIC, &t) == 0);
     21     return (double)t.tv_sec + (double)t.tv_nsec / 1e9;
     22 }
     23 static long rss_kib(void) {
     24     FILE *f = fopen("/proc/self/statm", "r");
     25     if (!f) return -1;
     26     long pages = 0, resident = 0;
     27     CHECK(fscanf(f, "%ld %ld", &pages, &resident) == 2);
     28     fclose(f);
     29     return resident * sysconf(_SC_PAGESIZE) / 1024;
     30 }
     31 static long peak_rss_kib(void) {
     32     // VmHWM belongs to this executable's address space. ru_maxrss can include
     33     // the launcher's pre-exec address space, obscuring small-workload peaks.
     34     FILE *f = fopen("/proc/self/status", "r"); CHECK(f);
     35     char line[256]; long peak = -1;
     36     while (fgets(line, sizeof line, f)) {
     37         if (sscanf(line, "VmHWM: %ld kB", &peak) == 1) break;
     38     }
     39     fclose(f); CHECK(peak >= 0); return peak;
     40 }
     41 static arb_handle_t load(arb_ctx_t *ctx, const char *path) {
     42     FILE *f = fopen(path, "rb"); CHECK(f);
     43     CHECK(fseek(f, 0, SEEK_END) == 0); long n = ftell(f); CHECK(n > 0);
     44     rewind(f); uint8_t *buf = malloc((size_t)n); CHECK(buf);
     45     CHECK(fread(buf, 1, (size_t)n, f) == (size_t)n); fclose(f);
     46     arb_handle_t h; OK(arb_load_bundle_default(ctx, buf, (size_t)n, &h));
     47     free(buf); return h;
     48 }
     49 int main(int argc, char **argv) {
     50     CHECK(argc == 5); CHECK(arb_abi_version() == 2);
     51     // Apply limits before runtime initialization without a Python preexec hook.
     52     struct rlimit memory = {512 * 1024 * 1024, 512 * 1024 * 1024};
     53     struct rlimit stack = {8 * 1024 * 1024, 8 * 1024 * 1024};
     54     struct rlimit cpu = {60, 60}, core = {0, 0};
     55     CHECK(setrlimit(RLIMIT_AS, &memory) == 0);
     56     CHECK(setrlimit(RLIMIT_STACK, &stack) == 0);
     57     CHECK(setrlimit(RLIMIT_CPU, &cpu) == 0);
     58     CHECK(setrlimit(RLIMIT_CORE, &core) == 0);
     59     const char *kind = argv[1];
     60     size_t n = strtoul(argv[2], NULL, 10), rounds = strtoul(argv[3], NULL, 10);
     61     CHECK(n > 0 && rounds > 0);
     62     int strings = strcmp(kind, "id") == 0 || strcmp(kind, "append") == 0;
     63     int append = strcmp(kind, "append") == 0;
     64     int distribution = strcmp(kind, "distribution") == 0;
     65     CHECK(strings || distribution || strcmp(kind, "deep") == 0);
     66     double start = now();
     67     arb_ctx_t *ctx = arboricx_init(); CHECK(ctx);
     68     arb_handle_t leaf, function;
     69     OK(arb_leaf(ctx, &leaf));
     70     if (strings) {
     71         function = load(ctx, argv[4]);
     72     } else if (distribution) {
     73         // I = Fork (Stem K) K, K = Stem Leaf; I x distributes before returning x.
     74         arb_handle_t k, sk;
     75         OK(arb_stem(ctx, leaf, &k)); OK(arb_stem(ctx, k, &sk));
     76         OK(arb_fork(ctx, sk, k, &function));
     77         OK(arb_release(ctx, sk)); OK(arb_release(ctx, k));
     78     } else {
     79         OK(arb_retain(ctx, leaf, &function));
     80     }
     81     double init_s = now() - start;
     82     uint8_t *input = malloc(n), *second = malloc(n); CHECK(input && second);
     83     double build_s = 0, reduce_s = 0, observe_s = 0, gc_s = 0;
     84     long rss_mid = -1, rss_end = -1;
     85     for (size_t r = 0; r < rounds; ++r) {
     86         /* Fresh, round-dependent bytes also prevent cross-host harnesses from
     87          * accidentally sharing one already-evaluated Haskell result. */
     88         if (strings) for (size_t j = 0; j < n; ++j) {
     89             input[j] = (uint8_t)('a' + (j + r) % 26);
     90             second[j] = (uint8_t)('A' + (j + r) % 26);
     91         }
     92         start = now();
     93         arb_handle_t term, result;
     94         if (strings) {
     95             arb_handle_t arg;
     96             OK(arb_of_bytes(ctx, input, n, &arg));
     97             OK(arb_app(ctx, function, arg, &term));
     98             if (append) {
     99                 arb_handle_t next, arg2;
    100                 OK(arb_of_bytes(ctx, second, n, &arg2));
    101                 OK(arb_app(ctx, term, arg2, &next));
    102                 OK(arb_release(ctx, arg2));
    103                 OK(arb_release(ctx, term)); term = next;
    104             }
    105             OK(arb_release(ctx, arg));
    106         } else {
    107             OK(arb_retain(ctx, leaf, &term));
    108             for (size_t j = 0; j < n; ++j) {
    109                 arb_handle_t next;
    110                 if (distribution) {
    111                     OK(arb_app(ctx, function, term, &next));
    112                 } else {
    113                     arb_handle_t k;
    114                     OK(arb_fork(ctx, leaf, term, &k));
    115                     OK(arb_app(ctx, k, leaf, &next));
    116                     OK(arb_release(ctx, k));
    117                 }
    118                 OK(arb_release(ctx, term)); term = next;
    119             }
    120         }
    121         build_s += now() - start;
    122         start = now();
    123         OK(arb_reduce(ctx, term, 1000000000, &result));
    124         reduce_s += now() - start;
    125         OK(arb_release(ctx, term));
    126         start = now();
    127         if (strings) {
    128             uint8_t *out; size_t len;
    129             OK(arb_to_bytes(ctx, result, &out, &len));
    130             CHECK(len == n * (append ? 2 : 1));
    131             CHECK(memcmp(out, input, n) == 0);
    132             if (append) CHECK(memcmp(out + n, second, n) == 0);
    133             arboricx_free_buf(ctx, out, len);
    134         } else {
    135             int is_leaf; OK(arb_is_leaf(ctx, result, &is_leaf)); CHECK(is_leaf);
    136         }
    137         observe_s += now() - start;
    138         OK(arb_release(ctx, result));
    139         // Batch reclamation rather than forcing one collection per tiny call.
    140         if ((r + 1) % 16 == 0 || r + 1 == rounds || r + 1 == rounds / 2) {
    141             start = now(); OK(arb_collect(ctx)); gc_s += now() - start;
    142             if (r + 1 == rounds / 2) rss_mid = rss_kib();
    143             if (r + 1 == rounds) rss_end = rss_kib();
    144         }
    145     }
    146     long peak = peak_rss_kib();
    147     printf("{\"host_mode\":\"%s\",\"kind\":\"%s\",\"n\":%zu,\"rounds\":%zu,\"init_s\":%.9f,"
    148            "\"build_s\":%.9f,\"reduce_s\":%.9f,\"observe_s\":%.9f,\"gc_s\":%.9f,"
    149            "\"peak_rss_kib\":%ld,\"mid_rss_kib\":%ld,\"end_rss_kib\":%ld}\n",
    150            ARB_HOST_MODE, kind, n, rounds, init_s, build_s, reduce_s, observe_s, gc_s,
    151            peak, rss_mid, rss_end);
    152     free(input); free(second); OK(arb_release(ctx, function)); OK(arb_release(ctx, leaf));
    153     arboricx_free(ctx);
    154     return 0;
    155 }