/// Simple standalone Nock benchmark /// This is a simplified version that doesn't require linking against full Vere #include #include #include #include #include #include // Simplified noun type (direct atom or indirect cell) typedef uintptr_t noun; #define IS_ATOM(n) (!((n) & 1)) #define IS_CELL(n) ((n) & 1) #define MAKE_DIRECT_ATOM(n) ((noun)(n) << 1) #define GET_DIRECT_ATOM(n) ((n) >> 1) // Simple cell structure typedef struct cell_s { noun head; noun tail; } cell_t; #define CELL_PTR(n) ((cell_t*)((n) & ~1ULL)) #define MAKE_CELL(ptr) ((noun)ptr | 1) static noun make_atom(uint64_t val) { return MAKE_DIRECT_ATOM(val); } static noun make_cell(noun head, noun tail) { cell_t* c = malloc(sizeof(cell_t)); c->head = head; c->tail = tail; return MAKE_CELL(c); } static noun head(noun n) { return CELL_PTR(n)->head; } static noun tail(noun n) { return CELL_PTR(n)->tail; } // Simplified slot (no error handling) static noun slot(uint64_t axis, noun n) { if (axis == 1) return n; if (axis & 1) { // Odd: go right (tail) return slot(axis >> 1, tail(n)); } else { // Even: go left (head) return slot(axis >> 1, head(n)); } } // Ultra-simplified nock (only handles the opcodes we benchmark) static noun nock_simple(noun subject, noun formula) { if (IS_CELL(formula) && IS_CELL(head(formula))) { // Cell construction: [[a b] c] -> [nock(a) nock(c)] noun h = nock_simple(subject, head(formula)); noun t = nock_simple(subject, tail(formula)); return make_cell(h, t); } if (!IS_CELL(formula)) return 0; // Error noun op = head(formula); noun arg = tail(formula); if (!IS_ATOM(op)) return 0; // Error uint64_t opcode = GET_DIRECT_ATOM(op); switch (opcode) { case 0: // slot if (!IS_ATOM(arg)) return 0; return slot(GET_DIRECT_ATOM(arg), subject); case 1: // constant return arg; case 3: { // is-cell noun val = nock_simple(subject, arg); return IS_CELL(val) ? make_atom(0) : make_atom(1); } case 4: { // increment noun val = nock_simple(subject, arg); if (!IS_ATOM(val)) return 0; return MAKE_DIRECT_ATOM(GET_DIRECT_ATOM(val) + 1); } case 5: { // equality noun pair = nock_simple(subject, arg); noun a = head(pair); noun b = tail(pair); // Simplified: only works for direct atoms if (IS_ATOM(a) && IS_ATOM(b)) { return (a == b) ? make_atom(0) : make_atom(1); } return make_atom(1); } case 6: { // if-then-else noun test_fol = head(arg); noun yes_fol = head(tail(arg)); noun no_fol = tail(tail(arg)); noun test_val = nock_simple(subject, test_fol); uint64_t test = GET_DIRECT_ATOM(test_val); if (test == 0) { return nock_simple(subject, yes_fol); } else { return nock_simple(subject, no_fol); } } case 7: { // composition noun b = head(arg); noun c = tail(arg); noun new_subj = nock_simple(subject, b); return nock_simple(new_subj, c); } case 8: { // push noun b = head(arg); noun c = tail(arg); noun val = nock_simple(subject, b); noun new_subj = make_cell(val, subject); return nock_simple(new_subj, c); } default: return 0; // Error } } static double get_time_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec * 1000.0) + (tv.tv_usec / 1000.0); } static void bench(const char* name, noun subject, noun formula, int iterations) { double start = get_time_ms(); for (int i = 0; i < iterations; i++) { noun _result = nock_simple(subject, formula); // Note: we're leaking memory here, but it's fine for a benchmark } double end = get_time_ms(); double total = end - start; double per_iter = total / iterations; printf("%-30s %8d iterations in %10.2f ms (%10.6f ms/iter, %10.0f ops/sec)\n", name, iterations, total, per_iter, 1000.0 / per_iter); } int main() { printf("Nock Benchmark - Simple C Implementation\n"); printf("=========================================\n"); printf("(Simplified version without full Vere infrastructure)\n\n"); int iterations = 1000000; int slow_iters = 100000; // Benchmark 0: slot bench("Opcode 0: slot/fragment", make_cell(make_atom(42), make_atom(99)), make_cell(make_atom(0), make_atom(2)), iterations); // Benchmark 1: constant bench("Opcode 1: constant", make_atom(0), make_cell(make_atom(1), make_atom(42)), iterations); // Benchmark 3: is-cell bench("Opcode 3: is-cell (atom)", make_atom(0), make_cell(make_atom(3), make_cell(make_atom(1), make_atom(42))), iterations); // Benchmark 4: increment bench("Opcode 4: increment", make_atom(0), make_cell(make_atom(4), make_cell(make_atom(1), make_atom(1000))), iterations); // Benchmark 5: equality bench("Opcode 5: equality (equal)", make_atom(0), make_cell(make_atom(5), make_cell(make_cell(make_atom(1), make_atom(42)), make_cell(make_atom(1), make_atom(42)))), iterations); // Benchmark 6: if-then-else bench("Opcode 6: if-then-else", make_atom(0), make_cell(make_atom(6), make_cell(make_cell(make_atom(1), make_atom(0)), make_cell(make_cell(make_atom(1), make_atom(11)), make_cell(make_atom(1), make_atom(22))))), iterations); // Benchmark 7: composition bench("Opcode 7: composition", make_atom(42), make_cell(make_atom(7), make_cell(make_cell(make_atom(1), make_atom(99)), make_cell(make_atom(0), make_atom(1)))), iterations); // Benchmark 8: push bench("Opcode 8: push", make_atom(42), make_cell(make_atom(8), make_cell(make_cell(make_atom(1), make_atom(99)), make_cell(make_atom(0), make_atom(1)))), iterations); // Cell construction bench("Cell construction", make_atom(0), make_cell(make_cell(make_atom(1), make_atom(1)), make_cell(make_atom(1), make_atom(2))), iterations); // Deep slot lookup bench("Deep slot lookup (depth 4)", make_cell(make_cell(make_cell(make_cell(make_atom(1), make_atom(2)), make_atom(3)), make_atom(4)), make_atom(5)), make_cell(make_atom(0), make_atom(16)), iterations); printf("\n"); return 0; }