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 }