#define _POSIX_C_SOURCE 200809L // #include #include #include #include #include #include // -- disable use of compiler intrinsics // #define PU3_NO_INTRINSICS // -- inaccurate clocks // #define PU3_BAD_CLOCK #ifndef PU3_BAD_CLOCK clock_t ntime() { struct timespec t; clock_gettime(CLOCK_MONOTONIC_RAW, &t); return t.tv_nsec + t.tv_sec * 1e9; } #else clock_t ntime() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000000 + tv.tv_usec * 1000; } #endif // it is time // the thing you've none been waiting for // pu3 typedef char *string; typedef unsigned char byte; typedef byte bool; typedef struct { byte __spare[ 1 ]; byte op; uint16_t data; uint32_t dest; } pu3_inst_t; typedef union { pu3_inst_t inst_struct; uint64_t inst_num; } pu3_inst_u; typedef struct { pu3_inst_u *memory; uint32_t mem_size; uint32_t *pc; uint32_t *time_slice; } pu3_ctx; // instructions // 0: nop // 1: halt // 2: mov [target], src pu3(pu3_ctx ctx) { static uint32_t i; static pu3_inst_t cur; pu3_inst_u *pc = ctx.memory + *ctx.pc; static const __attribute__((aligned(64))) void *GOTO_TABLE[] = { &&inst_0, &&pu3_end, &&inst_2, }; pu3_start: cur = pc->inst_struct; pc++; #ifndef PU3_NO_INTRINSICS __builtin_prefetch(pc + 1, 0, 3); #endif #define get_data() ((cur.op & 0b10000000) ? (ctx.memory[ cur.data ].inst_num) : (cur.data)) // 1 - 4: opcode // 5: data_type [0 = constant, 1 = memory] // [5] [ ] [ ] [ ] [1] [2] [3] [4] goto *GOTO_TABLE[ cur.op & 0b1111 ]; inst_0: // nop pc += cur.dest; goto pu3_start; inst_1: // halt // unused, we just jump to break inst_2: // mov ctx.memory[ cur.dest ].inst_num = get_data(); goto pu3_start; pu3_end: *ctx.pc = pc - ctx.memory; return *ctx.time_slice - i; } const string op_names[] = { "nop", "halt", "mov" }; // bench: measure using clock() double bench_cycle(byte instr, pu3_ctx ctx) { int i; for (i = 16; i < ctx.mem_size; i++) { ctx.memory[ i ].inst_struct.op = instr & 0b1111; ctx.memory[ i ].inst_struct.dest = 5; ctx.memory[ i ].inst_struct.data = 6; if (i == ctx.mem_size - 1) ctx.memory[ i ].inst_struct.op = 1; } ctx.memory[ i ].inst_struct.op = 0; *ctx.pc = 8; clock_t start = ntime(); // int wstart = rdtsc(); pu3(ctx); // int wend = rdtsc(); clock_t end = ntime(); double nanoseconds = (end - start); double inst_count = *ctx.pc - 8; double insts_per_ns = inst_count / nanoseconds; double insts_per_s = insts_per_ns * 1e9; printf( "%-8s | time: %.3fms | insts: %.0f (%li real) | ips: %.3fM inst/s\n", op_names[ instr ], nanoseconds / 1000000, inst_count, end - start, insts_per_ns * 1e3); return insts_per_s / 1e6; // return M inst/s } main() { static const uint32_t SIZE = 1 << 24; pu3_ctx ctx = { .memory = calloc(SIZE, sizeof(pu3_inst_u)), .mem_size = SIZE, .pc = calloc(1, sizeof(uint32_t)), .time_slice = calloc(1, sizeof(uint32_t)) }; bench_cycle(2, ctx); }