// Hand-C reference for bench_list_sum. // // Same algorithm as examples/bench_list_sum.ail — build a linked list // of [0, 1, ..., N-1] via prepending, then sum by linear traversal. // Three workload sizes: 100k / 1M / 3M cells, matching the AILang // fixture exactly so the AILang/C wall-time ratio is fair. // // Cell layout: { long head; struct cell *tail; } — 16 bytes (8 head // + 8 pointer). AILang's IntList ICons cell is wider (tag + payload + // tail = 24 bytes) because the runtime carries a constructor tag for // the discriminated-union. The 1.5x size difference is one of the // real costs of the discriminated-union representation; quoting the // raw ratio without naming this is the wrong comparison. // // Memory policy: this reference uses malloc and DELIBERATELY DOES // NOT FREE. That matches AILang's bench_list_sum running under // --alloc=rc with implicit-mode params (cells leak by design — the // 18c.3 known debt). The fair comparison is therefore: // AILang --alloc=rc (implicit-mode, leaks) vs. this C (leaks) // A future iter that ships explicit-mode bench_list_sum + a free()- // adding C variant would close the apples-to-apples gap on the // dec-cost axis. // // Build: clang -O2 -o list_sum list_sum.c // Expected stdout (one int per line): // 4999950000 // 499999500000 // 4499998500000 #include #include typedef struct cell { long head; struct cell *tail; } cell_t; static cell_t *cons_n(long n) { cell_t *acc = NULL; for (long i = n - 1; i >= 0; i--) { cell_t *c = (cell_t *) malloc(sizeof(cell_t)); c->head = i; c->tail = acc; acc = c; } return acc; } static long sum_list(const cell_t *xs) { long acc = 0; while (xs) { acc += xs->head; xs = xs->tail; } return acc; } static void run_one(long n) { const cell_t *xs = cons_n(n); printf("%ld\n", sum_list(xs)); } int main(void) { run_one(100000); run_one(1000000); run_one(3000000); return 0; }