/* Return a well formatted string with a time difference at millisecond resolution */ char * elapsed_time (struct timeval * start, struct timeval * stop) { static char et [64]; time_t elapsed = delta_time_in_milliseconds (stop, start); if (time_day (elapsed)) sprintf (et, "%d days, %02d:%02d:%02d.%03ld", time_day (elapsed), time_hour (elapsed), time_min (elapsed), time_sec (elapsed), time_usec (elapsed)); else if (time_hour (elapsed)) sprintf (et, "%02d:%02d:%02d.%03ld", time_hour (elapsed), time_min (elapsed), time_sec (elapsed), time_usec (elapsed)); else if (time_min (elapsed)) sprintf (et, "%02d:%02d.%03ld", time_min (elapsed), time_sec (elapsed), time_usec (elapsed)); else if (time_sec (elapsed)) sprintf (et, "%d.%03d secs", time_sec (elapsed), time_msec (elapsed)); else sprintf (et, "%3d msecs", time_msec (elapsed)); return et; }
static int sane_apicid(struct cpuid_state_t *state) { int ret = 0; uint32_t hwthreads = thread_count_native(NULL), i, c, worker_count, oldbinding; uint8_t *apic_ids = NULL, *apic_copy = NULL, worker_flag; struct apic_validate_t *apic_state = NULL; double start, now; thread_handle_t *busy_workers = NULL, *apic_workers = NULL; worker_count = hwthreads / 4 + 1; /* Store the current affinity mask. It will get clobbered. */ oldbinding = thread_get_binding(); printf("Verifying APIC ID sanity"); /* Populate initial APIC ID array. */ apic_ids = malloc(hwthreads * sizeof(unsigned char)); for (i = 0; i < hwthreads; i++) { apic_ids[i] = get_apicid_for_cpu(state, i); } /* First verify that no CPUs reported identical APIC IDs. */ apic_copy = malloc(hwthreads * sizeof(unsigned char)); memcpy(apic_copy, apic_ids, hwthreads * sizeof(unsigned char)); qsort(apic_copy, hwthreads, sizeof(unsigned char), apic_compare); for (i = 1; i < hwthreads; i++) { if (apic_ids[i - 1] == apic_ids[i]) { printf("fail (duplicate APIC IDs)\n"); ret = 1; goto cleanup; } } free(apic_copy); /* Spawn a few busy threads to incur thread migrations, if they're going to happen at all. */ worker_flag = 1; busy_workers = malloc(worker_count * sizeof(thread_handle_t)); for (i = 0; i < worker_count; i++) #ifdef TARGET_OS_WINDOWS busy_workers[i] = CreateThread(NULL, 0, apic_nonsensical_worker_thread, &worker_flag, 0, NULL); #else pthread_create(&busy_workers[i], NULL, apic_nonsensical_worker_thread, &worker_flag); #endif /* Now verify that the APIC IDs don't change over time. */ apic_state = malloc(hwthreads * sizeof(struct apic_validate_t)); apic_workers = malloc(hwthreads * sizeof(thread_handle_t)); memset(apic_state, 0, hwthreads * sizeof(struct apic_validate_t)); for (i = 0; i < hwthreads; i++) { apic_state[i].state = state; apic_state[i].index = i; apic_state[i].expected = apic_ids[i]; apic_state[i].worker_flag = &worker_flag; #ifdef TARGET_OS_WINDOWS apic_workers[i] = CreateThread(NULL, 0, apic_validation_thread, &apic_state[i], 0, NULL); #else pthread_create(&apic_workers[i], NULL, apic_validation_thread, &apic_state[i]); #endif } free(apic_ids); /* Occasionally signal workers to run validation checks. */ start = time_sec(); c = 1; printf("."); fflush(stdout); while(worker_flag) { now = time_sec(); if (now - start > 30.0) break; if (c % 100 == 0) { c = 1; printf("."); fflush(stdout); } else { c++; } #ifdef TARGET_OS_WINDOWS Sleep(10); #else usleep(10000); #endif for (i = 0; i < hwthreads; i++) { if (apic_state[i].failed) { printf(" failed (APIC IDs changed over time)\n"); ret = 2; goto cleanup; } } } printf(" ok\n"); cleanup: /* Wait for workers to exit. */ worker_flag = 0; if (busy_workers) { #ifdef TARGET_OS_WINDOWS WaitForMultipleObjects(worker_count, busy_workers, TRUE, INFINITE); for (i = 0; i < worker_count; i++) CloseHandle(busy_workers[i]); #else for (i = 0; i < worker_count; i++) { pthread_join(busy_workers[i], NULL); } #endif free(busy_workers); } if (apic_workers) { #ifdef TARGET_OS_WINDOWS WaitForMultipleObjects(hwthreads, apic_workers, TRUE, INFINITE); for (i = 0; i < hwthreads; i++) CloseHandle(apic_workers[i]); #else for (i = 0; i < hwthreads; i++) { pthread_join(apic_workers[i], NULL); } #endif free(apic_workers); } if (apic_state) free(apic_state); /* Restore the affinity mask from before. */ thread_bind_mask(oldbinding); return ret; }
int main(int argc, char **argv) { struct time_type time; ZOOM_connection *z; ZOOM_resultset *r; int *elc; struct event_line_t *els; ZOOM_options o; int i; int k; init_statics(); read_params(argc, argv, ¶meters); z = (ZOOM_connection *) xmalloc(sizeof(*z) * parameters.concurrent); r = (ZOOM_resultset *) xmalloc(sizeof(*r) * parameters.concurrent); elc = (int *) xmalloc(sizeof(*elc) * parameters.concurrent * parameters.repeat); els = (struct event_line_t *) xmalloc( sizeof(*els) * parameters.concurrent * parameters.repeat * 10); o = ZOOM_options_create(); /* async mode */ ZOOM_options_set (o, "async", "1"); /* get first record of result set (using piggypack) */ if (parameters.piggypack) ZOOM_options_set (o, "count", "1"); /* set proxy */ if (strlen(parameters.proxy)) ZOOM_options_set (o, "proxy", parameters.proxy); /* preferred record syntax */ if (0){ ZOOM_options_set (o, "preferredRecordSyntax", "usmarc"); ZOOM_options_set (o, "elementSetName", "F"); } time_init(&time); /* repeat loop */ for (k = 0; k < parameters.repeat; k++){ /* progress zeroing */ for (i = 0; i < 4096; i++){ parameters.progress[i] = k * 5 -1; } /* connect to all concurrent connections*/ for ( i = 0; i < parameters.concurrent; i++){ /* set event count to zero */ elc[k * parameters.concurrent + i] = 0; /* create connection - pass options (they are the same for all) */ z[i] = ZOOM_connection_create(o); /* connect and init */ ZOOM_connection_connect(z[i], parameters.host, 0); } /* search all */ for (i = 0; i < parameters.concurrent; i++) r[i] = ZOOM_connection_search_pqf (z[i], parameters.query); /* network I/O. pass number of connections and array of connections */ while ((i = ZOOM_event (parameters.concurrent, z))){ int event = ZOOM_connection_last_event(z[i-1]); const char *errmsg; const char *addinfo; int error = 0; //int progress = zoom_progress[event]; if (event == ZOOM_EVENT_SEND_DATA || event == ZOOM_EVENT_RECV_DATA) continue; time_stamp(&time); /* updating events and event list */ error = ZOOM_connection_error(z[i-1] , &errmsg, &addinfo); if (error) parameters.progress[i] = zoom_progress[ZOOM_EVENT_UNKNOWN]; //parameters.progress[i] = zoom_progress[ZOOM_EVENT_NONE]; else if (event == ZOOM_EVENT_CONNECT) parameters.progress[i] = zoom_progress[event]; else //parameters.progress[i] = zoom_progress[event]; parameters.progress[i] += 1; update_events(elc, els, k, i-1, time_sec(&time), time_usec(&time), parameters.progress[i], event, zoom_events[event], error, errmsg); } /* destroy connections */ for (i = 0; i<parameters.concurrent; i++) { ZOOM_resultset_destroy (r[i]); ZOOM_connection_destroy (z[i]); } } /* for (k = 0; k < parameters.repeat; k++) repeat loop */ /* output */ if (parameters.gnuplot){ printf("# gnuplot data and instruction file \n"); printf("# gnuplot thisfile \n"); printf("\n"); printf("set title \"Z39.50 connection plot\"\n"); printf("set xlabel \"Connection\"\n"); printf("set ylabel \"Time Seconds\"\n"); printf("set zlabel \"Progress\"\n"); printf("set ticslevel 0\n"); printf("set grid\n"); printf("set pm3d\n"); printf("splot '-' using ($1):($2):($3) t '' with points\n"); printf("\n"); printf("\n"); } print_table_header(); print_events(elc, els, parameters.concurrent); if (parameters.gnuplot){ printf("end\n"); printf("pause -1 \"Hit ENTER to return\"\n"); } /* destroy data structures and exit */ xfree(z); xfree(r); xfree(elc); xfree(els); ZOOM_options_destroy(o); exit (0); }
int main(int argc, char *argv[]) { enum { MAX_RETRIES = 100 }; enum { SLEEP_US = 1000 }; bool attach = false; _xcc_status cc; bool client = false; int count; int count_read; int count_written; bool dif = false; double dloop; double dms; double dsec; int err; bool exec = false; int ferr; bool fin = false; MS_Mon_Process_Info_Type info; MS_Mon_Process_Info_Type *infop; int inx; int inx2; int inx3; short len; short lerr; short lerr2; int loop = 10; int max; int nid; pid_t pid; int sargc; ssize_t size; int snid; int spid; bool startup = false; xzsys_ddl_smsg_def *sys_msgp = (xzsys_ddl_smsg_def *) recv_buffer; int sys_msg; int sys_msg_count; bool verbose = false; TAD zargs[] = { { "-attach", TA_Bool, TA_NOMAX, &attach }, { "-client", TA_Bool, TA_NOMAX, &client }, { "-dif", TA_Bool, TA_NOMAX, &dif }, { "-exec", TA_Bool, TA_NOMAX, &exec }, { "-loop", TA_Int, TA_NOMAX, &loop }, { "-maxsp", TA_Int, TA_NOMAX, &maxsp }, { "-server", TA_Ign, TA_NOMAX, NULL }, { "-startup", TA_Bool, TA_NOMAX, &startup }, { "-trace", TA_Bool, TA_NOMAX, &trace }, { "-v", TA_Bool, TA_NOMAX, &verbose }, { "-verbose", TA_Ign, TA_NOMAX, NULL }, { "", TA_End, TA_NOMAX, NULL } }; arg_proc_args(zargs, false, argc, argv); sprintf(fifo1, "%s-%s", FIFO1, getenv("USER")); sprintf(fifo2, "%s-%s", FIFO2, getenv("USER")); if (trace) msg_init_trace(); if (exec) return 0; if (startup) { err = fifo_open(fifo1, O_WRONLY); assert(err != -1); ffds[1] = err; err = fifo_open(fifo2, O_RDONLY); assert(err != -1); ffds[0] = err; if (trace) trace_printf("cli: writing fifo\n"); size = write(ffds[1], recv_buffer, 1); if (trace) trace_printf("cli: fifo write, size=%d\n", (int) size); assert(size == 1); if (trace) trace_printf("cli: fifo written\n"); close(ffds[1]); return 0; } if (attach) ferr = file_init_attach(&argc, &argv, false, NULL); else ferr = file_init(&argc, &argv); TEST_CHK_FEOK(ferr); util_test_start(client); ferr = msg_mon_process_startup(true); // system messages util_check("msg_mon_process_startup", ferr); ferr = msg_mon_get_my_process_name(procname, BUFSIZ); util_check("msg_mon_get_my_process_name", ferr); ferr = msg_mon_get_process_info(procname, &nid, &pid); TEST_CHK_FEOK(ferr); if (trace) trace_printf("proc=%s, nid=%d, pid=%d\n", procname, nid, pid); dloop = (double) loop; for (inx = 0; inx < T_MAX; inx++) t_elapsed[inx] = 0.0; if (client) { printf("loop=%d, maxsp=%d\n", loop, maxsp); sargc = argc; assert(sargc < MAX_ARGS); for (inx2 = 0; inx2 < argc; inx2++) { if (strcmp(argv[inx2], "-client") == 0) sargv[inx2] = (char *) "-server"; else sargv[inx2] = argv[inx2]; if (strcmp(argv[inx2], "-attach") == 0) sargv[inx2] = (char *) "-server"; } sargv[argc] = NULL; sprintf(sprog, "%s/%s", getenv("PWD"), argv[0]); time_start(T_TOTAL); for (inx = 0; inx < loop; inx += maxsp) { if (dif) snid = -1; else snid = nid; max = loop - inx; if (max > maxsp) max = maxsp; for (inx2 = 0; inx2 < max; inx2++) sname[inx2][0] = 0; // mon picks name if (trace) trace_printf("cli: newproc, inx=%d\n", inx); time_start(T_NEWPROC); for (inx2 = 0; inx2 < max; inx2++) { ferr = msg_mon_start_process(sprog, // prog sname[inx2], // name sname[inx2], // ret_name sargc, // argc sargv, // argv TPT_REF2(sphandle,inx2),// phandle false, // open &soid[inx2], // oid MS_ProcessType_Generic, // type 0, // priority false, // debug false, // backup &snid, // nid &spid, // pid NULL, // infile NULL); // outfile TEST_CHK_FEOK(ferr); } time_stop(T_NEWPROC); time_elapsed(T_NEWPROC); // wait here until processes are 'up' // so that open timing is correct inx3 = 0; for (inx2 = 0; inx2 < max; inx2++) { ferr = msg_mon_get_process_info_detail(sname[inx2], &info); TEST_CHK_FEOK(ferr); if (info.state != MS_Mon_State_Up) { inx3++; if (inx3 > MAX_RETRIES) { printf("process %s did not enter 'UP' state\n", sname[inx2]); assert(inx3 < MAX_RETRIES); } usleep(SLEEP_US); inx2--; continue; } else inx3 = 0; } if (trace) trace_printf("cli: open, inx=%d\n", inx); time_start(T_OPEN); for (inx2 = 0; inx2 < max; inx2++) { if (trace) trace_printf("cli: opening inx=%d, name=%s\n", inx, sname[inx2]); len = (short) strlen(sname[inx2]); ferr = BFILE_OPEN_(sname[inx2], len, &sfilenum[inx2], 0, 0, 0, 0, 0, 0, 0); if (trace) trace_printf("cli: open, inx=%d, name=%s, ferr=%d\n", inx, sname[inx2], ferr); TEST_CHK_FEOK(ferr); } time_stop(T_OPEN); time_elapsed(T_OPEN); if (trace) trace_printf("cli: procinfo, inx=%d\n", inx); time_start(T_PROCINFO); for (inx2 = 0; inx2 < max; inx2++) { ferr = msg_mon_get_process_info_detail(sname[inx2], &info); TEST_CHK_FEOK(ferr); } time_stop(T_PROCINFO); time_elapsed(T_PROCINFO); if (trace) trace_printf("cli: procinfo-type, inx=%d\n", inx); time_start(T_PROCINFO_TYPE); ferr = msg_mon_get_process_info_type(MS_ProcessType_Generic, &count, MAX_SRV, infotype); TEST_CHK_FEOK(ferr); time_stop(T_PROCINFO_TYPE); time_elapsed(T_PROCINFO_TYPE); if (verbose) { for (inx2 = 0; inx2 < count; inx2++) { infop = &infotype[inx2]; char s_em = infop->event_messages ? 'E' : '-'; char s_sm = infop->system_messages ? 'S' : '-'; char s_pr = infop->pending_replication ? 'R' : '-'; char s_pd = infop->pending_delete ? 'D' : '-'; char s_s = infop->state == MS_Mon_State_Up ? 'A' : 'U'; char s_o = infop->opened ? 'O' : '-'; char s_p = infop->paired ? 'P' : infop->backup ? 'B' : '-'; printf("%3.3d,%8.8d %3.3d %d %c%c%c%c%c%c%c %-11s %-11s %-15s\n", infop->nid, infop->pid, infop->priority, infop->state, s_em, s_sm, s_pr, s_pd, s_s, s_o, s_p, infop->process_name, infop->parent_name, infop->program); } } if (trace) trace_printf("cli: close, inx=%d\n", inx); time_start(T_CLOSE); for (inx2 = 0; inx2 < max; inx2++) { ferr = BFILE_CLOSE_(sfilenum[inx2]); TEST_CHK_FEOK(ferr); } time_stop(T_CLOSE); time_elapsed(T_CLOSE); // re-open/close for (inx2 = 0; inx2 < max; inx2++) { if (trace) trace_printf("cli: re-opening inx=%d, name=%s\n", inx, sname[inx2]); len = (short) strlen(sname[inx2]); ferr = BFILE_OPEN_(sname[inx2], len, &sfilenum[inx2], 0, 0, 0, 0, 0, 0, 0); TEST_CHK_FEOK(ferr); } if (trace) trace_printf("cli: re-close, inx=%d\n", inx); for (inx2 = 0; inx2 < max; inx2++) { ferr = BFILE_CLOSE_(sfilenum[inx2]); TEST_CHK_FEOK(ferr); } if (trace) trace_printf("cli: newproc-forkexec, inx=%d\n", inx); sargc = 2; sargv[0] = argv[0]; sargv[1] = (char *) "-exec"; if (trace) sargv[sargc++] = (char *) "-trace"; sargv[sargc] = NULL; time_start(T_FORKEXEC); for (inx2 = 0; inx2 < max; inx2++) { pid = fork(); assert(pid >= 0); if (pid == 0) { // child err = execv(sprog, sargv); assert(err == 0); } } time_stop(T_FORKEXEC); time_elapsed(T_FORKEXEC); if (trace) trace_printf("cli: newproc-forkexec-su, inx=%d\n", inx); sargc = 2; sargv[0] = argv[0]; sargv[1] = (char *) "-startup"; if (trace) sargv[sargc++] = (char *) "-trace"; sargv[sargc] = NULL; time_start(T_FORKEXEC_SU); for (inx2 = 0; inx2 < max; inx2++) { fifo_create(fifo1, fifo2); pid = fork(); assert(pid >= 0); if (pid > 0) { // parent err = fifo_open(fifo1, O_RDONLY); assert(err != -1); ffds[0] = err; err = fifo_open(fifo2, O_WRONLY); assert(err != -1); ffds[1] = err; if (trace) trace_printf("cli: reading fifo, inx=%d\n", inx2); size = ::read(ffds[0], recv_buffer, 1); if (trace) trace_printf("cli: fifo read, size=%d\n", (int) size); assert(size == 1); if (trace) trace_printf("cli: fifo read, inx=%d\n", inx2); ::read(ffds[0], recv_buffer, 1); err = fifo_close(ffds[0]); assert(err == 0); err = fifo_close(ffds[1]); assert(err == 0); fifo_destroy(fifo1, fifo1); } else { // child err = execv(sprog, sargv); assert(err == 0); } } fifo_destroy(fifo2, fifo2); time_stop(T_FORKEXEC_SU); time_elapsed(T_FORKEXEC_SU); } } else { sys_msg_count = 0; time_start(T_TOTAL); ferr = BFILE_OPEN_((char *) "$RECEIVE", 8, &filenumr, 0, 0, 0, 1, 0); // sys msgs TEST_CHK_FEOK(ferr); for (inx = 0; !fin; inx++) { if (trace) trace_printf("srv: readupdate, inx=%d\n", inx); cc = BREADUPDATEX(filenumr, recv_buffer, 4, &count_read, 0); sys_msg = _xstatus_ne(cc); if (trace && sys_msg) trace_printf("srv: rcvd sys msg=%d\n", sys_msgp->u_z_msg.z_msgnumber[0]); if (sys_msg) { sys_msg_count++; inx--; } lerr2 = BFILE_GETINFO_(filenumr, &lerr); TEST_CHK_FEIGNORE(lerr2); if (trace) trace_printf("srv: reply, inx=%d\n", inx); cc = BREPLYX(recv_buffer, (unsigned short) 0, &count_written, 0, XZFIL_ERR_OK); TEST_CHK_CCEQ(cc); if (sys_msg_count >= 4) fin = true; } } time_stop(T_TOTAL); time_elapsed(T_TOTAL); if (client) { dsec = time_sec(T_TOTAL); dms = dsec * 1000.0; printf("elapsed=%f\n", dms); printf("open/close/newprocess/processinfo/forkexec=%d\n", loop); dsec = time_sec(T_OPEN); dms = dsec * 1000.0; printf("open : total-time=%f ms, time/loop=%f ms, ops/sec=%f\n", dms, dms / dloop, dloop / dsec); dsec = time_sec(T_CLOSE); dms = dsec * 1000.0; printf("close : total-time=%f ms, time/loop=%f ms, ops/sec=%f\n", dms, dms / dloop, dloop / dsec); dsec = time_sec(T_PROCINFO); dms = dsec * 1000.0; printf("procinfo : total-time=%f ms, time/loop=%f ms, ops/sec=%f\n", dms, dms / dloop, dloop / dsec); dsec = time_sec(T_PROCINFO_TYPE); dms = dsec * 1000.0; printf("procinfo-type : total-time=%f ms, time/loop=%f ms, ops/sec=%f\n", dms, dms / dloop, dloop / dsec); dsec = time_sec(T_NEWPROC); dms = dsec * 1000.0; printf("newproc : total-time=%f ms, time/loop=%f ms, ops/sec=%f\n", dms, dms / dloop, dloop / dsec); dsec = time_sec(T_FORKEXEC); dms = dsec * 1000.0; printf("forkexec : total-time=%f ms, time/loop=%f ms, ops/sec=%f\n", dms, dms / dloop, dloop / dsec); dsec = time_sec(T_FORKEXEC_SU); dms = dsec * 1000.0; printf("forkexec-startup: total-time=%f ms, time/loop=%f ms, ops/sec=%f\n", dms, dms / dloop, dloop / dsec); } ferr = msg_mon_process_shutdown(); TEST_CHK_FEOK(ferr); util_test_finish(client); return 0; }
void runBenchmark(enum BenchmarkType benchmarkType, int32_t size, struct BenchmarkResult* out) { const int32_t experiments = 10; struct BenchmarkResult result = { .time = __builtin_nan(""), .flops = __builtin_nan(""), .throughput = __builtin_nan("") }; switch (benchmarkType) { case BenchmarkTypeNaiveDGEMM: { double* a = malloc(size * size * sizeof(double)); double* b = malloc(size * size * sizeof(double)); double* c = malloc(size * size * sizeof(double)); for (int32_t i = 0; i < size * size; i++) { a[i] = ((double) rand()) / ((double) RAND_MAX); b[i] = ((double) rand()) / ((double) RAND_MAX); } memset(c, 0, size * size * sizeof(double)); for (int32_t experiment = 0; experiment < experiments; ++experiment) { const double timeStart = time_sec(); dgemm_naive(size, a, b, c); result.time = fmin(result.time, time_sec() - timeStart); } result.flops = 2.0 * size * size * size / result.time; free(a); free(b); free(c); break; } case BenchmarkTypeBlockedDGEMM: { double* a = malloc(size * size * sizeof(double)); double* b = malloc(size * size * sizeof(double)); double* c = malloc(size * size * sizeof(double)); for (int32_t i = 0; i < size * size; i++) { a[i] = ((double) rand()) / ((double) RAND_MAX); b[i] = ((double) rand()) / ((double) RAND_MAX); } memset(c, 0, size * size * sizeof(double)); for (int32_t experiment = 0; experiment < experiments; ++experiment) { const double timeStart = time_sec(); dgemm_blocked(size, a, b, c); result.time = fmin(result.time, time_sec() - timeStart); } result.flops = 2.0 * size * size * size / result.time; free(a); free(b); free(c); break; } case BenchmarkTypeBlisDGEMM: { obj_t alpha, beta; bli_obj_scalar_init_detached(BLIS_DOUBLE, &alpha); bli_obj_scalar_init_detached(BLIS_DOUBLE, &beta); bli_setsc( 1.0, 0.0, &alpha); bli_setsc( 0.0, 0.0, &beta); obj_t a, b, c; bli_obj_create(BLIS_DOUBLE, size, size, 0, 0, &a); bli_obj_create(BLIS_DOUBLE, size, size, 0, 0, &b); bli_obj_create(BLIS_DOUBLE, size, size, 0, 0, &c); bli_randm(&a); bli_randm(&b); bli_randm(&c); for (int32_t i = 0; i < experiments; ++i) { const double timeStart = time_sec(); bli_gemm(&alpha, &a, &b, &beta, &c); result.time = fmin(result.time, time_sec() - timeStart); } result.flops = 2.0 * size * size * size / result.time; bli_obj_free(&a); bli_obj_free(&b); bli_obj_free(&c); break; } case BenchmarkTypePointerChasing: { struct xor_shift xor_shift = xor_shift_init(UINT32_C(1), __builtin_ctz(size)); uint32_t last_index = 1; void** data = (void**) malloc(size * sizeof(void*)); data[0] = &data[1]; for (size_t i = 0; i < size; i++) { const uint32_t index = xor_shift_next(&xor_shift); data[last_index] = &data[index]; last_index = index; } const int32_t iterations = 16777216 / size; for (int32_t experiment = 0; experiment < experiments; ++experiment) { const double timeStart = time_sec(); for (int32_t iteration = 0; iteration < iterations; iteration++) { chase_pointers(data); } result.time = fmin(result.time, (time_sec() - timeStart) / ((double) iterations) / ((double) size)); } free((void*) data); break; } default: __builtin_unreachable(); } *out = result; }