From 34e8731854697a16a29ddafe24323e10b99a2b4b Mon Sep 17 00:00:00 2001 From: Emil Date: Fri, 22 Jul 2022 15:47:34 -0400 Subject: [PATCH] HTTP Log Tool and More Script Automations (#354) * Added new HTTP Perf Log mechanism (It measures http session lifetime, receive and send block durations) Also: - automated generation of spec.json - combined HEY and LOADTEST scripts into one, optional - refactor run.sh to be ready for the MTDBF --- .vscode/settings.json | 3 +- runtime/include/http_session.h | 12 +- runtime/include/http_session_perf_log.h | 43 ++ runtime/include/sandbox_perf_log.h | 2 +- runtime/include/sandbox_set_as_complete.h | 2 +- runtime/src/http_session_perf_log.c | 25 + runtime/src/listener_thread.c | 4 + runtime/src/main.c | 1 + runtime/src/runtime.c | 1 + tests/.gitignore | 1 + tests/bash_libraries/experiment_globals.sh | 79 ++++ tests/bash_libraries/framework.sh | 31 +- tests/bash_libraries/generate_spec_json.sh | 65 +++ tests/common/edf_preemption.env | 2 + tests/common/mt/mtds_preemption.env | 3 - tests/common/mtds_preemption.env | 1 + tests/mt_unimodal/Makefile | 48 ++ tests/mt_unimodal/README.md | 15 + tests/mt_unimodal/run.sh | 504 +++++++++++++++++++++ tests/mt_unimodal/spec.json | 36 ++ tests/mt_unimodal/template.json | 17 + 21 files changed, 876 insertions(+), 19 deletions(-) create mode 100644 runtime/include/http_session_perf_log.h create mode 100644 runtime/src/http_session_perf_log.c create mode 100644 tests/bash_libraries/experiment_globals.sh create mode 100644 tests/bash_libraries/generate_spec_json.sh delete mode 100644 tests/common/mt/mtds_preemption.env create mode 100644 tests/mt_unimodal/Makefile create mode 100644 tests/mt_unimodal/README.md create mode 100755 tests/mt_unimodal/run.sh create mode 100644 tests/mt_unimodal/spec.json create mode 100644 tests/mt_unimodal/template.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 5ad4c9a..09ae2d4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -124,7 +124,8 @@ "route.h": "c", "pool.h": "c", "local_cleanup_queue.h": "c", - "sandbox_state_transition.h": "c" + "sandbox_state_transition.h": "c", + "http_session_perf_log.h": "c" }, "files.exclude": { "**/.git": true, diff --git a/runtime/include/http_session.h b/runtime/include/http_session.h index 6f7998a..2252f64 100644 --- a/runtime/include/http_session.h +++ b/runtime/include/http_session.h @@ -16,8 +16,10 @@ #include "http_parser_settings.h" #include "tenant.h" #include "vec.h" +#include "http_session_perf_log.h" #define HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE (PAGE_SIZE) +#define HTTP_SESSION_RESPONSE_HEADER_CAPACITY 256 #define u8 uint8_t VEC(u8) @@ -38,8 +40,6 @@ enum http_session_state HTTP_SESSION_SENT_RESPONSE }; -#define HTTP_SESSION_RESPONSE_HEADER_CAPACITY 256 - struct http_session { enum http_session_state state; struct sockaddr client_address; /* client requesting connection! */ @@ -54,9 +54,13 @@ struct http_session { size_t response_buffer_written; struct tenant *tenant; /* Backlink required when read blocks on listener core */ uint64_t request_arrival_timestamp; + uint64_t request_downloaded_timestamp; + uint64_t response_takeoff_timestamp; uint64_t response_sent_timestamp; }; +extern void http_session_perf_log_print_entry(struct http_session *http_session); + /** * Initalize state associated with an http parser * Because the http_parser structure uses pointers to the request buffer, if realloc moves the request @@ -183,6 +187,8 @@ http_session_set_response_header(struct http_session *session, int status_code, strncpy(session->response_header, http_header_build(status_code), to_copy - 1); session->response_header_length = to_copy; } + + session->response_takeoff_timestamp = __getcycles(); } static inline void @@ -468,7 +474,9 @@ http_session_send_response(struct http_session *session, void_star_cb on_eagain) goto CLOSE; } + /* Terminal State Logging for Http Session */ session->response_sent_timestamp = __getcycles(); + http_session_perf_log_print_entry(session); CLOSE: http_session_close(session); diff --git a/runtime/include/http_session_perf_log.h b/runtime/include/http_session_perf_log.h new file mode 100644 index 0000000..98ba049 --- /dev/null +++ b/runtime/include/http_session_perf_log.h @@ -0,0 +1,43 @@ +#pragma once + +#include "pretty_print.h" +#include "runtime.h" +#include "http_session.h" + +extern FILE *http_session_perf_log; +typedef struct http_session http_session; +void http_session_perf_log_print_entry(struct http_session *http_session); + +/** + * @brief Prints headers for the per-session perf logs + */ +static inline void +http_session_perf_log_print_header() +{ + if (http_session_perf_log == NULL) { perror("http_session perf log"); } + fprintf(http_session_perf_log, + "tenant,route,state,header_len,resp_body_len,receive_duration,sent_duration,total_lifetime,proc_MHz\n"); +} + +static inline void +http_session_perf_log_init() +{ + char *http_session_perf_log_path = getenv("SLEDGE_HTTP_SESSION_PERF_LOG"); + if (http_session_perf_log_path != NULL) { + pretty_print_key_value("HTTP Session Performance Log", "%s\n", http_session_perf_log_path); + http_session_perf_log = fopen(http_session_perf_log_path, "w"); + if (http_session_perf_log == NULL) perror("http_session_perf_log_init\n"); + http_session_perf_log_print_header(); + } else { + pretty_print_key_disabled("HTTP Session Performance Log"); + } +} + +static inline void +http_session_perf_log_cleanup() +{ + if (http_session_perf_log != NULL) { + fflush(http_session_perf_log); + fclose(http_session_perf_log); + } +} diff --git a/runtime/include/sandbox_perf_log.h b/runtime/include/sandbox_perf_log.h index 23b55a9..a7a96de 100644 --- a/runtime/include/sandbox_perf_log.h +++ b/runtime/include/sandbox_perf_log.h @@ -36,7 +36,7 @@ sandbox_perf_log_print_entry(struct sandbox *sandbox) * becomes more intelligent, then peak linear memory size needs to be tracked * seperately from current linear memory size. */ - fprintf(sandbox_perf_log, "%lu,%s,%s,%s,%lu,%lu,%lu,,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u\n", + fprintf(sandbox_perf_log, "%lu,%s,%s,%s,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%u\n", sandbox->id, sandbox->tenant->name, sandbox->route->route, sandbox_state_stringify(sandbox->state), sandbox->route->relative_deadline, sandbox->total_time, queued_duration, sandbox->duration_of_state[SANDBOX_UNINITIALIZED], sandbox->duration_of_state[SANDBOX_ALLOCATED], diff --git a/runtime/include/sandbox_set_as_complete.h b/runtime/include/sandbox_set_as_complete.h index 3741592..2ff5e95 100644 --- a/runtime/include/sandbox_set_as_complete.h +++ b/runtime/include/sandbox_set_as_complete.h @@ -51,7 +51,7 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state) + sandbox->duration_of_state[SANDBOX_RUNNING_SYS]); admissions_control_subtract(sandbox->admissions_estimate); - /* Terminal State Logging */ + /* Terminal State Logging for Sandbox */ sandbox_perf_log_print_entry(sandbox); sandbox_summarize_page_allocations(sandbox); diff --git a/runtime/src/http_session_perf_log.c b/runtime/src/http_session_perf_log.c new file mode 100644 index 0000000..4c67672 --- /dev/null +++ b/runtime/src/http_session_perf_log.c @@ -0,0 +1,25 @@ +#include "http_session_perf_log.h" + +FILE *http_session_perf_log = NULL; + +/** + * Prints key performance metrics for a http_session to http_session_perf_log + * This is defined by an environment variable + * @param http_session + */ +void +http_session_perf_log_print_entry(struct http_session *http_session) +{ + /* If the log was not defined by an environment variable, early out */ + if (http_session_perf_log == NULL) return; + + const uint64_t receive_duration = http_session->request_downloaded_timestamp + - http_session->request_arrival_timestamp; + const uint64_t sent_duration = http_session->response_sent_timestamp - http_session->response_takeoff_timestamp; + const uint64_t total_lifetime = http_session->response_sent_timestamp - http_session->request_arrival_timestamp; + + fprintf(http_session_perf_log, "%s,%s,%u,%lu,%lu,%lu,%lu,%lu,%u\n", http_session->tenant->name, + http_session->http_request.full_url, http_session->state, http_session->response_header_written, + http_session->response_buffer_written, receive_duration, sent_duration, total_lifetime, + runtime_processor_speed_MHz); +} diff --git a/runtime/src/listener_thread.c b/runtime/src/listener_thread.c index 4553487..f615db8 100644 --- a/runtime/src/listener_thread.c +++ b/runtime/src/listener_thread.c @@ -11,6 +11,7 @@ #include "tcp_session.h" #include "tenant.h" #include "tenant_functions.h" +#include "http_session_perf_log.h" static void listener_thread_unregister_http_session(struct http_session *http); static void panic_on_epoll_error(struct epoll_event *evt); @@ -203,6 +204,7 @@ static void on_client_request_received(struct http_session *session) { assert(session->state == HTTP_SESSION_RECEIVED_REQUEST); + session->request_downloaded_timestamp = __getcycles(); struct route *route = http_router_match_route(&session->tenant->router, session->http_request.full_url); if (route == NULL) { @@ -288,7 +290,9 @@ on_client_response_body_sending(struct http_session *session) static void on_client_response_sent(struct http_session *session) { + /* Terminal State Logging for Http Session */ session->response_sent_timestamp = __getcycles(); + http_session_perf_log_print_entry(session); http_session_close(session); http_session_free(session); diff --git a/runtime/src/main.c b/runtime/src/main.c index 6772016..ff04f3b 100644 --- a/runtime/src/main.c +++ b/runtime/src/main.c @@ -226,6 +226,7 @@ runtime_configure() pretty_print_key_value("Quantum", "%u us\n", runtime_quantum_us); sandbox_perf_log_init(); + http_session_perf_log_init(); } void diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index f355ee7..7213ccc 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -40,6 +40,7 @@ void runtime_cleanup() { sandbox_perf_log_cleanup(); + http_session_perf_log_cleanup(); if (runtime_worker_threads_deadline) free(runtime_worker_threads_deadline); if (runtime_worker_threads_argument) free(runtime_worker_threads_argument); diff --git a/tests/.gitignore b/tests/.gitignore index 0b0d31e..d786eca 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -3,6 +3,7 @@ perf.data perf.data.old samples perf.log +http_perf.log log.csv *res.dat result.dat diff --git a/tests/bash_libraries/experiment_globals.sh b/tests/bash_libraries/experiment_globals.sh new file mode 100644 index 0000000..6beb561 --- /dev/null +++ b/tests/bash_libraries/experiment_globals.sh @@ -0,0 +1,79 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 +if [ -n "$__experiment_server_globals_sh__" ]; then return; fi +__experiment_server_globals_sh__=$(date) + +# The global configs for the scripts +declare -gr SERVER_LOG_FILE="perf.log" +declare -gr SERVER_HTTP_LOG_FILE="http_perf.log" +declare -gr NWORKERS=$(($(nproc)-2)) + +# Sandbox Perf Log Globals: +declare -ga SANDBOX_METRICS=(total queued uninitialized allocated initialized runnable interrupted preempted running_sys running_user asleep returned complete error) +declare -gA SANDBOX_METRICS_FIELDS=( + [total]=6 + [queued]=7 + [uninitialized]=8 + [allocated]=9 + [initialized]=10 + [runnable]=11 + [interrupted]=12 + [preempted]=13 + [running_sys]=14 + [running_user]=15 + [asleep]=16 + [returned]=17 + [complete]=18 + [error]=19 +) +declare -gr SANDBOX_TENANT_NAME_FIELD=2 +declare -gr SANDBOX_ROUTE_FIELD=3 +declare -gr SANDBOX_CPU_FREQ_FIELD=20 +declare -gr SANDBOX_RESPONSE_CODE_FIELD=21 + +# HTTP Session Perf Log Globals: +declare -ga HTTP_METRICS=(http_receive http_sent http_total) +declare -gA HTTP_METRICS_FIELDS=( + [http_receive]=6 + [http_sent]=7 + [http_total]=8 +) +declare -gr HTTP_TENANT_NAME_FIELD=1 +declare -gr HTTP_ROUTE_FIELD=2 +declare -gr HTTP_CPU_FREQ_FIELD=9 + +assert_run_experiments_args() { + if (($# != 3)); then + panic "invalid number of arguments \"$#\"" + return 1 + elif [[ -z "$1" ]]; then + panic "hostname \"$1\" was empty" + return 1 + elif [[ ! -d "$2" ]]; then + panic "directory \"$2\" does not exist" + return 1 + elif [[ -z "$3" ]]; then + panic "load gen \"$3\" was empty" + return 1 + fi +} + +assert_process_client_results_args() { + if (($# != 1)); then + error_msg "invalid number of arguments ($#, expected 1)" + return 1 + elif ! [[ -d "$1" ]]; then + error_msg "directory $1 does not exist" + return 1 + fi +} + +assert_process_server_results_args() { + if (($# != 1)); then + panic "invalid number of arguments \"$#\"" + return 1 + elif [[ ! -d "$1" ]]; then + panic "directory \"$1\" does not exist" + return 1 + fi +} diff --git a/tests/bash_libraries/framework.sh b/tests/bash_libraries/framework.sh index 0bc7bcf..de46869 100755 --- a/tests/bash_libraries/framework.sh +++ b/tests/bash_libraries/framework.sh @@ -1,4 +1,5 @@ # shellcheck shell=bash +# shellcheck disable=SC1091,SC2034,SC2155 if [ -n "$__framework_sh__" ]; then return; fi __framework_sh__=$(date) @@ -85,6 +86,7 @@ __framework_sh__usage() { echo " -p,--perf Run under perf. Limited to running on a baremetal Linux host!" echo " -s,--serve Serve but do not run client" echo " -t,--target= Execute as client against remote URL" + echo " -l,--loadgen= Pick load generator to use for client. Default is hey." echo " -v,--valgrind Debug under Valgrind but do not run client" } @@ -92,8 +94,6 @@ __framework_sh__usage() { __framework_sh__initialize_globals() { # timestamp is used to name the results directory for a particular test run # This can be manually overridden via the name argument - # shellcheck disable=SC2155 - # shellcheck disable=SC2034 declare -gir __framework_sh__timestamp=$(date +%s) declare -g __framework_sh__experiment_name="$__framework_sh__timestamp" @@ -101,13 +101,12 @@ __framework_sh__initialize_globals() { declare -g __framework_sh__target="" declare -g __framework_sh__role="" declare -g __framework_sh__envfile="" + declare -g __framework_sh__loadgen="" # Configure environment variables - # shellcheck disable=SC2155 declare -gr __framework_sh__application_directory="$(dirname "$(realpath "$0"))")" - # shellcheck disable=SC2155 declare -gr __framework_sh__path=$(dirname "$(realpath "${BASH_SOURCE[0]}")") - declare -gr __framework_sh__common_path="$(cd "$__framework_sh__path" && cd ../common && pwd)" + declare -gr common_directory="$(cd "$__framework_sh__path" && cd ../common && pwd)" local -r binary_directory="$(cd "$__framework_sh__path" && cd ../../runtime/bin && pwd)" export PATH=$binary_directory:$PATH export LD_LIBRARY_PATH=$binary_directory:$LD_LIBRARY_PATH @@ -177,6 +176,16 @@ __framework_sh__parse_arguments() { __framework_sh__envfile="${i#*=}" echo "Set envfile to $__framework_sh__envfile" ;; + -l=* | --loadgen=*) + if [[ "$__framework_sh__role" == "server" ]]; then + echo "Expected to be used with run by the client" + __framework_sh__usage + return 1 + fi + __framework_sh__loadgen="${i#*=}" + echo "Set load generator to $__framework_sh__loadgen" + shift + ;; -h | --help) __framework_sh__usage exit 0 @@ -279,7 +288,7 @@ __framework_sh__start_runtime() { local -r how_to_run="$1" local -r specification="$2" - local -r log_name=log.txt + local -r log_name=log_server.txt local log="$RESULTS_DIRECTORY/${log_name}" local -i sledgert_pid=0 @@ -346,7 +355,7 @@ __framework_sh__run_valgrind() { # Starts the Sledge Runtime under GDB __framework_sh__run_debug() { validate_dependencies gdb - # shellcheck disable=SC2155 + local project_directory=$(cd ../.. && pwd) if [[ "$project_directory" != "/sledge/runtime" ]]; then @@ -374,12 +383,12 @@ __framework_sh__run_debug() { } __framework_sh__run_client() { - local -r log_name=log.txt + local -r log_name=log_client.txt local log="$RESULTS_DIRECTORY/${log_name}" - + __framework_sh__log_environment >> "$log" - experiment_client "$__framework_sh__target" "$RESULTS_DIRECTORY" || return 1 + experiment_client "$__framework_sh__target" "$RESULTS_DIRECTORY" "$__framework_sh__loadgen" || return 1 return 0 } @@ -442,7 +451,7 @@ __framework_sh__run_both() { __framework_sh__run_both_env "$__framework_sh__envfile" else local -i envfiles_found=0 - for envfile in "$__framework_sh__common_path"/*.env; do + for envfile in "$common_directory"/*.env; do ((envfiles_found++)) __framework_sh__run_both_env "$envfile" || exit 1 done diff --git a/tests/bash_libraries/generate_spec_json.sh b/tests/bash_libraries/generate_spec_json.sh new file mode 100644 index 0000000..490eacc --- /dev/null +++ b/tests/bash_libraries/generate_spec_json.sh @@ -0,0 +1,65 @@ +# shellcheck shell=bash +# shellcheck disable=SC2154 +if [ -n "$__generate_spec_json_sh__" ]; then return; fi +__generate_spec_json_sh__=$(date) + + +generate_spec_json() { + printf "Generating 'spec.json'\n" + + for tenant in "${TENANTS[@]}"; do + port=${PORTS[$tenant]} + repl_period=${MTDS_REPLENISH_PERIODS_us[$tenant]} + budget=${MTDS_MAX_BUDGETS_us[$tenant]} + # reservation=${MTDBF_RESERVATIONS_percen[${tenant}]} + route=${ROUTES[$tenant]} + workload="$tenant-$route" + deadline=${DEADLINES_us[$workload]} + expected=${EXPECTED_EXECUTIONS_us[$workload]} + + # Generates unique module specs on different ports using the given 'ru's + jq ". + { \ + \"name\": \"$tenant\",\ + \"port\": $port,\ + \"replenishment-period-us\": $repl_period, \ + \"max-budget-us\": $budget} | \ + (.routes[] = \ + .routes[] + { \ + \"route\": \"/$route\",\ + \"admissions-percentile\": $ESTIMATIONS_PERCENTILE,\ + \"expected-execution-us\": $expected,\ + \"relative-deadline-us\": $deadline + }) \ + " \ + < "./template.json" \ + > "./result_${tenant}.json" + # \"reservation-percentile\": $reservation, \ + done + + if [ "$CLIENT_TERMINATE_SERVER" == true ]; then + jq ". + { \ + \"name\": \"Admin\",\ + \"port\": 55555,\ + \"replenishment-period-us\": 0, \ + \"max-budget-us\": 0} | \ + (.routes = [\ + .routes[] + { \ + \"route\": \"/main\",\ + \"admissions-percentile\": 70,\ + \"expected-execution-us\": 1000,\ + \"relative-deadline-us\": 10000}, \ + .routes[] + { \ + \"route\": \"/terminator\",\ + \"admissions-percentile\": 70,\ + \"expected-execution-us\": 1000,\ + \"relative-deadline-us\": 10000 }\ + ]) \ + " \ + < "./template.json" \ + > "./result_admin.json" + fi + + # Merges all of the multiple specs for a single module + jq -s '. | sort_by(.name)' ./result_*.json > "./spec.json" + rm ./result_*.json +} diff --git a/tests/common/edf_preemption.env b/tests/common/edf_preemption.env index 0cd4ebf..536bbb1 100644 --- a/tests/common/edf_preemption.env +++ b/tests/common/edf_preemption.env @@ -1,2 +1,4 @@ SLEDGE_SCHEDULER=EDF SLEDGE_DISABLE_PREEMPTION=false +SLEDGE_SANDBOX_PERF_LOG=perf.log +SLEDGE_HTTP_SESSION_PERF_LOG=http_perf.log diff --git a/tests/common/mt/mtds_preemption.env b/tests/common/mt/mtds_preemption.env deleted file mode 100644 index cf8670b..0000000 --- a/tests/common/mt/mtds_preemption.env +++ /dev/null @@ -1,3 +0,0 @@ -SLEDGE_SCHEDULER=MTDS -SLEDGE_DISABLE_PREEMPTION=false -SLEDGE_SANDBOX_PERF_LOG=perf.log diff --git a/tests/common/mtds_preemption.env b/tests/common/mtds_preemption.env index cf8670b..379e0e5 100644 --- a/tests/common/mtds_preemption.env +++ b/tests/common/mtds_preemption.env @@ -1,3 +1,4 @@ SLEDGE_SCHEDULER=MTDS SLEDGE_DISABLE_PREEMPTION=false SLEDGE_SANDBOX_PERF_LOG=perf.log +SLEDGE_HTTP_SESSION_PERF_LOG=http_perf.log diff --git a/tests/mt_unimodal/Makefile b/tests/mt_unimodal/Makefile new file mode 100644 index 0000000..e99c18c --- /dev/null +++ b/tests/mt_unimodal/Makefile @@ -0,0 +1,48 @@ +RUNTIME_DIR=../../../runtime/ +SLEDGE_BINARY_DIR=${RUNTIME_DIR}/bin +SLEDGE_TESTS_DIR=${RUNTIME_DIR}/tests +HOSTNAME=localhost +DURATION_SEC=5 + +all: run + +clean: + make -C ${RUNTIME_DIR} clean + make -C ${SLEDGE_TESTS_DIR} clean + rm -f ${SLEDGE_BINARY_DIR}/fibonacci.wasm.so + +${SLEDGE_BINARY_DIR}/sledgert: + make -C ${RUNTIME_DIR} runtime + +.PHONY: sledgert +sledgert: ${SLEDGE_BINARY_DIR}/sledgert + +${SLEDGE_BINARY_DIR}/fibonacci.wasm.so: + make -C ../../../applications fibonacci.install + +.PHONY: fibonacci +fibonacci: ${SLEDGE_BINARY_DIR}/fibonacci.wasm.so + +run: sledgert fibonacci + LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} ${SLEDGE_BINARY_DIR}/sledgert spec.json + +debug: sledgert fibonacci + SLEDGE_DISABLE_PREEMPTION=true SLEDGE_NWORKERS=1 \ + LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} gdb ${SLEDGE_BINARY_DIR}/sledgert \ + --eval-command="handle SIGUSR1 noprint nostop" \ + --eval-command="handle SIGPIPE noprint nostop" \ + --eval-command="set pagination off" \ + --eval-command="set print pretty" \ + --eval-command="run spec.json" + +client-fib30-once: + echo 30 | http :10031/fib + +client-fib40-once: + http :10030/fib?40 + +client-fib30-multi: + hey -z ${DURATION_SEC}s -cpus 2 -c 100 -t 0 -o csv -m POST -d "30\n" "http://${HOSTNAME}:10030/fib" + +client-fib40-multi: + hey -z ${DURATION_SEC}s -cpus 2 -c 100 -t 0 -o csv -m POST -d "40\n" "http://${HOSTNAME}:10030/fib" diff --git a/tests/mt_unimodal/README.md b/tests/mt_unimodal/README.md new file mode 100644 index 0000000..24390b4 --- /dev/null +++ b/tests/mt_unimodal/README.md @@ -0,0 +1,15 @@ +# Unimodal Distribution + +This experiment drives a unimodal distribution of two workloads from the same tenant, where one workload has some guarantees and the other does not. + + +First, the non-guaranteed workload is launched to acquire all the CPU. After a few second (e.g. 3s) the guaranteed workload begins. Depending on the amount of the reservation, the guaranteed workload should typically have a better success rate than the other one. + +## Independent Variable + +The Scheduling Policy: MT-EDF versus just EDF + +## Dependent Variables + +Replenishment Period and Max Budget of Tenants +Latency of high priority workload diff --git a/tests/mt_unimodal/run.sh b/tests/mt_unimodal/run.sh new file mode 100755 index 0000000..9bd8877 --- /dev/null +++ b/tests/mt_unimodal/run.sh @@ -0,0 +1,504 @@ +#!/bin/bash + +# shellcheck disable=SC1091,SC2034 + +# This experiment is intended to document how the level of concurrent requests influence the latency, throughput, and success rate +# Success - The percentage of requests that complete by their deadlines +# Throughput - The mean number of successful requests per second +# Latency - the rount-trip resonse time (us) of successful requests at the p50, p90, p99, and p100 percentiles + +# Add bash_libraries directory to path +__run_sh__base_path="$(dirname "$(realpath --logical "${BASH_SOURCE[0]}")")" +__run_sh__bash_libraries_relative_path="../bash_libraries" +__run_sh__bash_libraries_absolute_path=$(cd "$__run_sh__base_path" && cd "$__run_sh__bash_libraries_relative_path" && pwd) +export PATH="$__run_sh__bash_libraries_absolute_path:$PATH" + +source csv_to_dat.sh || exit 1 +source framework.sh || exit 1 +source generate_gnuplots.sh || exit 1 +source get_result_count.sh || exit 1 +source panic.sh || exit 1 +source path_join.sh || exit 1 +source percentiles_table.sh || exit 1 +source experiment_globals.sh || exit 1 + +validate_dependencies hey loadtest jq + +# The global configs for the scripts +declare -r CLIENT_TERMINATE_SERVER=false +declare -r ITERATIONS=10000 +declare -r DURATION_sec=1 +declare -r ESTIMATIONS_PERCENTILE=70 + +declare -r TENANT_NG="NNN" +declare -r TENANT_GR="GWU" +declare -ar TENANTS=("$TENANT_NG" "$TENANT_GR") + +declare -Ar ARGS=( + [$TENANT_NG]="30" + [$TENANT_GR]="30" +) + +# Make sure not to use 55555 (Reserved for Admin) +declare -Ar PORTS=( + [$TENANT_NG]=10030 + [$TENANT_GR]=20030 +) + +# No need for slashes +declare -Ar ROUTES=( + [$TENANT_NG]="fib" + [$TENANT_GR]="fib" +) + +declare -Ar MTDS_REPLENISH_PERIODS_us=( + [$TENANT_NG]=0 + [$TENANT_GR]=16000 +) + +declare -Ar MTDS_MAX_BUDGETS_us=( + [$TENANT_NG]=0 + [$TENANT_GR]=144000 +) + +declare -Ar MTDBF_RESERVATIONS_percen=( + [$TENANT_NG]=0 + [$TENANT_GR]=0 +) + +declare -ar WORKLOADS=("${TENANT_NG}-${ROUTES[$TENANT_NG]}" "${TENANT_GR}-${ROUTES[$TENANT_GR]}") + +declare -Ar EXPECTED_EXECUTIONS_us=( + [${WORKLOADS[0]}]=4000 + [${WORKLOADS[1]}]=4000 +) + +declare -Ar DEADLINES_us=( + [${WORKLOADS[0]}]=16000 + [${WORKLOADS[1]}]=16000 +) + + +# Generate the spec.json file from the given arguments above +. "$__run_sh__bash_libraries_absolute_path/generate_spec_json.sh" + +# Execute the experiments concurrently +run_experiments() { + assert_run_experiments_args "$@" + + local -r hostname="$1" + local -r results_directory="$2" + local -r loadgen="$3" + + # The duration in seconds that the low priority task should run before the high priority task starts + local -ir OFFSET=1 + + printf "Running Experiments with %s\n" "$loadgen" + + # Run concurrently + local app_gr_PID + local app_ng_PID + + local -r port_ng=${PORTS[$TENANT_NG]} + local -r port_gr=${PORTS[$TENANT_GR]} + + local -r route_ng=${ROUTES[$TENANT_NG]} + local -r route_gr=${ROUTES[$TENANT_GR]} + + local -r workload_ng=${WORKLOADS[0]} + local -r workload_gr=${WORKLOADS[1]} + + local -r deadline_ng=${DEADLINES_us[$workload_ng]} + local -r deadline_gr=${DEADLINES_us[$workload_gr]} + + local -r con_ng=$((NWORKERS*10)) + local -r con_gr=$((NWORKERS*2)) + + local -r rps_ng=$((1000000*con_ng/deadline_ng)) + local -r rps_gr=$((1000000*con_gr/deadline_gr)) + + local -r arg_ng=${ARGS[$TENANT_NG]} + local -r arg_gr=${ARGS[$TENANT_GR]} + + if [ "$loadgen" = "hey" ]; then + hey -disable-compression -disable-keepalive -disable-redirects -z $((DURATION_sec+OFFSET))s -n "$ITERATIONS" -c $con_ng -t 0 -o csv -m POST -d "$arg_ng" "http://${hostname}:$port_ng/$route_ng" > "$results_directory/$workload_ng.csv" 2> "$results_directory/$workload_ng-err.dat" & + app_ng_PID="$!" + + sleep "$OFFSET"s + + hey -disable-compression -disable-keepalive -disable-redirects -z "$DURATION_sec"s -n "$ITERATIONS" -c $con_gr -t 0 -o csv -m POST -d "$arg_gr" "http://${hostname}:$port_gr/$route_gr" > "$results_directory/$workload_gr.csv" 2> "$results_directory/$workload_gr-err.dat" & + app_gr_PID="$!" + elif [ "$loadgen" = "loadtest" ]; then + loadtest -t $((DURATION_sec+OFFSET)) -c $con_ng --rps $rps_ng -P "$arg_ng" "http://${hostname}:$port_ng/$route_ng" > "$results_directory/$workload_ng.dat" 2> "$results_directory/$workload_ng-err.dat" & + app_ng_PID="$!" + + sleep "$OFFSET"s + + loadtest -t "$DURATION_sec" -c $con_gr --rps $rps_gr -P "$arg_gr" "http://${hostname}:$port_gr/$route_gr" > "$results_directory/$workload_gr.dat" 2> "$results_directory/$workload_gr-err.dat" & + app_gr_PID="$!" + fi + + wait -f "$app_gr_PID" || { + printf "\t%s: [ERR]\n" "$workload_gr" + panic "failed to wait -f ${app_gr_PID}" + return 1 + } + [ "$loadgen" = "hey" ] && (get_result_count "$results_directory/$workload_gr.csv" || { + printf "\t%s: [ERR]\n" "$workload_gr" + panic "$workload_gr has zero requests." + return 1 + }) + printf "\t%s: [OK]\n" "$workload_gr" + + wait -f "$app_ng_PID" || { + printf "\t%s: [ERR]\n" "$workload_ng" + panic "failed to wait -f ${app_ng_PID}" + return 1 + } + [ "$loadgen" = "hey" ] && (get_result_count "$results_directory/$workload_ng.csv" || { + printf "\t%s: [ERR]\n" "$workload_ng" + panic "$workload_ng has zero requests." + return 1 + }) + printf "\t%s: [OK]\n" "$workload_ng" + + if [ "$CLIENT_TERMINATE_SERVER" == true ]; then + printf "Sent a Terminator to the server\n" + echo "55" | http "$hostname":55555/terminator &> /dev/null + fi + + return 0 +} + +# Process the experimental results and generate human-friendly results for success rate, throughput, and latency +process_client_results_hey() { + assert_process_client_results_args "$@" + + local -r results_directory="$1" + + printf "Processing HEY Results: " + + # Write headers to CSVs + printf "Workload,Scs%%,TOTAL,ClientScs,All200,AllFail,Deny,MisDL,Shed,MiscErr\n" >> "$results_directory/success.csv" + printf "Workload,Throughput\n" >> "$results_directory/throughput.csv" + percentiles_table_header "$results_directory/latency.csv" "Workload" + # percentiles_table_header "$results_directory/latency-200.csv" "Workload" + + for workload in "${WORKLOADS[@]}"; do + + local -i deadline=${DEADLINES_us[$workload]} + + # Some requests come back with an "Unsolicited response ..." See issue #185 + misc_err=$(wc -l < "$results_directory/$workload-err.dat") + + # Calculate Success Rate for csv (percent of requests that return 200 within deadline) + awk -v misc_err="$misc_err" -F, ' + $7 == 200 && ($1 * 1000000) <= '"$deadline"' {ok++} + $7 == 200 {all200++} + $7 != 200 {total_failed++} + $7 == 429 {denied++} + $7 == 408 {missed_dl++} + $7 == 409 {killed++} + END{printf "'"$workload"',%3.1f,%d,%d,%d,%d,%d,%d,%d,%d\n", (ok/(NR-1+misc_err)*100), (NR-1+misc_err), ok, all200, (total_failed-1+misc_err), denied, missed_dl, killed, misc_err} + ' < "$results_directory/$workload.csv" >> "$results_directory/success.csv" + + # Convert from s to us, and sort + awk -F, 'NR > 1 {print ($1 * 1000000)}' < "$results_directory/$workload.csv" \ + | sort -g > "$results_directory/$workload-response.csv" + + # Filter on 200s, convert from s to us, and sort + awk -F, '$7 == 200 {print ($1 * 1000000)}' < "$results_directory/$workload.csv" \ + | sort -g > "$results_directory/$workload-response-200.csv" + + # Get Number of all entries + all=$(wc -l < "$results_directory/$workload-response.csv") + ((all == 0)) && continue # If all errors, skip line + + # Get Number of 200s + oks=$(wc -l < "$results_directory/$workload-response-200.csv") + # ((oks == 0)) && continue # If all errors, skip line + + # We determine duration by looking at the timestamp of the last complete request + # TODO: Should this instead just use the client-side synthetic DURATION_sec value? + duration=$(tail -n1 "$results_directory/$workload.csv" | cut -d, -f8) + + # Throughput is calculated as the mean number of successful requests per second + throughput=$(echo "$oks/$duration" | bc) + printf "%s,%d\n" "$workload" "$throughput" >> "$results_directory/throughput.csv" + + # Generate Latency Data for csv + percentiles_table_row "$results_directory/$workload-response.csv" "$results_directory/latency.csv" "$workload" + # percentiles_table_row "$results_directory/$workload-response-200.csv" "$results_directory/latency-200.csv" "$workload" + # Delete scratch file used for sorting/counting + rm "$results_directory/$workload-response.csv" "$results_directory/$workload-response-200.csv" + done + + # Transform csvs to dat files for gnuplot + csv_to_dat "$results_directory/success.csv" "$results_directory/throughput.csv" "$results_directory/latency.csv" + rm "$results_directory/success.csv" "$results_directory/throughput.csv" "$results_directory/latency.csv" + + printf "[OK]\n" + return 0 +} + +# Process the experimental results and generate human-friendly results for success rate, throughput, and latency +process_client_results_loadtest() { + assert_process_client_results_args "$@" + + local -r results_directory="$1" + + printf "Processing Loadtest Results: " + + # Write headers to CSVs + printf "Workload,Scs%%,TOTAL,All200,AllFail,Deny,MisDL,Shed,MiscErr\n" >> "$results_directory/success.csv" + printf "Workload,Throughput\n" >> "$results_directory/throughput.csv" + percentiles_table_header "$results_directory/latency.csv" "Workload" + + for workload in "${WORKLOADS[@]}"; do + + if [[ ! -f "$results_directory/$workload.dat" ]]; then + printf "[ERR]\n" + error_msg "Missing $results_directory/$workload.dat" + return 1 + fi + + # Get Number of 200s and then calculate Success Rate (percent of requests that return 200) + # If using loadtest -n option (not -t), then use all200/iterations instead of all200/total. + total=$(grep "Completed requests:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 13) + total_failed=$(grep "Total errors:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 13) + denied=$(grep "429:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12) + missed_dl=$(grep "408:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12) + killed=$(grep "409:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12) + misc_err=$(grep "\-1:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12) + all200=$((total-total_failed)) + + # ((all200 == 0)) && continue # If all errors, skip line + success_rate=$(echo "scale=2; $all200/$total*100"|bc) + printf "%s,%3.1f,%d,%d,%d,%d,%d,%d,%d\n" "$workload" "$success_rate" "$total" "$all200" "$total_failed" "$denied" "$missed_dl" "$killed" "$misc_err" >> "$results_directory/success.csv" + + # Throughput is calculated as the mean number of successful requests per second + duration=$(grep "Total time:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 13) + printf -v duration %.0f "$duration" + # throughput=$(grep "Requests per second" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 14 | tail -n 1) # throughput of ALL + throughput=$(echo "$all200/$duration" | bc) + printf "%s,%d\n" "$workload" "$throughput" >> "$results_directory/throughput.csv" + + # Generate Latency Data + min=0 # not provided by loadtest + p50=$(echo "$(grep 50% "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12)*1000" |bc) + p90=$(echo "$(grep 90% "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12)*1000" |bc) + p99=$(echo "$(grep 99% "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12)*1000" |bc) + p100=$(echo "$(grep 100% "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 12 | tail -n 1)*1000" |bc) + mean=$(echo "scale=1;$(grep "Mean latency:" "$results_directory/${workload}.dat" | tr -s ' ' | cut -d ' ' -f 13)*1000" |bc) + + printf "%s,%d,%d,%.1f,%d,%d,%d,%d\n" "$workload" "$total" "$min" "$mean" "$p50" "$p90" "$p99" "$p100" >> "$results_directory/latency.csv" + done + + # Transform csvs to dat files for gnuplot + csv_to_dat "$results_directory/success.csv" "$results_directory/throughput.csv" "$results_directory/latency.csv" + rm "$results_directory/success.csv" "$results_directory/throughput.csv" "$results_directory/latency.csv" + + printf "[OK]\n" + return 0 +} + +process_server_results() { + assert_process_server_results_args "$@" + + local -r results_directory="${1:?results_directory not set}" + + printf "Processing Server Results: \n" + + num_of_lines=$(wc -l < "$results_directory/$SERVER_LOG_FILE") + if [ "$num_of_lines" == 1 ]; then + printf "No results to process! Exiting the script." + return 1 + fi + + # Write headers to CSVs + printf "Workload,Scs%%,TOTAL,SrvScs,All200,AllFail,DenyAny,DenyG,MisDL_Glb,MisDL_Loc,Shed_Glb,Shed_Loc,QueFull\n" >> "$results_directory/success.csv" + printf "Workload,Throughput\n" >> "$results_directory/throughput.csv" + percentiles_table_header "$results_directory/latency.csv" "Workload" + + # Write headers to CSVs + for metric in "${SANDBOX_METRICS[@]}"; do + percentiles_table_header "$results_directory/$metric.csv" "Workload" + done + percentiles_table_header "$results_directory/running_user_200.csv" "Workload" + percentiles_table_header "$results_directory/running_user_nonzero.csv" "Workload" + percentiles_table_header "$results_directory/total_200.csv" "Workload" + # percentiles_table_header "$results_directory/memalloc.csv" "module" + + for workload in "${WORKLOADS[@]}"; do + mkdir "$results_directory/$workload" + + local -i deadline=${DEADLINES_us[$workload]} + + for metric in "${SANDBOX_METRICS[@]}"; do + awk -F, ' + {workload = sprintf("%s-%s", $'"$SANDBOX_TENANT_NAME_FIELD"', substr($'"$SANDBOX_ROUTE_FIELD"',2))} + workload == "'"$workload"'" {printf("%d,%d\n", $'"${SANDBOX_METRICS_FIELDS[$metric]}"' / $'"$SANDBOX_CPU_FREQ_FIELD"', $'"$SANDBOX_RESPONSE_CODE_FIELD"')} + ' < "$results_directory/$SERVER_LOG_FILE" | sort -g > "$results_directory/$workload/${metric}_sorted.csv" + + percentiles_table_row "$results_directory/$workload/${metric}_sorted.csv" "$results_directory/${metric}.csv" "$workload" + + # Delete scratch file used for sorting/counting + # rm "$results_directory/$workload/${metric}_sorted.csv" + done + + awk -F, '$2 == 200 {printf("%d,%d\n", $1, $2)}' < "$results_directory/$workload/running_user_sorted.csv" > "$results_directory/$workload/running_user_200_sorted.csv" + percentiles_table_row "$results_directory/$workload/running_user_200_sorted.csv" "$results_directory/running_user_200.csv" "$workload" + awk -F, '$1 > 0 {printf("%d,%d\n", $1, $2)}' < "$results_directory/$workload/running_user_sorted.csv" > "$results_directory/$workload/running_user_nonzero_sorted.csv" + percentiles_table_row "$results_directory/$workload/running_user_nonzero_sorted.csv" "$results_directory/running_user_nonzero.csv" "$workload" + awk -F, '$2 == 200 {printf("%d,%d\n", $1, $2)}' < "$results_directory/$workload/total_sorted.csv" > "$results_directory/$workload/total_200_sorted.csv" + percentiles_table_row "$results_directory/$workload/total_200_sorted.csv" "$results_directory/total_200.csv" "$workload" + + + # Memory Allocation + # awk -F, '$2 == "'"$workload"'" {printf("%.0f\n", $MEMORY_FIELD)}' < "$results_directory/$SERVER_LOG_FILE" | sort -g > "$results_directory/$workload/memalloc_sorted.csv" + + # percentiles_table_row "$results_directory/$workload/memalloc_sorted.csv" "$results_directory/memalloc.csv" "$workload" "%1.0f" + + # Calculate Success Rate for csv (percent of requests that complete), $1 and deadline are both in us, so not converting + awk -F, ' + $2 == 200 && $1 <= '"$deadline"' {ok++} + $2 == 200 {all200++} + $2 != 200 {total_failed++} + $2 == 4290 {denied_any++} + $2 == 4291 {denied_gtd++} + $2 == 4080 {mis_dl_glob++} + $2 == 4081 {mis_dl_local++} + $2 == 4090 {shed_glob++} + $2 == 4091 {shed_local++} + $2 == 999 {global_full++} + END{printf "'"$workload"',%3.1f,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", (ok/NR*100), NR, ok, all200, total_failed, denied_any, denied_gtd, mis_dl_glob, mis_dl_local, shed_glob, shed_local, global_full} + ' < "$results_directory/$workload/total_sorted.csv" >> "$results_directory/success.csv" + + # Throughput is calculated on the client side, so ignore the below line + printf "%s,%d\n" "$workload" "1" >> "$results_directory/throughput.csv" + + # Generate Latency Data for csv + percentiles_table_row "$results_directory/$workload/total_sorted.csv" "$results_directory/latency.csv" "$workload" + + # Delete scratch file used for sorting/counting + # rm "$results_directory/$workload/memalloc_sorted.csv" + + # Delete directory + # rm -rf "${results_directory:?}/${workload:?}" + + done + + # Transform csvs to dat files for gnuplot + for metric in "${SANDBOX_METRICS[@]}"; do + csv_to_dat "$results_directory/$metric.csv" + rm "$results_directory/$metric.csv" + done + csv_to_dat "$results_directory/running_user_200.csv" "$results_directory/running_user_nonzero.csv" "$results_directory/total_200.csv" + rm "$results_directory/running_user_200.csv" "$results_directory/running_user_nonzero.csv" "$results_directory/total_200.csv" + + # csv_to_dat "$results_directory/memalloc.csv" + csv_to_dat "$results_directory/success.csv" "$results_directory/throughput.csv" "$results_directory/latency.csv" + # rm "$results_directory/memalloc.csv" + rm "$results_directory/success.csv" "$results_directory/throughput.csv" "$results_directory/latency.csv" + + printf "[OK]\n" + return 0 +} + +process_server_http_results() { + assert_process_server_results_args "$@" + + local -r results_directory="${1:?results_directory not set}" + + printf "Processing Server HTTP Results: \n" + + num_of_lines=$(wc -l < "$results_directory/$SERVER_HTTP_LOG_FILE") + if [ "$num_of_lines" == 1 ]; then + printf "No results to process! Exiting the script." + return 1 + fi + + for metric in "${HTTP_METRICS[@]}"; do + percentiles_table_header "$results_directory/$metric.csv" "workload" + done + + for workload in "${WORKLOADS[@]}"; do + mkdir -p "$results_directory/$workload" + + for metric in "${HTTP_METRICS[@]}"; do + awk -F, ' + {workload = sprintf("%s-%s", $'"$HTTP_TENANT_NAME_FIELD"', substr($'"$HTTP_ROUTE_FIELD"',2))} + workload == "'"$workload"'" {printf("%.1f\n", $'"${HTTP_METRICS_FIELDS[$metric]}"' / $'"$HTTP_CPU_FREQ_FIELD"')} + ' < "$results_directory/$SERVER_HTTP_LOG_FILE" | sort -g > "$results_directory/$workload/${metric}_sorted.csv" + + percentiles_table_row "$results_directory/$workload/${metric}_sorted.csv" "$results_directory/${metric}.csv" "$workload" + + # Delete directory + # rm -rf "${results_directory:?}/${workload:?}" + done + done + + # Transform CSVs to dat files for gnuplot + for metric in "${HTTP_METRICS[@]}"; do + csv_to_dat "$results_directory/$metric.csv" + rm "$results_directory/$metric.csv" + done + + printf "[OK]\n" + return 0 +} + +experiment_server_post() { + local -r results_directory="$1" + + # Only process data if SLEDGE_SANDBOX_PERF_LOG was set when running sledgert + if [[ -n "$SLEDGE_SANDBOX_PERF_LOG" ]]; then + if [[ -f "$__run_sh__base_path/$SLEDGE_SANDBOX_PERF_LOG" ]]; then + mv "$__run_sh__base_path/$SLEDGE_SANDBOX_PERF_LOG" "$results_directory/$SERVER_LOG_FILE" + process_server_results "$results_directory" || return 1 + rm "$results_directory/$SLEDGE_SANDBOX_PERF_LOG" + else + echo "Sandbox Perf Log was set, but $SERVER_LOG_FILE not found!" + fi + fi + + if [[ -n "$SLEDGE_HTTP_SESSION_PERF_LOG" ]]; then + if [[ -f "$__run_sh__base_path/$SLEDGE_HTTP_SESSION_PERF_LOG" ]]; then + mv "$__run_sh__base_path/$SLEDGE_HTTP_SESSION_PERF_LOG" "$results_directory/$SERVER_HTTP_LOG_FILE" + process_server_http_results "$results_directory" || return 1 + rm "$results_directory/$SERVER_HTTP_LOG_FILE" + else + echo "HTTP Perf Log was set, but $SERVER_HTTP_LOG_FILE not found!" + fi + fi +} + +# Expected Symbol used by the framework +experiment_client() { + local -r target_hostname="$1" + local -r results_directory="$2" + local loadgen="$3" + + if [ "$loadgen" = "" ]; then + loadgen="hey" + echo "No load generator specified. So, defaulting to HEY!" + elif [ $loadgen = "lt" ]; then + loadgen="loadtest" + fi + + #run_samples "$target_hostname" "$loadgen" || return 1 + run_experiments "$target_hostname" "$results_directory" "$loadgen" || return 1 + + if [ "$loadgen" = "hey" ]; then + process_client_results_hey "$results_directory" || return 1 + elif [ "$loadgen" = "loadtest" ]; then + process_client_results_loadtest "$results_directory" || return 1 + else + echo "Unknown load generator \"$loadgen\" was entered!" + fi + + return 0 +} + +generate_spec_json +framework_init "$@" diff --git a/tests/mt_unimodal/spec.json b/tests/mt_unimodal/spec.json new file mode 100644 index 0000000..95d8a8e --- /dev/null +++ b/tests/mt_unimodal/spec.json @@ -0,0 +1,36 @@ +[ + { + "name": "GWU", + "port": 20030, + "replenishment-period-us": 16000, + "max-budget-us": 144000, + "routes": [ + { + "route": "/fib", + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 4000, + "relative-deadline-us": 16000, + "http-resp-size": 1024, + "http-resp-content-type": "text/plain" + } + ] + }, + { + "name": "NNN", + "port": 10030, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/fib", + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 4000, + "relative-deadline-us": 16000, + "http-resp-size": 1024, + "http-resp-content-type": "text/plain" + } + ] + } +] diff --git a/tests/mt_unimodal/template.json b/tests/mt_unimodal/template.json new file mode 100644 index 0000000..e5e2a81 --- /dev/null +++ b/tests/mt_unimodal/template.json @@ -0,0 +1,17 @@ +{ + "name": "tenant", + "port": 0, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/route", + "path": "fibonacci.wasm.so", + "admissions-percentile": 0, + "expected-execution-us": 0, + "relative-deadline-us": 0, + "http-resp-size": 1024, + "http-resp-content-type": "text/plain" + } + ] +}