From 42a16bf2eec15fcfa372252602cfcbbbd7b96526 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Tue, 23 Aug 2022 14:56:23 -0400 Subject: [PATCH] refactor: perf_window --- runtime/include/perf_window.h | 81 +++++++++++++++++--------------- runtime/include/perf_window_t.h | 18 ++++--- runtime/include/route_config.h | 5 +- runtime/src/admissions_control.c | 3 ++ runtime/src/admissions_info.c | 4 +- tests/empty/concurrency/run.sh | 14 +++--- tests/fibonacci/bimodal/run.sh | 14 +++--- tests/workload_mix/run.sh | 14 +++--- 8 files changed, 85 insertions(+), 68 deletions(-) diff --git a/runtime/include/perf_window.h b/runtime/include/perf_window.h index d0f975f..7df158d 100644 --- a/runtime/include/perf_window.h +++ b/runtime/include/perf_window.h @@ -21,8 +21,8 @@ perf_window_initialize(struct perf_window *perf_window) lock_init(&perf_window->lock); perf_window->count = 0; - memset(perf_window->by_duration, 0, sizeof(struct execution_node) * PERF_WINDOW_BUFFER_SIZE); - memset(perf_window->by_termination, 0, sizeof(uint16_t) * PERF_WINDOW_BUFFER_SIZE); + memset(perf_window->by_duration, 0, sizeof(struct execution_node) * perf_window_capacity); + memset(perf_window->by_termination, 0, sizeof(uint16_t) * perf_window_capacity); } @@ -38,8 +38,8 @@ perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx { assert(lock_is_locked(&perf_window->lock)); assert(perf_window != NULL); - assert(first_by_duration_idx >= 0 && first_by_duration_idx < PERF_WINDOW_BUFFER_SIZE); - assert(second_by_duration_idx >= 0 && second_by_duration_idx < PERF_WINDOW_BUFFER_SIZE); + assert(first_by_duration_idx < perf_window_capacity); + assert(second_by_duration_idx < perf_window_capacity); uint16_t first_by_termination_idx = perf_window->by_duration[first_by_duration_idx].by_termination_idx; uint16_t second_by_termination_idx = perf_window->by_duration[second_by_duration_idx].by_termination_idx; @@ -51,11 +51,11 @@ perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx uint64_t first_execution_time = perf_window->by_duration[first_by_duration_idx].execution_time; uint64_t second_execution_time = perf_window->by_duration[second_by_duration_idx].execution_time; - /* Swap Indices in Buffer*/ + /* Swap indices */ perf_window->by_termination[first_by_termination_idx] = second_by_duration_idx; perf_window->by_termination[second_by_termination_idx] = first_by_duration_idx; - /* Swap by_termination_idx */ + /* Swap nodes */ struct execution_node tmp_node = perf_window->by_duration[first_by_duration_idx]; perf_window->by_duration[first_by_duration_idx] = perf_window->by_duration[second_by_duration_idx]; perf_window->by_duration[second_by_duration_idx] = tmp_node; @@ -67,66 +67,73 @@ perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx == second_execution_time); } +static inline void +perf_window_fill(struct perf_window *perf_window, uint64_t newest_execution_time) +{ + for (uint16_t i = 0; i < perf_window_capacity; i++) { + perf_window->by_termination[i] = i; + perf_window->by_duration[i] = (struct execution_node){ .execution_time = newest_execution_time, + .by_termination_idx = i }; + } + perf_window->count = perf_window_capacity; +} + /** - * Adds a new value to the perf window + * Adds newest_execution_time to the perf window * Not intended to be called directly! * @param perf_window - * @param value + * @param newest_execution_time */ static inline void -perf_window_add(struct perf_window *perf_window, uint64_t value) +perf_window_add(struct perf_window *perf_window, uint64_t newest_execution_time) { assert(perf_window != NULL); + /* Assumption: A successful invocation should run for a non-zero amount of time */ + assert(newest_execution_time > 0); - uint16_t idx_of_oldest; + uint16_t idx_to_replace; + uint64_t previous_execution_time; bool check_up; if (unlikely(!lock_is_locked(&perf_window->lock))) panic("lock not held when calling perf_window_add\n"); - /* A successful invocation should run for a non-zero amount of time */ - assert(value > 0); - - /* If count is 0, then fill entire array with initial execution times */ + /* If perf window is empty, fill all elements with newest_execution_time */ if (perf_window->count == 0) { - for (int i = 0; i < PERF_WINDOW_BUFFER_SIZE; i++) { - perf_window->by_termination[i] = i; - perf_window->by_duration[i] = (struct execution_node){ .execution_time = value, - .by_termination_idx = i }; - } - perf_window->count = PERF_WINDOW_BUFFER_SIZE; + perf_window_fill(perf_window, newest_execution_time); goto done; } - /* Otherwise, replace the oldest value, and then sort */ - idx_of_oldest = perf_window->by_termination[perf_window->count % PERF_WINDOW_BUFFER_SIZE]; - check_up = value > perf_window->by_duration[idx_of_oldest].execution_time; - - perf_window->by_duration[idx_of_oldest].execution_time = value; + /* If full, replace the oldest execution_time. Save the old execution time to know which direction to swap */ + idx_to_replace = perf_window->by_termination[perf_window->count % perf_window_capacity]; + previous_execution_time = perf_window->by_duration[idx_to_replace].execution_time; + perf_window->by_duration[idx_to_replace].execution_time = newest_execution_time; - if (check_up) { - for (uint16_t i = idx_of_oldest; - i + 1 < PERF_WINDOW_BUFFER_SIZE + /* At this point, the by_duration array is partially sorted. The node we overwrote needs to be shifted left or + * right. We can determine which direction to shift by comparing with the previous execution time. */ + if (newest_execution_time > previous_execution_time) { + for (uint16_t i = idx_to_replace; + i + 1 < perf_window_capacity && perf_window->by_duration[i + 1].execution_time < perf_window->by_duration[i].execution_time; i++) { perf_window_swap(perf_window, i, i + 1); } } else { - for (int i = idx_of_oldest; - i - 1 >= 0 + for (uint16_t i = idx_to_replace; + i >= 1 && perf_window->by_duration[i - 1].execution_time > perf_window->by_duration[i].execution_time; i--) { perf_window_swap(perf_window, i, i - 1); } } - /* The idx that we replaces should still point to the same value */ - assert(perf_window->by_duration[perf_window->by_termination[perf_window->count % PERF_WINDOW_BUFFER_SIZE]] + /* The idx that we replaces should still point to the same newest_execution_time */ + assert(perf_window->by_duration[perf_window->by_termination[perf_window->count % perf_window_capacity]] .execution_time - == value); + == newest_execution_time); /* The by_duration array should be ordered by execution time */ #ifndef NDEBUG - for (int i = 1; i < PERF_WINDOW_BUFFER_SIZE; i++) { + for (int i = 1; i < perf_window_capacity; i++) { assert(perf_window->by_duration[i - 1].execution_time <= perf_window->by_duration[i].execution_time); } #endif @@ -151,10 +158,10 @@ perf_window_get_percentile(struct perf_window *perf_window, uint8_t percentile, assert(percentile >= 50 && percentile <= 99); assert(perf_window->count > 0); - if (likely(perf_window->count >= PERF_WINDOW_BUFFER_SIZE)) - return perf_window->by_duration[precomputed_index].execution_time; + int idx = precomputed_index; + if (unlikely(perf_window->count < perf_window_capacity)) idx = perf_window->count * percentile / 100; - return perf_window->by_duration[perf_window->count * percentile / 100].execution_time; + return perf_window->by_duration[idx].execution_time; } /** diff --git a/runtime/include/perf_window_t.h b/runtime/include/perf_window_t.h index 4d39af4..0cc0f3e 100644 --- a/runtime/include/perf_window_t.h +++ b/runtime/include/perf_window_t.h @@ -1,15 +1,19 @@ #pragma once +#include #include #include "lock.h" -/* Should be Power of 2! */ -#define PERF_WINDOW_BUFFER_SIZE 16 +enum +{ + perf_window_capacity = 32 +}; + +static_assert(perf_window_capacity && !(perf_window_capacity & (perf_window_capacity - 1)), + "perf_window_capacity must be power of 2!"); -#if ((PERF_WINDOW_BUFFER_SIZE == 0) || (PERF_WINDOW_BUFFER_SIZE & (PERF_WINDOW_BUFFER_SIZE - 1)) != 0) -#error "PERF_WINDOW_BUFFER_SIZE must be power of 2!" -#endif +static_assert(perf_window_capacity <= UINT16_MAX, "perf_window_capacity must be indexable by a 16-bit unsigned int"); /* * The by_duration array sorts the last N executions by execution time @@ -24,8 +28,8 @@ struct execution_node { }; struct perf_window { - struct execution_node by_duration[PERF_WINDOW_BUFFER_SIZE]; - uint16_t by_termination[PERF_WINDOW_BUFFER_SIZE]; + struct execution_node by_duration[perf_window_capacity]; + uint16_t by_termination[perf_window_capacity]; uint64_t count; lock_t lock; }; diff --git a/runtime/include/route_config.h b/runtime/include/route_config.h index af44241..a7ad950 100644 --- a/runtime/include/route_config.h +++ b/runtime/include/route_config.h @@ -4,6 +4,7 @@ #include #include +#include "admissions_control.h" #include "runtime.h" #include "scheduler_options.h" @@ -100,12 +101,12 @@ route_config_validate(struct route_config *config, bool *did_set) if (config->admissions_percentile > 99 || config->admissions_percentile < 50) { fprintf(stderr, "admissions-percentile must be > 50 and <= 99 but was %u\n", - route_config->admissions_percentile); + config->admissions_percentile); return -1; } /* If the ratio is too big, admissions control is too coarse */ - uint32_t ratio = route_config->relative_deadline_us / route_config->expected_execution_us; + uint32_t ratio = config->relative_deadline_us / config->expected_execution_us; if (ratio > ADMISSIONS_CONTROL_GRANULARITY) { fprintf(stderr, "Ratio of Deadline to Execution time cannot exceed admissions control " diff --git a/runtime/src/admissions_control.c b/runtime/src/admissions_control.c index 022da50..2e63b97 100644 --- a/runtime/src/admissions_control.c +++ b/runtime/src/admissions_control.c @@ -3,6 +3,9 @@ #include "admissions_control.h" #include "debuglog.h" +#include "likely.h" +#include "panic.h" +#include "runtime.h" /* * Unitless estimate of the instantaneous fraction of system capacity required to complete all previously diff --git a/runtime/src/admissions_info.c b/runtime/src/admissions_info.c index 8aca6f9..8ef76bc 100644 --- a/runtime/src/admissions_info.c +++ b/runtime/src/admissions_info.c @@ -1,5 +1,7 @@ +#include "admissions_control.h" #include "admissions_info.h" #include "debuglog.h" +#include "perf_window.h" /** * Initializes perf window @@ -22,7 +24,7 @@ admissions_info_initialize(struct admissions_info *admissions_info, uint8_t perc if (unlikely(percentile < 50 || percentile > 99)) panic("Invalid admissions percentile"); admissions_info->percentile = percentile; - admissions_info->control_index = PERF_WINDOW_BUFFER_SIZE * percentile / 100; + admissions_info->control_index = perf_window_capacity * percentile / 100; #ifdef LOG_ADMISSIONS_CONTROL debuglog("Percentile: %u\n", admissions_info->percentile); debuglog("Control Index: %d\n", admissions_info->control_index); diff --git a/tests/empty/concurrency/run.sh b/tests/empty/concurrency/run.sh index 378f0fe..2816770 100755 --- a/tests/empty/concurrency/run.sh +++ b/tests/empty/concurrency/run.sh @@ -38,16 +38,16 @@ run_samples() { # Scrape the perf window size from the source if possible # TODO: Make a util function local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../../runtime/include/perf_window_t.h)" - local -i perf_window_buffer_size - if ! perf_window_buffer_size=$(grep "#define PERF_WINDOW_BUFFER_SIZE" < "$perf_window_path" | cut -d\ -f3); then - printf "Failed to scrape PERF_WINDOW_BUFFER_SIZE from ../../include/perf_window.h\n" + local -i perf_window_capacity + if ! perf_window_capacity=$(grep "perf_window_capacity =" < "$perf_window_path" | cut -d\ -f3); then + printf "Failed to scrape perf_window_capacity from ../../include/perf_window.h\n" printf "Defaulting to 16\n" - perf_window_buffer_size=16 + perf_window_capacity=16 fi - local -ir perf_window_buffer_size + local -ir perf_window_capacity printf "Running Samples: " - hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -q 200 -cpus 3 -o csv -m GET "http://${hostname}:10000" 1> /dev/null 2> /dev/null || { + hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -q 200 -cpus 3 -o csv -m GET "http://${hostname}:10000/empty" 1> /dev/null 2> /dev/null || { printf "[ERR]\n" panic "samples failed" return 1 @@ -79,7 +79,7 @@ run_experiments() { printf "Running Experiments:\n" for conn in ${concurrency[*]}; do printf "\t%d Concurrency: " "$conn" - hey -disable-compression -disable-keepalive -disable-redirects -n "$iterations" -c "$conn" -cpus 2 -o csv -m GET "http://$hostname:10000" > "$results_directory/con$conn.csv" 2> /dev/null || { + hey -disable-compression -disable-keepalive -disable-redirects -n "$iterations" -c "$conn" -cpus 2 -o csv -m GET "http://$hostname:10000/empty" > "$results_directory/con$conn.csv" 2> /dev/null || { printf "[ERR]\n" panic "experiment failed" return 1 diff --git a/tests/fibonacci/bimodal/run.sh b/tests/fibonacci/bimodal/run.sh index f64a167..c95b694 100755 --- a/tests/fibonacci/bimodal/run.sh +++ b/tests/fibonacci/bimodal/run.sh @@ -41,22 +41,22 @@ run_samples() { # Scrape the perf window size from the source if possible # TODO: Make a util function local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../../runtime/include/perf_window_t.h)" - local -i perf_window_buffer_size - if ! perf_window_buffer_size=$(grep "#define PERF_WINDOW_BUFFER_SIZE" < "$perf_window_path" | cut -d\ -f3); then - printf "Failed to scrape PERF_WINDOW_BUFFER_SIZE from ../../../runtime/include/perf_window.h\n" + local -i perf_window_capacity + if ! perf_window_capacity=$(grep "perf_window_capacity =" < "$perf_window_path" | cut -d\ -f3); then + printf "Failed to scrape perf_window_capacity from ../../../runtime/include/perf_window.h\n" printf "Defaulting to 16\n" - perf_window_buffer_size=16 + perf_window_capacity=16 fi - local -ir perf_window_buffer_size + local -ir perf_window_capacity printf "Running Samples: " - hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10010/fib2" 1> /dev/null 2> /dev/null || { + hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10010/fib2" 1> /dev/null 2> /dev/null || { printf "[ERR]\n" panic "fib40 samples failed with $?" return 1 } - hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:100010/fib" 1> /dev/null 2> /dev/null || { + hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:100010/fib" 1> /dev/null 2> /dev/null || { printf "[ERR]\n" panic "fib10 samples failed with $?" return 1 diff --git a/tests/workload_mix/run.sh b/tests/workload_mix/run.sh index 4079276..86e54b9 100755 --- a/tests/workload_mix/run.sh +++ b/tests/workload_mix/run.sh @@ -38,22 +38,22 @@ run_samples() { # Scrape the perf window size from the source if possible # TODO: Make a util function local -r perf_window_path="$(path_join "$__run_sh__base_path" ../../runtime/include/perf_window_t.h)" - local -i perf_window_buffer_size - if ! perf_window_buffer_size=$(grep "#define PERF_WINDOW_BUFFER_SIZE" < "$perf_window_path" | cut -d\ -f3); then - printf "Failed to scrape PERF_WINDOW_BUFFER_SIZE from ../../runtime/include/perf_window.h\n" + local -i perf_window_capacity + if ! perf_window_capacity=$(grep "perf_window_capacity =" < "$perf_window_path" | cut -d\ -f3); then + printf "Failed to scrape perf_window_capacity from ../../runtime/include/perf_window.h\n" printf "Defaulting to 16\n" - perf_window_buffer_size=16 + perf_window_capacity=16 fi - local -ir perf_window_buffer_size + local -ir perf_window_capacity printf "Running Samples: " - hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || { + hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "40\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || { printf "[ERR]\n" panic "fibonacci_40 samples failed with $?" return 1 } - hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_buffer_size" -c "$perf_window_buffer_size" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || { + hey -disable-compression -disable-keepalive -disable-redirects -n "$perf_window_capacity" -c "$perf_window_capacity" -cpus 3 -t 0 -o csv -m GET -d "10\n" "http://${hostname}:10000/fibonacci_10" 1> /dev/null 2> /dev/null || { printf "[ERR]\n" panic "fibonacci_10 samples failed with $?" return 1