int main() { unsigned long delay_tlbmiss; unsigned long long cpms; cpms = cycles_per_ms(); printf("%-20s %6s %8s %10s %12s %24s\n", "Test Name", "Stride", "NumOps", "Iterations", "Time/ms", "Cycles/Iteration"); printf("%-20s %6s %8s %10s %12s %24s\n", "---------", "------", "------", "----------", "-------", "----------------"); delay_tlbmiss = measure_latency(cpms); printf("Report\n------\n"); printf("Delay from TLB Miss: %lu clockcycles\n", delay_tlbmiss); exit(0); }
int main(){ const int nrows = 12; int nlist[nrows] = {1, 2, 4, 8, 16, 24, 32, 40, 60, 1000, 10*1000, 100*1000}; const char *rows[nrows] = {"1", "2", "4", "8", "16", "24", "32", "40", "60", "1000", "10*1000", "100*1000"}; const int ncols = 3; const char *cols[ncols] = {"min", "median", "max"}; double cyc2dram[nrows*ncols]; StatVector stats(NTRIALS); for(int i=0; i < nrows; i++){ measure_latency(nlist[i], stats); cyc2dram[i+0*nrows] = stats.min(); cyc2dram[i+1*nrows] = stats.median(); cyc2dram[i+2*nrows] = stats.max(); } verify_dir("DBG"); #ifdef MEMWALK const char* fname = "DBG/latency_memwalk.txt"; #else const char* fname = "DBG/latency_clflush.txt"; #endif link_cout(fname); Table tbl; tbl.dim(nrows, ncols); tbl.rows(rows); tbl.cols(cols); tbl.data(cyc2dram); char banner[200]; sprintf(banner, "cycles to dram, ntrials = %d, when N pages accessed", NTRIALS); tbl.print(banner); unlink_cout(); }
// Runs a latency test and reports the results as JSON written to the given // connection. static void report_latency(struct mg_connection *connection, const uint8_t magic_pattern[]) { double key_down_latency_ms = 0; double scroll_latency_ms = 0; double max_js_pause_time_ms = 0; double max_css_pause_time_ms = 0; double max_scroll_pause_time_ms = 0; char *error = "Unknown error."; if (!measure_latency(magic_pattern, &key_down_latency_ms, &scroll_latency_ms, &max_js_pause_time_ms, &max_css_pause_time_ms, &max_scroll_pause_time_ms, &error)) { // Report generic error. debug_log("measure_latency reported error: %s", error); mg_printf(connection, "HTTP/1.1 500 Internal Server Error\r\n" "Access-Control-Allow-Origin: *\r\n" "Content-Type: text/plain\r\n\r\n" "%s", error); } else { // Send the measured latency information back as JSON. mg_printf(connection, "HTTP/1.1 200 OK\r\n" "Access-Control-Allow-Origin: *\r\n" "Cache-Control: no-cache\r\n" "Content-Type: text/plain\r\n\r\n" "{ \"keyDownLatencyMs\": %f, " "\"scrollLatencyMs\": %f, " "\"maxJSPauseTimeMs\": %f, " "\"maxCssPauseTimeMs\": %f, " "\"maxScrollPauseTimeMs\": %f}", key_down_latency_ms, scroll_latency_ms, max_js_pause_time_ms, max_css_pause_time_ms, max_scroll_pause_time_ms); } }
// Main test function. Locates the given magic pixel pattern on the screen, then // runs one full latency test, sending input events and recording responses. On // success, the results of the test are reported in the output parameters, and // true is returned. If the test fails, the error parameter is filled in with // an error message and false is returned. bool measure_latency( const uint8_t magic_pattern[], double *out_key_down_latency_ms, double *out_scroll_latency_ms, double *out_max_js_pause_time_ms, double *out_max_css_pause_time_ms, double *out_max_scroll_pause_time_ms, char **error) { screenshot *screenshot = take_screenshot(0, 0, UINT32_MAX, UINT32_MAX); if (!screenshot) { *error = "Failed to take screenshot."; return false; } assert(screenshot->width > 0 && screenshot->height > 0); size_t x, y; bool found_pattern = find_pattern(magic_pattern, screenshot, &x, &y); free_screenshot(screenshot); if (!found_pattern) { *error = "Failed to find test pattern on screen."; return false; } uint8_t full_pattern[pattern_bytes]; for (int i = 0; i < pattern_magic_bytes; i++) { full_pattern[i] = magic_pattern[i]; } measurement_t measurement; measurement_t previous_measurement; memset(&measurement, 0, sizeof(measurement_t)); memset(&previous_measurement, 0, sizeof(measurement_t)); int screenshots = 0; bool first_screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!first_screenshot_successful) { *error = "Failed to read data from test pattern."; return false; } if (measurement.test_mode == TEST_MODE_NATIVE_REFERENCE) { uint8_t *test_pattern = (uint8_t *)malloc(pattern_bytes); memset(test_pattern, 0, pattern_bytes); for (int i = 0; i < pattern_magic_bytes; i++) { test_pattern[i] = rand(); } if (!open_native_reference_window(test_pattern)) { *error = "Failed to open native reference window."; return false; } bool return_value = measure_latency(test_pattern, out_key_down_latency_ms, out_scroll_latency_ms, out_max_js_pause_time_ms, out_max_css_pause_time_ms, out_max_scroll_pause_time_ms, error); if (!close_native_reference_window()) { debug_log("Failed to close native reference window."); }; return return_value; } int64_t start_time = measurement.screenshot_time; previous_measurement = measurement; statistic javascript_frames; statistic css_frames; statistic key_down_events; statistic scroll_stats; init_statistic("javascript_frames", &javascript_frames, measurement.javascript_frames, start_time); init_statistic("key_down_events", &key_down_events, measurement.key_down_events, start_time); init_statistic("css_frames", &css_frames, measurement.css_frames, start_time); init_statistic("scroll", &scroll_stats, measurement.scroll_position, start_time); int sent_events = 0; int scroll_x = x + 40; int scroll_y = y + 40; int64_t last_scroll_sent = start_time; if (measurement.test_mode == TEST_MODE_SCROLL_LATENCY) { send_scroll_down(scroll_x, scroll_y); scroll_stats.previous_change_time = get_nanoseconds(); } while(true) { bool screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!screenshot_successful) { *error = "Test window moved during test. The test window must remain " "stationary and focused during the entire test."; return false; } if (measurement.test_mode == TEST_MODE_ABORT) { *error = "Test aborted."; return false; } screenshots++; int64_t screenshot_time = measurement.screenshot_time; int64_t previous_screenshot_time = previous_measurement.screenshot_time; debug_log("screenshot time %f", (screenshot_time - previous_screenshot_time) / (double)nanoseconds_per_millisecond); update_statistic(&javascript_frames, measurement.javascript_frames, screenshot_time, previous_screenshot_time); update_statistic(&key_down_events, measurement.key_down_events, screenshot_time, previous_screenshot_time); update_statistic(&css_frames, measurement.css_frames, screenshot_time, previous_screenshot_time); bool scroll_updated = update_statistic(&scroll_stats, measurement.scroll_position, screenshot_time, previous_screenshot_time); if (measurement.test_mode == TEST_MODE_JAVASCRIPT_LATENCY) { if (key_down_events.measurements >= latency_measurements_to_take) { break; } if (key_down_events.value_delta > sent_events) { *error = "More events received than sent! This is probably a bug in " "the test."; return false; } if (screenshot_time - key_down_events.previous_change_time > event_response_timeout_ms * nanoseconds_per_millisecond) { *error = "Browser did not respond to keyboard input. Make sure the " "test page remains focused for the entire test."; return false; } if (key_down_events.value_delta == sent_events) { // We want to avoid sending input events at a predictable time relative // to frames, so introduce a random delay of up to 1 frame (16.67 ms) // before sending the next event. usleep((rand() % 17) * 1000); if (!send_keystroke_z()) { *error = "Failed to send keystroke for \"Z\" key to test window."; return false; } key_down_events.previous_change_time = get_nanoseconds(); sent_events++; } } else if (measurement.test_mode == TEST_MODE_SCROLL_LATENCY) { if (scroll_stats.measurements >= latency_measurements_to_take) { break; } if (screenshot_time - scroll_stats.previous_change_time > event_response_timeout_ms * nanoseconds_per_millisecond) { *error = "Browser did not respond to scroll events. Make sure the " "test page remains focused for the entire test."; return false; } if (scroll_updated) { // We saw the start of a scroll. Wait for the scroll animation to // finish before continuing. We assume the animation is finished if // it's been 100 milliseconds since we last saw the scroll position // change. int64_t scroll_update_time = screenshot_time; int64_t scroll_wait_start_time = screenshot_time; while (screenshot_time - scroll_update_time < 100 * nanoseconds_per_millisecond) { screenshot_successful = read_data_from_screen((uint32_t)x, (uint32_t) y, magic_pattern, &measurement); if (!screenshot_successful) { *error = "Test window moved during test. The test window must " "remain stationary and focused during the entire test."; return false; } screenshot_time = measurement.screenshot_time; if (screenshot_time - scroll_wait_start_time > nanoseconds_per_second) { *error = "Browser kept scrolling for more than 1 second after a " "single scrollwheel event."; return false; } if (measurement.scroll_position != scroll_stats.value) { scroll_stats.value = measurement.scroll_position; scroll_update_time = screenshot_time; } } // We want to avoid sending input events at a predictable time // relative to frames, so introduce a random delay of up to 1 frame // (16.67 ms) before sending the next event. usleep((rand() % 17) * 1000); send_scroll_down(scroll_x, scroll_y); scroll_stats.previous_change_time = get_nanoseconds(); } } else if (measurement.test_mode == TEST_MODE_PAUSE_TIME) { // For the pause time test we want the browser to scroll continuously. // Send a scroll event every frame. if (screenshot_time - last_scroll_sent > 17 * nanoseconds_per_millisecond) { send_scroll_down(scroll_x, scroll_y); last_scroll_sent = get_nanoseconds(); } } else if (measurement.test_mode == TEST_MODE_PAUSE_TIME_TEST_FINISHED) { break; } else { *error = "Invalid test type. This is a bug in the test."; return false; } if (screenshot_time - start_time > test_timeout_ms * nanoseconds_per_millisecond) { *error = "Timeout."; return false; } previous_measurement = measurement; usleep(0); } // The latency we report is the midpoint of the interval given by the average // upper and lower bounds we've computed. *out_key_down_latency_ms = (upper_bound_ms(&key_down_events) + lower_bound_ms(&key_down_events)) / 2; *out_scroll_latency_ms = (upper_bound_ms(&scroll_stats) + lower_bound_ms(&scroll_stats) / 2); *out_max_js_pause_time_ms = javascript_frames.max_lower_bound / (double) nanoseconds_per_millisecond; *out_max_css_pause_time_ms = css_frames.max_lower_bound / (double) nanoseconds_per_millisecond; *out_max_scroll_pause_time_ms = scroll_stats.max_lower_bound / (double) nanoseconds_per_millisecond; debug_log("out_key_down_latency_ms: %f out_scroll_latency_ms: %f " "out_max_js_pause_time_ms: %f out_max_css_pause_time: %f\n " "out_max_scroll_pause_time_ms: %f", *out_key_down_latency_ms, *out_scroll_latency_ms, *out_max_js_pause_time_ms, *out_max_css_pause_time_ms, *out_max_scroll_pause_time_ms); return true; }