diff --git a/.gitmodules b/.gitmodules index 6bf13ebca..2aa945212 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "jsmn"] path = runtime/thirdparty/jsmn url = https://github.com/gwsystems/jsmn.git +[submodule "eRPC"] + path = eRPC + url = https://github.com/lyuxiaosu/eRPC diff --git a/Makefile b/Makefile index 929f95a86..db40ffa6e 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ SHELL:=/bin/bash .PHONY: all -all: awsm libsledge runtime applications +all: awsm erpc libsledge runtime applications .PHONY: clean -clean: awsm.clean libsledge.clean runtime.clean applications.clean +clean: awsm.clean erpc.clean libsledge.clean runtime.clean applications.clean .PHONY: submodules submodules: @@ -16,7 +16,7 @@ install: submodules wasm_apps all # aWsm: the WebAssembly to LLVM bitcode compiler .PHONY: awsm awsm: - cd awsm && cargo build --release + cd awsm && git checkout f0b35e756395f79b06be8dd2660eecac94506e94 && cargo build --release .PHONY: awsm.clean awsm.clean: @@ -31,6 +31,16 @@ libsledge: libsledge.clean: make -C libsledge clean +.PHONY: erpc +erpc: + @echo "Building eRPC interface..." + cd eRPC/c_interface && ./build.sh + @echo "eRPC build complete." + +.PHONY: erpc.clean +erpc.clean: + cd eRPC/c_interface && make clean + # sledgert: the runtime that executes *.so modules .PHONY: runtime runtime: @@ -52,7 +62,7 @@ applications.clean: # Instead of having two copies of wasm_apps, just link to the awsm repo's copy wasm_apps: - ln -sr awsm/applications/wasm_apps/ applications/ + cd awsm/applications/wasm_apps && git checkout master && cd ../../../ && ln -sr awsm/applications/wasm_apps/ applications/ # Tests .PHONY: test diff --git a/README.md b/README.md index ee6e99f93..e9914274a 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,37 @@ -# SLEdge - -**SLEdge** is a lightweight serverless solution suitable for edge computing. It builds on WebAssembly sandboxing provided by the [aWsm compiler](https://github.com/gwsystems/aWsm). - -## Setting up a development environment - -### Native on Debian Host - -```sh -git clone https://github.com/gwsystems/sledge-serverless-framework.git -cd sledge-serverless-framework -./install_deb.sh -source ~/.bashrc -make install -make test -``` - -### Docker - -**Note: These steps require Docker. Make sure you've got it installed!** - -[Docker Installation Instructions](https://docs.docker.com/install/) - -We provide a Docker build environment configured with the dependencies and toolchain needed to build the SLEdge runtime and serverless functions. - -To setup this environment, run: - -```bash -./devenv.sh setup -``` - -### Using the Docker container to compile your serverless functions - -To enter the docker environment, run: - -```bash -./devenv.sh run -``` - -The first time you enter this environment, run the following to copy the sledgert binary to /sledge/runtime/bin. - -```bash -cd /sledge/runtime -make clean all -``` - +# SLEdgeScale + +**SLEdgeScale** is an ultra-low-latency, high-density, and task-deadline-aware serverless computing solution suitable for edge environments, extending **SLEdge**. It leverages WebAssembly sandboxing provided by the [aWsm compiler](https://github.com/gwsystems/aWsm) and kernel-bypass RPC offered by [eRPC](https://github.com/erpc-io/eRPC). + +## Setting up a development environment (Native on Debian Host) +SLEdgeScale was developed and tested on [Cloudlab](https://www.cloudlab.us) nodes (d6515) equipped with Mellanox NICs. A public [profile](https://www.cloudlab.us/p/GWCloudLab/sledge-rpc2) is available on CloudLab for easily creating a development environment for eRPC with node d6515. If you plan to set up the environment on machines with Intel NICs or other machines, please refer to the [eRPC](https://github.com/erpc-io/eRPC) repository for details about environment configuration, including driver and DPDK installation. For Mellanox NICs, please follow [this](https://docs.nvidia.com/networking/display/mlnxofedv590560125/installing+mlnx_ofed) guide to install *MLNX_OFED*. + +For using CloudLab profile to create the development environment: +Choose the [profile](https://www.cloudlab.us/p/GWCloudLab/sledge-rpc2) and use the following configuration: +Number of Nodes: 2 +Select OS image: SLEDGE +Optional physical node type : d6515 + +Now the environment is prepared for eRPC. The following steps are to build and install SLEdgeScale: +1. Git clone this repo and checkout branch *compare_dispatchers* +2. Extend the root filesystem: + ```sh + cd sledge-serverless-framework/runtime/tests + ./add_partition.sh + ``` +4. Move `sledge-serverless-framework` to `/my_mount/` +5. Disable multiple threads: + ```sh + cd sledge-serverless-framework/runtime/tests + sudo ./no_hyperthreads.sh + ``` +7. Build: + ```sh + cd sledge-serverless-framework + ./install_deb.sh + source $HOME/.cargo/env + source ~/.bashrc + make install + ``` There are a set of benchmarking applications in the `/sledge/applications` directory. Run the following to compile all benchmarks runtime tests using the aWsm compiler and then copy all resulting `.wasm.so` files to /sledge/runtime/bin. ```bash @@ -51,110 +39,182 @@ cd /sledge/applications/ make clean all ``` -You now have everything that you need to execute your first serverless function on SLEdge +All binary files are generated in `sledge-serverless-framework/runtime/bin`. You now have everything that you need to execute your first serverless function on SLEdgeScale -To exit the container: +## Running your first serverless function -```bash -exit -``` +An SLEdgeScale serverless function consists of a shared library (\*.so) and a JSON configuration file that determines how the runtime should execute the serverless function. We first need to prepare this configuration file. As an example, here is the configuration file for our sample fibonacci function: -To stop the Docker container: +```json +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/fib", + "request-type": 1, + "n-resas": 1, + "group-id": 1, + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + } + ] + + } -```bash -./devenv.sh stop +] ``` -### Deleting Docker Build Containers - -If you are finished working with the SLEdge runtime and wish to remove it, run the following command to delete our Docker build and runtime images. +`port`:Refers to the UDP port. +`request-type` and `path`: Used to determine which serverless function will be served; `request-type` must be unique per function. +`route`: An inherited field from SLEdge. It is not used currently but is kept to avoid parse errors. +`n-resas`: Specifies the number of CPU cores reserved for this serverless function. It is used by the DARC algorithm. +`group-id`: Specifies the group identifier used in the DARC algorithm. +`expected-execution-us`: Currently not used. SLEdgeScale will estimate execution time online. +`relative-deadline-us`: Specifies the request deadline in microseconds. +`http-resp-content-type`: Not used currently but is kept to avoid parse errors. -```bash -./devenv.sh rma -``` +### Start the SLEdgeScale Server +First, set the public IPs and ports for eRPC. Open `sledge-serverless-framework/eRPC/scripts/autorun_process_file` — the first line specifies the server IP and port, and the second line specifies the client IP and port. Make sure to apply the same change on the client machine as well. -And then simply delete this repository. +Then we need to export some environment variables before start the server. The commonly used environment variables are: -## Running your first serverless function +`SLEDGE_DISABLE_PREEMPTION`: Disables the timer that sends a SIGALRM signal every 5 ms for preemption. Must disable in SLEdgeScale. -An SLEdge serverless function consists of a shared library (\*.so) and a JSON configuration file that determines how the runtime should execute the serverless function. As an example, here is the configuration file for our sample fibonacci function: +`SLEDGE_DISPATCHER`: Specifies the dispatcher policy. There are seven types of dispatchers: +- SHINJUKU: Requests are enqueued to each dispatcher's typed queue. +- EDF_INTERRUPT: The dispatcher policy used by SLEdgeScale. +- DARC: Requests are enqueued to each dispatcher's typed queue. +- LLD: The dispatcher selects the worker with the least loaded queue to enqueue a request.. +- TO_GLOBAL_QUEUE: The dispatcher policy used by SLEdge. All dispatchers enqueue requests to a global queue. +- RR: The dispatcher selects a worker in a round-robin fashion. +- JSQ: The dispatcher selects the worker with the shortest queue to enqueue a request. + +`SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ`: Disable workers fetching requests from the global queue. Must be disabled if the dispatcher policy is not set to TO_GLOBAL_QUEUE. -```json -[ - { - "name": "GWU", - "port": 10010, - "routes": [ - { - "route": "/fib", - "path": "fibonacci.wasm.so", - "expected-execution-us": 6000, - "relative-deadline-us": 20000, - "http-resp-content-type": "text/plain" - } - ] - } -] +`SLEDGE_SCHEDULER`: Specifies the scheduler policy. There are two types of schedulers: +- FIFO: First-In-First-Out. Must use the TO_GLOBAL_QUEUE dispatch policy when using FIFO. +- EDF: Earliest-deadline-first. + +`SLEDGE_FIFO_QUEUE_BATCH_SIZE`: When using the FIFO scheduler, specifies how many requests are fetched from the global queue to the local queue each time the local queue becomes empty. -``` +`SLEDGE_DISABLE_BUSY_LOOP`: Disables the worker’s busy loop for fetching requests from the local or global queue. The busy loop must be enabled if the dispatcher policy is set to `TO_GLOBAL_QUEUE`. -The `port` and `route` fields are used to determine the path where our serverless function will be served served. +`SLEDGE_DISABLE_AUTOSCALING`: Currently not used;always set to `true`. -In our case, we are running the SLEdge runtime on localhost, so our function is available at `localhost:10010/fib`. +`SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION`: For the `hash` function, enabling this option allows SLEdgeScale to estimate the function’s execution time based on the input number. For other types of functions, this should be disabled. -Our fibonacci function will parse a single argument from the HTTP POST body that we send. The expected Content-Type is "text/plain". +`SLEDGE_FIRST_WORKER_COREID`: Specifies the ID of the first core for the worker thread. Cores 0–2 are reserved, so numbering should start from 3. -Now that we understand roughly how the SLEdge runtime interacts with serverless function, let's run Fibonacci! +`SLEDGE_NWORKERS`: The total number of workers in the system. -The fastest way to check it out is just to click on the following URL on your Web browser: [http://localhost:10010/fib?10](http://localhost:10010/fib?10) +`SLEDGE_NLISTENERS`: The total number of dispachers in the system. -From the root project directory of the host environment (not the Docker container!), navigate to the binary directory +`SLEDGE_WORKER_GROUP_SIZE`: The number of workers in each worker group. Its value is equal to SLEDGE_NWORKERS / SLEDGE_NLISTENERS -```bash -cd runtime/bin/ -``` +`SLEDGE_SANDBOX_PERF_LOG`: Server log file path -Now run the sledgert binary, passing the JSON file of the serverless function we want to serve. Because serverless functions are loaded by SLEdge as shared libraries, we want to add the `applications/` directory to LD_LIBRARY_PATH. +Now run the sledgert binary with the following script using sudo, passing the JSON file (e.g., the above Fibonacci function configuration) of the serverless function we want to serve. Because serverless functions are loaded by SLEdgeScale as shared libraries, we want to add the `applications/` directory to LD_LIBRARY_PATH: -```bash -LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../../tests/fibonacci/bimodal/spec.json +```sh +#!/bin/bash + +declare project_path="$( + cd "$(dirname "$0")/../.." + pwd +)" +echo $project_path +path=`pwd` +export SLEDGE_DISABLE_PREEMPTION=true +export SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ=true +export SLEDGE_FIFO_QUEUE_BATCH_SIZE=5 +export SLEDGE_DISABLE_BUSY_LOOP=true +export SLEDGE_DISABLE_AUTOSCALING=true +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=true +export SLEDGE_FIRST_WORKER_COREID=3 +export SLEDGE_NWORKERS=1 +export SLEDGE_NLISTENERS=1 +export SLEDGE_WORKER_GROUP_SIZE=1 +export SLEDGE_SCHEDULER=EDF +export SLEDGE_DISPATCHER=EDF_INTERRUPT +export SLEDGE_SANDBOX_PERF_LOG=$path/server.log + +cd $project_path/runtime/bin +LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/fib.json ``` +### Start the client to send requests +First git clone the client code: +```sh +git clone https://github.com/lyuxiaosu/eRPC.git +``` +There are several client implementations under eRPC/apps, and you can also create your own customized client. In our setup, we use `openloop_client`, which is an open-loop client that sends requests following a Poisson distribution. +Edit `autorun_app_file`, `autorun_process_file`, and build: +```sh +cd eRPC +echo "openloop_client" > ./scripts/autorun_app_file +./build.sh +``` +Our fibonacci function will parse a single argument from the rpc request that we send. Create the configuration file `eRPC/apps/openloop_client/conf` for `openloop_client`: +``` +--test_ms 10000 +--sm_verbose 0 +--num_server_threads 1 +--window_size 10 +--req_size 5 +--resp_size 32 +--num_processes 2 +--numa_0_ports 0 +--numa_1_ports 1,3 +--req_type 1 +--rps 1000 +--req_parameter 20 +--warmup_rps 200 +``` +`test_ms`: Define the test duration time in milliseconds. -While you don't see any output to the console, the runtime is running in the foreground. - -Let's now invoke our serverless function to compute the 10th fibonacci number. We'll use `cURL` and [HTTPie](https://httpie.org/) to send a HTTP GET and POST requests with the parameter we want to pass to my serverless function. Feel free to use whatever other network client you prefer! - -Open a **new** terminal session and execute the following +`num_server_threads`: Specifies how many dispatcher threads to run on the server. -```bash -# HTTP GET method: -http localhost:10010/fib?10 -curl localhost:10010/fib?10 +`req_size`: The size of the request package in bytes -# HTTP POST method: -echo "10" | http POST localhost:10010/fib -curl -i -d 10 localhost:10010/fib -``` +`resp_size`: The size of the response package in bytes -You should receive the following in response. The serverless function says that the 10th fibonacci number is 55, which seems to be correct! +`req_type`: The request type -```bash -HTTP/1.1 200 OK -Server: SLEdge -Connection: close -Content-Type: text/plain -Content-Length: 3 +`req_parameter`: The parameter carried by the request. Here is the fibonacci number. -55 -``` -When done, terminal the SLEdge runtime with `Ctrl+c` +Now we have everything, let's run Fibonacci! -## Running Test Workloads +```sh +cd eRPC +./scripts/do.sh 1 0 -Various synthetic and real-world tests can be found in `runtime/tests`. Generally, each experiment can be run by Make rules in the top level `test.mk`. +``` +The results is saved at `client.log`: +``` +thread id, type id, latency, cpu time +0 1 64.492000 23 +0 1 46.649000 23 +0 1 45.806000 23 +0 1 45.877000 22 +0 1 45.416000 22 +... +``` +The first column is the thread ID, the second column is the request type, the third column is the end-to-end latency in microseconds, and the fourth column is the execution time in microseconds. -`make -f test.mk all` +### High Density Test +Since the High Density experiment involves a large number of RPC types, we need to modify the maximum number of RPC types supported by eRPC, as well as some parts of the SLEdgeScale code. These changes are temporary and not part of the permanent code base. +Please run: +``` +./apply_patch.sh +``` +in the `eRPC` directory (on both the client and server sides) and in the `runtime` directory, and then recompile `eRPC` and the `runtime`. ## Problems or Feedback? diff --git a/applications/applications_Makefile_patch b/applications/applications_Makefile_patch new file mode 100644 index 000000000..efe3f7952 --- /dev/null +++ b/applications/applications_Makefile_patch @@ -0,0 +1,30 @@ +diff --git a/applications/Makefile b/applications/Makefile +index 1ec969c..08cd49c 100644 +--- a/applications/Makefile ++++ b/applications/Makefile +@@ -21,6 +21,9 @@ dist: + all: \ + cifar10.install \ + empty.install \ ++ sift.install \ ++ hash.install \ ++ multi_ncut.install \ + fibonacci.install \ + gocr.install \ + gps_ekf.install \ +@@ -90,6 +93,15 @@ exit.install: ../runtime/bin/exit.wasm.so + .PHONY: fibonacci.install + fibonacci.install: ../runtime/bin/fibonacci.wasm.so + ++.PHONY: multi_ncut.install ++fibonacci.install: ../runtime/bin/multi_ncut.wasm.so ++ ++.PHONY: sift.install ++fibonacci.install: ../runtime/bin/sift.wasm.so ++ ++.PHONY: hash.install ++hash.install: ../runtime/bin/hash.wasm.so ++ + .PHONY: asc-fib.install + asc-fib.install: ../runtime/bin/asc-fib.wasm.so + diff --git a/eRPC b/eRPC new file mode 160000 index 000000000..c608d9e54 --- /dev/null +++ b/eRPC @@ -0,0 +1 @@ +Subproject commit c608d9e54a2ea2dfb2dc758e1e3a1bf684dfd352 diff --git a/libsledge/include/sledge_abi.h b/libsledge/include/sledge_abi.h index 2b47f86ec..4c5556b80 100644 --- a/libsledge/include/sledge_abi.h +++ b/libsledge/include/sledge_abi.h @@ -256,3 +256,4 @@ int sledge_abi__scratch_storage_set(uint32_t key_offset, uint32_t key_len, uint3 int sledge_abi__scratch_storage_delete(uint32_t key_offset, uint32_t key_len); void sledge_abi__scratch_storage_upsert(uint32_t key_offset, uint32_t key_len, uint32_t value_offset, uint32_t value_len); +uint64_t sledge_abi__env_getcycles(); diff --git a/libsledge/src/sledge_extensions.c b/libsledge/src/sledge_extensions.c index 3687a4fe5..1d973e7e3 100644 --- a/libsledge/src/sledge_extensions.c +++ b/libsledge/src/sledge_extensions.c @@ -63,3 +63,12 @@ scratch_storage_upsert(uint32_t key_offset, uint32_t key_len, uint32_t value_off { sledge_abi__scratch_storage_upsert(key_offset, key_len, value_offset, value_len); } + +/* + * Return CPU cycles + */ +INLINE uint64_t +env_getcycles() { + return sledge_abi__env_getcycles(); +} + diff --git a/runtime/Makefile b/runtime/Makefile index a90f1b351..efecbcb16 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -13,8 +13,11 @@ CFLAGS=-std=c18 -pthread # sched_getcpu, MAP_ANONYMOUS, acceess to 'gregs' in 'mcontext_t', REG_RIP, REG_RSP CFLAGS+=-D_GNU_SOURCE +# Enable SLEDGE macro for erpc c interface build +CFLAGS+=-DSLEDGE # Release Flags CFLAGS+=-O3 -flto +#CFLAGS+=-O3 -flto -fsanitize=undefined -fno-sanitize-recover=undefined # Debugging Flags # CFLAGS+=-O0 -g3 @@ -92,9 +95,11 @@ BINARY_NAME=sledgert # This flag has a dependency on the flag HTTP_ROUTE_TOTAL_COUNTERS # CFLAGS += -DROUTE_LATENCY +# This flag enables print any log into memory buffer + CFLAGS += -DLOG_RUNTIME_MEM_LOG # This flag tracks the total number of sandboxes in the various states # It is useful to debug if sandboxes are "getting caught" in a particular state -# CFLAGS += -DSANDBOX_STATE_TOTALS + CFLAGS += -DSANDBOX_STATE_TOTALS # This flag enables an per-worker atomic count of sandbox's local runqueue count in thread local storage # Useful to debug if sandboxes are "getting caught" or "leaking" while in a local runqueue @@ -112,11 +117,15 @@ CFLAGS += -D${ARCH} # the runtime to execute. The export-dynamic Linker flag adds all globals to the dynamic symbol table, # allowing the libraries acess to such symbols. The libm math library is used several places, including # in backing functions that implement the WebAssembly instruction set. -LDFLAGS += -Wl,--export-dynamic -ldl -lm +LDFLAGS += -Wl,--whole-archive -ldpdk -Wl,--no-whole-archive -Wl,--export-dynamic -ldl -lm -lstdc++ -lnuma -ldl -libverbs -lmlx4 -lmlx5 +LDFLAGS += -L ../eRPC/c_interface/build # Our third-party dependencies build into a single dist directory to simplify configuration here. LDFLAGS += -Lthirdparty/dist/lib/ INCLUDES += -Iinclude/ -Ithirdparty/dist/include/ -I../libsledge/include/ +INCLUDES += -I../eRPC/c_interface +INCLUDES += -I /usr/include/dpdk + # CFILES CFILES += src/*.c @@ -130,7 +139,7 @@ JSMNCFLAGS += -DJSMN_STRICT # Force sledgert to rebuild when header files change # This is a bit fragile, as it does not recurse subdirectories when detecting last changed times -HEADER_DEPS = thirdparty/dist/include/*.h include/*.h include/arch/x86_64/*.h include/arch/aarch64/*.h +HEADER_DEPS = thirdparty/dist/include/*.h include/*.h include/arch/x86_64/*.h include/arch/aarch64/*.h ../eRPC/c_interface/*.h .PHONY: all all: thirdparty runtime @@ -142,7 +151,7 @@ clean: thirdparty.clean runtime.clean bin/${BINARY_NAME}: ${HEADER_DEPS} ${CFILES} @echo "Compiling runtime" @mkdir -p bin/ - @${CC} ${INCLUDES} ${CFLAGS} ${LDFLAGS} ${JSMNCFLAGS} -L/usr/lib/ ${CFILES} -o bin/${BINARY_NAME} + @${CC} ${INCLUDES} ${CFLAGS} ${LDFLAGS} ${JSMNCFLAGS} -L/usr/lib/ ${CFILES} -L../eRPC/c_interface/build -lerpc_c_interface -o bin/${BINARY_NAME} .PHONY: runtime runtime: bin/${BINARY_NAME} diff --git a/runtime/apply_patch.sh b/runtime/apply_patch.sh new file mode 100755 index 000000000..2dcf8f81a --- /dev/null +++ b/runtime/apply_patch.sh @@ -0,0 +1 @@ +git apply increase_req_type.patch diff --git a/runtime/delete_patch.sh b/runtime/delete_patch.sh new file mode 100755 index 000000000..27c4ce593 --- /dev/null +++ b/runtime/delete_patch.sh @@ -0,0 +1 @@ +git apply -R increase_req_type.patch diff --git a/runtime/include/admissions_info.h b/runtime/include/admissions_info.h index d2e0dfb2a..13f31395f 100644 --- a/runtime/include/admissions_info.h +++ b/runtime/include/admissions_info.h @@ -1,4 +1,5 @@ #pragma once +#include #include "perf_window_t.h" @@ -8,8 +9,10 @@ struct admissions_info { int control_index; /* Precomputed Lookup index when perf_window is full */ uint64_t estimate; /* cycles */ uint64_t relative_deadline; /* Relative deadline in cycles. This is duplicated state */ + uint32_t uid; /* unique id to identify an admissions_info at each thread */ }; -void admissions_info_initialize(struct admissions_info *admissions_info, uint8_t percentile, +void admissions_info_initialize(struct admissions_info *admissions_info, uint8_t uid, uint8_t percentile, uint64_t expected_execution, uint64_t relative_deadline); void admissions_info_update(struct admissions_info *admissions_info, uint64_t execution_duration); +void perf_window_per_thread_update(struct admissions_info *admissions_info, uint64_t execution_duration); diff --git a/runtime/include/binary_search_tree.h b/runtime/include/binary_search_tree.h new file mode 100644 index 000000000..8116810ca --- /dev/null +++ b/runtime/include/binary_search_tree.h @@ -0,0 +1,421 @@ +#include +#include +#include + +#include "memlogging.h" +#include "lock.h" + +#define MAX_NODES 4096 // Maximum number of nodes in the pool + +typedef uint64_t (*binary_tree_get_priority_fn_t)(void *data); +typedef uint64_t (*binary_tree_get_execution_cost_fn_t)(void *data, int thread_id); + +// Definition of a binary search tree node +struct TreeNode { + struct TreeNode *left; + struct TreeNode *right; + struct TreeNode *next; // pointing to the next node, this is used for nodePool + // to find next available node + struct TreeNode *dup_next; // pointing to next duplicate key item + uint64_t left_subtree_sum; + void *data; // sandbox +}; + +// Definition of TreeNode memory pool +struct TreeNodePool { + struct TreeNode* head; +}; + +struct binary_tree { + struct TreeNode *root; + struct TreeNodePool nodePool; + binary_tree_get_priority_fn_t get_priority_fn; + binary_tree_get_execution_cost_fn_t get_execution_cost_fn; + lock_t lock; + bool use_lock; + int id; + int queue_length; +}; + +// Initialize the node pool +void initNodePool(struct TreeNodePool *nodePool, int pool_size) { + + assert(nodePool != NULL); + + struct TreeNode *nodes = (struct TreeNode*)malloc(pool_size * sizeof(struct TreeNode)); + nodePool->head = nodes; // Initialize head to the beginning of the node array + + for (int i = 0; i < MAX_NODES - 1; ++i) { + nodes[i].next = &nodes[i + 1]; // Set the next pointer of each node to the next node + nodes[i].left = NULL; + nodes[i].right = NULL; + nodes[i].dup_next = NULL; + nodes[i].left_subtree_sum = 0; + nodes[i].data = NULL; + } + nodes[MAX_NODES - 1].next = NULL; +} + +struct binary_tree * init_binary_tree(bool use_lock, binary_tree_get_priority_fn_t get_priority_fn, + binary_tree_get_execution_cost_fn_t get_execution_cost_fn, int id, int queue_size) { + + assert(get_priority_fn != NULL); + + struct binary_tree *binary_tree = (struct binary_tree *)calloc(1, sizeof(struct binary_tree)); + initNodePool(&binary_tree->nodePool, queue_size); + binary_tree->root = NULL; + binary_tree->get_priority_fn = get_priority_fn; + binary_tree->get_execution_cost_fn = get_execution_cost_fn; + binary_tree->use_lock = use_lock; + binary_tree->id = id; + binary_tree->queue_length = 0; + + if (binary_tree->use_lock) lock_init(&binary_tree->lock); + + return binary_tree; +} + +// Function to get the total number of non-deleted nodes in the binary tree +int getNonDeletedNodeCount(struct binary_tree *binary_tree) { + assert(binary_tree != NULL); + return binary_tree->queue_length; +} + +// Get a new node from the pool +struct TreeNode* newNode(struct binary_tree *binary_tree, void *data) { + + assert(binary_tree != NULL); + + if (binary_tree->nodePool.head == NULL) { + panic("Binary search tree queue %d is full\n", binary_tree->id); + return NULL; + } else { + // Remove a node from the head of the memory pool + struct TreeNode* new_node_t = binary_tree->nodePool.head; + binary_tree->nodePool.head = new_node_t->next; + new_node_t->next = NULL; // Reset the next pointer of the new node + new_node_t->data = data; + new_node_t->left = NULL; + new_node_t->right = NULL; + new_node_t->dup_next = NULL; + new_node_t->left_subtree_sum = 0; + return new_node_t; + } +} + +void getAvailableCapacity(struct binary_tree *binary_tree) { + + assert(binary_tree != NULL); + + int size = 0; + struct TreeNode* start = binary_tree->nodePool.head; + while(start) { + size++; + start = start->next; + } + + printf("available capacity of the queue is %d\n", size); +} + +void print_in_order(struct TreeNode* node) { + if (node != NULL) { + // Recursively traverse the left subtree + print_in_order(node->left); + + // Print the data in the current node + if (node->data) { + mem_log("%lu(%lu) ", ((struct sandbox *)node->data)->absolute_deadline, ((struct sandbox *)node->data)->estimated_cost); + } + //Print data in the dup_next list + struct TreeNode* cur = node->dup_next; + while(cur) { + mem_log("%lu(%lu) ", ((struct sandbox *)cur->data)->absolute_deadline, ((struct sandbox *)cur->data)->estimated_cost); + cur = cur->dup_next; + } + + // Recursively traverse the right subtree + print_in_order(node->right); + } +} + +// Function to print the items in the binary search tree in order +void print_tree_in_order(struct binary_tree* bst) { + if (bst != NULL) { + print_in_order(bst->root); + mem_log("\n"); + } +} + +//get the total execute time of a node. +uint64_t get_sum_exe_time_of_node(struct binary_tree *binary_tree, struct TreeNode* node, int thread_id){ + uint64_t total = 0; + struct TreeNode* curNode = node; + while (curNode!=NULL) { + total += binary_tree->get_execution_cost_fn(curNode->data, thread_id); + curNode = curNode->dup_next; + } + + return total; +} + +// Return a node to the pool +void deleteNode(struct binary_tree *binary_tree, struct TreeNode* node) { + + assert(binary_tree != NULL); + assert(node != NULL); + + // Insert the node back to the head of the memory pool + node->left = NULL; + node->right = NULL; + node->data = NULL; + node->left_subtree_sum = 0; + node->dup_next = NULL; + node->next = binary_tree->nodePool.head; + binary_tree->nodePool.head = node; +} + +int findHeight(struct TreeNode *root) +{ + int lefth, righth; + if(root == NULL) + return 0; + lefth = findHeight(root->left); + righth = findHeight(root->right); + return (lefth > righth ? lefth : righth)+1; +} + +// Function to insert a value into a binary search tree +struct TreeNode* insert(struct binary_tree *binary_tree, struct TreeNode* root, void *data, int thread_id) { + + assert(binary_tree != NULL); + + if (root == NULL) { + binary_tree->queue_length++; + return newNode(binary_tree, data); // Create a new node for an empty tree + } + + if (binary_tree->get_priority_fn(data) == binary_tree->get_priority_fn(root->data)) { + //go to the tail of clone chain + struct TreeNode* tail = root; + while (tail->dup_next != NULL){ + tail = tail->dup_next; + } + + //append the new node to the chain + struct TreeNode* new_node = newNode(binary_tree, data); + tail->dup_next = new_node; + binary_tree->queue_length++; + return root; + } + + if (binary_tree->get_priority_fn(data) < binary_tree->get_priority_fn(root->data)) { + root->left = insert(binary_tree, root->left, data, thread_id); // Insert into the left subtree + root->left_subtree_sum += binary_tree->get_execution_cost_fn(data, thread_id); + } else { + root->right = insert(binary_tree, root->right, data, thread_id); // Insert into the right subtree + } + return root; +} + +// Helper function to find the minimum value in a binary search tree +struct TreeNode* findMin(struct binary_tree *binary_tree, struct TreeNode *root) { + + assert(binary_tree != NULL); + + if (root == NULL) { + return NULL; + } + + if (binary_tree->queue_length == 1) { + return root; + } + + lock_node_t node = {}; + lock_lock(&binary_tree->lock, &node); + while (root->left != NULL) { + root = root->left; // Keep traversing to the left until the leftmost node is reached + } + lock_unlock(&binary_tree->lock, &node); + return root; +} + +// Helper function to find the maximum value in a binary search tree +struct TreeNode* findMax(struct binary_tree *binary_tree, struct TreeNode *root) { + if (root == NULL) { + return NULL; + } + + lock_node_t node = {}; + lock_lock(&binary_tree->lock, &node); + while (root->right != NULL) { + root = root->right; // Keep traversing to the right until the rightmost node is reached + } + lock_unlock(&binary_tree->lock, &node); + return root; +} + +struct TreeNode* remove_node_from_dup_list(struct binary_tree *binary_tree, struct TreeNode* root, void *data, bool *deleted) { + if (root->data == data) { + root->dup_next->left = root->left; + root->dup_next->right = root->right; + root->dup_next->left_subtree_sum = root->left_subtree_sum; + //free old root + struct TreeNode* new_root = root->dup_next; + deleteNode(binary_tree, root); + *deleted = true; + binary_tree->queue_length--; + return new_root; + } else { + struct TreeNode* cur = root->dup_next; + struct TreeNode* pre = root; + while(cur) { + if (cur->data == data) { + pre->dup_next = cur->dup_next; + //free cur + deleteNode(binary_tree, cur); + *deleted = true; + binary_tree->queue_length--; + return root; + } else { + pre = cur; + cur = cur->dup_next; + } + } + *deleted = false; + return root; + } +} + +// Function to delete a value from a binary search tree +struct TreeNode* delete_i(struct binary_tree *binary_tree, struct TreeNode* root, void *data, bool *deleted, int thread_id) { + + assert(binary_tree != NULL); + + if (root == NULL) { + *deleted = false; + return NULL; + } + + int64_t cmp_result = binary_tree->get_priority_fn(data) - binary_tree->get_priority_fn(root->data); + if (cmp_result < 0) { + root->left_subtree_sum -= binary_tree->get_execution_cost_fn(data, thread_id); + root->left = delete_i(binary_tree, root->left, data, deleted, thread_id); + return root; + } else if (cmp_result > 0) { + root->right = delete_i(binary_tree, root->right, data, deleted, thread_id); + return root; + } else { // cmp_result == 0 + // The deleted node might either be the root or in the dup_next list + if (root->dup_next != NULL) { + struct TreeNode* new_root = remove_node_from_dup_list(binary_tree, root, data, deleted); + return new_root; + } + // If key is same as root's key, then this is the node to be deleted + // Node with only one child or no child + if (root->left == NULL) { + struct TreeNode* temp = root->right; + deleteNode(binary_tree, root); + *deleted = true; + binary_tree->queue_length--; + return temp; + } else if (root->right == NULL) { + struct TreeNode* temp = root->left; + deleteNode(binary_tree, root); + *deleted = true; + binary_tree->queue_length--; + return temp; + } else { + // Node with two children: Get the inorder successor(smallest in the right subtree) + struct TreeNode* succParent = root; + struct TreeNode* succ = root->right; + while (succ->left != NULL) { + succParent = succ; + succ = succ->left; + } + // Copy the inorder successor's content to this node, left_subtree_sum is not changed + root->data = succ->data; + root->dup_next = succ->dup_next; + //update the sum_less_than of the nodes affected by the removed node. + int removed_exe_time = get_sum_exe_time_of_node(binary_tree, succ, thread_id); + struct TreeNode* temp = root->right; + while (temp->left != NULL) { + temp->left_subtree_sum -= removed_exe_time; + temp = temp->left; + } + + // Delete the inorder successor + if (succParent->left == succ) { + succParent->left = succ->right; + } else { + succParent->right = succ->right; + } + + deleteNode(binary_tree, succ); + *deleted = true; + binary_tree->queue_length--; + return root; + } + + } + +} + +// Function to find a value in a binary search tree (non-recursive) +/*struct TreeNode* find(struct TreeNode* root, int val) { + while (root != NULL) { + if (val == root->val) { + return root; // Return the node if value is found + } else if (val < root->val) { + root = root->left; // Move to left subtree if value is less + } else { + root = root->right; // Move to right subtree if value is greater + } + } + return NULL; // Return NULL if value is not found or if the tree is empty +}*/ + +bool is_empty(struct binary_tree *binary_tree) { + assert(binary_tree != NULL); + + return binary_tree->root == NULL; +} + +void inorder(struct binary_tree *binary_tree, struct TreeNode* root) +{ + assert(binary_tree != NULL); + + if(root == NULL) + return; + inorder(binary_tree, root->left); + printf("%lu ", binary_tree->get_priority_fn(root->data)); + inorder(binary_tree, root->right); +} +// return the sum of nodes' execution time that less than the target priority +uint64_t findMaxValueLessThan(struct binary_tree *binary_tree, struct TreeNode* root, void *target, int thread_id) { + assert(binary_tree != NULL); + + if (root == NULL) { + return 0; + } + if (binary_tree->get_priority_fn(target) == binary_tree->get_priority_fn(root->data)) { + return root->left_subtree_sum; + } else if (binary_tree->get_priority_fn(target) < binary_tree->get_priority_fn(root->data)) { + return findMaxValueLessThan(binary_tree, root->left, target, thread_id); + } else { + return get_sum_exe_time_of_node(binary_tree, root, thread_id) + root->left_subtree_sum + findMaxValueLessThan(binary_tree, root->right, target, thread_id); + } + +} + +struct TreeNode* makeEmpty(struct binary_tree *binary_tree, struct TreeNode* root) +{ + assert(binary_tree != NULL); + + if(root != NULL) { + makeEmpty(binary_tree, root->left); + makeEmpty(binary_tree, root->right); + deleteNode(binary_tree, root); + } + return NULL; +} + diff --git a/runtime/include/binary_search_tree.h_redblacktree b/runtime/include/binary_search_tree.h_redblacktree new file mode 100644 index 000000000..2b5936086 --- /dev/null +++ b/runtime/include/binary_search_tree.h_redblacktree @@ -0,0 +1,630 @@ +#include +#include +#include + +#include "memlogging.h" +#include "lock.h" + +#define MAX_NODES 4096 // Maximum number of nodes in the pool + +typedef uint64_t (*binary_tree_get_priority_fn_t)(void *data); +typedef uint64_t (*binary_tree_get_execution_cost_fn_t)(void *data, int thread_id); + +// Define the colors for Red-Black Tree +typedef enum { RED, BLACK } Color; + +// Definition of a binary search tree node +struct TreeNode { + struct TreeNode *left; + struct TreeNode *right; + struct TreeNode *next; // pointing to the next node, this is used for nodePool + // to find next available node + struct TreeNode *dup_next; // pointing to next duplicate key item + struct TreeNode *parent; + Color color; + uint64_t left_subtree_sum; + void *data; // sandbox +}; + +// Definition of TreeNode memory pool +struct TreeNodePool { + struct TreeNode* head; +}; + +struct binary_tree { + struct TreeNode *root; + struct TreeNodePool nodePool; + binary_tree_get_priority_fn_t get_priority_fn; + binary_tree_get_execution_cost_fn_t get_execution_cost_fn; + lock_t lock; + bool use_lock; + int id; + int queue_length; +}; + +// Initialize the node pool +void initNodePool(struct TreeNodePool *nodePool, int pool_size) { + + assert(nodePool != NULL); + + struct TreeNode *nodes = (struct TreeNode*)malloc(pool_size * sizeof(struct TreeNode)); + nodePool->head = nodes; // Initialize head to the beginning of the node array + + for (int i = 0; i < MAX_NODES - 1; ++i) { + nodes[i].next = &nodes[i + 1]; // Set the next pointer of each node to the next node + nodes[i].left = NULL; + nodes[i].right = NULL; + nodes[i].dup_next = NULL; + nodes[i].parent = NULL; + nodes[i].color = RED; + nodes[i].left_subtree_sum = 0; + nodes[i].data = NULL; + } + nodes[MAX_NODES - 1].next = NULL; +} + +struct binary_tree * init_binary_tree(bool use_lock, binary_tree_get_priority_fn_t get_priority_fn, + binary_tree_get_execution_cost_fn_t get_execution_cost_fn, int id, int queue_size) { + + assert(get_priority_fn != NULL); + + struct binary_tree *binary_tree = (struct binary_tree *)calloc(1, sizeof(struct binary_tree)); + initNodePool(&binary_tree->nodePool, queue_size); + binary_tree->root = NULL; + binary_tree->get_priority_fn = get_priority_fn; + binary_tree->get_execution_cost_fn = get_execution_cost_fn; + binary_tree->use_lock = use_lock; + binary_tree->id = id; + binary_tree->queue_length = 0; + + if (binary_tree->use_lock) lock_init(&binary_tree->lock); + + return binary_tree; +} + +// Function to get the total number of non-deleted nodes in the binary tree +int getNonDeletedNodeCount(struct binary_tree *binary_tree) { + assert(binary_tree != NULL); + return binary_tree->queue_length; +} + +// Get a new node from the pool +struct TreeNode* newNode(struct binary_tree *binary_tree, Color color, struct TreeNode* left, + struct TreeNode* right, struct TreeNode* parent, void *data) { + + assert(binary_tree != NULL); + + if (binary_tree->nodePool.head == NULL) { + panic("Binary search tree queue %d is full\n", binary_tree->id); + return NULL; + } else { + // Remove a node from the head of the memory pool + struct TreeNode* new_node_t = binary_tree->nodePool.head; + binary_tree->nodePool.head = new_node_t->next; + new_node_t->next = NULL; // Reset the next pointer of the new node + new_node_t->data = data; + new_node_t->left = left; + new_node_t->right = right; + new_node_t->parent = parent; + new_node_t->color = color; + new_node_t->dup_next = NULL; + new_node_t->left_subtree_sum = 0; + return new_node_t; + } +} + +void getAvailableCapacity(struct binary_tree *binary_tree) { + + assert(binary_tree != NULL); + + int size = 0; + struct TreeNode* start = binary_tree->nodePool.head; + while(start) { + size++; + start = start->next; + } + + printf("available capacity of the queue is %d\n", size); +} + +void print_in_order(struct TreeNode* node) { + if (node != NULL) { + // Recursively traverse the left subtree + print_in_order(node->left); + + // Print the data in the current node + if (node->data) { + mem_log("%lu(%lu) ", ((struct sandbox *)node->data)->absolute_deadline, ((struct sandbox *)node->data)->estimated_cost); + } + //Print data in the dup_next list + struct TreeNode* cur = node->dup_next; + while(cur) { + mem_log("%lu(%lu) ", ((struct sandbox *)cur->data)->absolute_deadline, ((struct sandbox *)cur->data)->estimated_cost); + cur = cur->dup_next; + } + + // Recursively traverse the right subtree + print_in_order(node->right); + } +} + +// Function to print the items in the binary search tree in order +void print_tree_in_order(struct binary_tree* bst) { + if (bst != NULL) { + print_in_order(bst->root); + mem_log("\n"); + } +} + +//get the total execute time of a node. +uint64_t get_sum_exe_time_of_node(struct binary_tree *binary_tree, struct TreeNode* node, int thread_id){ + uint64_t total = 0; + struct TreeNode* curNode = node; + while (curNode!=NULL) { + total += binary_tree->get_execution_cost_fn(curNode->data, thread_id); + curNode = curNode->dup_next; + } + + return total; +} + +//get the total execute time of a node's subtree, including left and right subtree +uint64_t get_sum_exe_time_of_subtree(struct binary_tree *binary_tree, struct TreeNode* root, int thread_id) { + if (root == NULL) { + return 0; + } + + return get_sum_exe_time_of_node(binary_tree, root, thread_id) + + root->left_subtree_sum + get_sum_exe_time_of_subtree(binary_tree, root->right, thread_id); +} + + +// Return a node to the pool +void deleteNode(struct binary_tree *binary_tree, struct TreeNode* node) { + + assert(binary_tree != NULL); + assert(node != NULL); + + // Insert the node back to the head of the memory pool + node->left = NULL; + node->right = NULL; + node->parent = NULL; + node->color = RED; + node->data = NULL; + node->left_subtree_sum = 0; + node->dup_next = NULL; + node->next = binary_tree->nodePool.head; + binary_tree->nodePool.head = node; +} + +int findHeight(struct TreeNode *root) +{ + int lefth, righth; + if(root == NULL) + return 0; + lefth = findHeight(root->left); + righth = findHeight(root->right); + return (lefth > righth ? lefth : righth)+1; +} + +// Update root if rotating. +void leftRotate(struct binary_tree *binary_tree, struct TreeNode *x, int thread_id) { + struct TreeNode *y = x->right; + x->right = y->left; + if (y->left != NULL) + y->left->parent = x; + + y->parent = x->parent; + + if (x->parent == NULL) + binary_tree->root = y; + else if (x == x->parent->left) + x->parent->left = y; + else + x->parent->right = y; + y->left = x; + x->parent = y; + y->left_subtree_sum += binary_tree->get_execution_cost_fn(x->data, thread_id) + x->left_subtree_sum; +} + +// Update root if rotatiing +void rightRotate(struct binary_tree *binary_tree, struct TreeNode *x, int thread_id) { + struct TreeNode *y = x->left; + x->left = y->right; + + int new_sum_left = 0; + if (y->right != NULL) { + y->right->parent = x; + new_sum_left = get_sum_exe_time_of_subtree(binary_tree, y->right, thread_id); //y->right->exe_time + y->right->sum_left; + } + + x->left_subtree_sum = new_sum_left; + y->parent = x->parent; + if (x->parent == NULL) + binary_tree->root = y; + else if (x == x->parent->right) + x->parent->right = y; + else + x->parent->left = y; + y->right = x; + x->parent = y; +} + +void insertFixup(struct binary_tree *binary_tree, struct TreeNode *z, int thread_id) { + while (z->parent != NULL && z->parent->color == RED) { + if (z->parent == z->parent->parent->left) { + struct TreeNode *y = z->parent->parent->right; + if (y != NULL && y->color == RED) { + z->parent->color = BLACK; + y->color = BLACK; + z->parent->parent->color = RED; + z = z->parent->parent; + } else { + if (z == z->parent->right) { + z = z->parent; + leftRotate(binary_tree, z, thread_id); + } + z->parent->color = BLACK; + z->parent->parent->color = RED; + rightRotate(binary_tree, z->parent->parent, thread_id); + } + } else { + struct TreeNode *y = z->parent->parent->left; + if (y != NULL && y->color == RED) { + z->parent->color = BLACK; + y->color = BLACK; + z->parent->parent->color = RED; + z = z->parent->parent; + } else { + if (z == z->parent->left) { + z = z->parent; + rightRotate(binary_tree, z, thread_id); + } + z->parent->color = BLACK; + z->parent->parent->color = RED; + leftRotate(binary_tree, z->parent->parent, thread_id); + } + } + } + binary_tree->root->color = BLACK; +} + +// Function to insert a value into a binary search tree. Tree root might be changed after inserting a new node +void insert(struct binary_tree *binary_tree, void *data, int thread_id) { + assert(binary_tree != NULL); + + struct TreeNode *z = newNode(binary_tree, RED, NULL, NULL, NULL, data); + binary_tree->queue_length++; + struct TreeNode *y = NULL; + struct TreeNode *x = binary_tree->root; + while (x != NULL) { + y = x; + + //if found a dup_next deadline, inserted the node + //at the end of the linklist + if (binary_tree->get_priority_fn(z->data) == binary_tree->get_priority_fn(x->data)) { + //find the tail the link list; + struct TreeNode* tail = x; + while (tail->dup_next != NULL) { + tail = tail->dup_next; + } + //append the new node at the end of the list; + tail->dup_next = z; + z->color = x->color; + z->left_subtree_sum = x->left_subtree_sum; + return; + } else if (binary_tree->get_priority_fn(z->data) < binary_tree->get_priority_fn(x->data)) { + x->left_subtree_sum += binary_tree->get_execution_cost_fn(data, thread_id); + x = x->left; + } else { + x = x->right; + } + } + + z->parent = y; + if (y == NULL) { + binary_tree->root = z; + } else if (binary_tree->get_priority_fn(z->data) < binary_tree->get_priority_fn(y->data)) { + y->left = z; + } else { + y->right = z; + } + + insertFixup(binary_tree, z, thread_id); +} + +// Helper function to find the minimum value in a binary search tree +struct TreeNode* findMin(struct binary_tree *binary_tree) { + + assert(binary_tree != NULL); + + struct TreeNode *curNode = binary_tree->root; + if (curNode == NULL) { + return NULL; + } + + while (curNode->left != NULL) { + curNode = curNode->left; // Keep traversing to the left until the leftmost node is reached + } + return curNode; +} + +// Helper function to find the maximum value in a binary search tree +struct TreeNode* findMax(struct binary_tree *binary_tree) { + + assert(binary_tree != NULL); + + struct TreeNode *curNode = binary_tree->root; + if (curNode == NULL) { + return NULL; + } + + while (curNode->right != NULL) { + curNode = curNode->right; // Keep traversing to the right until the rightmost node is reached + } + return curNode; +} + +struct TreeNode* searchByKey(struct binary_tree *binary_tree, void *data) { + struct TreeNode* current = binary_tree->root; + + while (current != NULL && binary_tree->get_priority_fn(current->data) != binary_tree->get_priority_fn(data)) { + if (binary_tree->get_priority_fn(data) < binary_tree->get_priority_fn(current->data)) { + current = current->left; + } else { + current = current->right; + } + } + + return current; +} + +void transplant(struct binary_tree *binary_tree, struct TreeNode *u, struct TreeNode *v) { + if (u->parent == NULL) + binary_tree->root = v; + else if (u == u->parent->left) + u->parent->left = v; + else + u->parent->right = v; + if (v != NULL) + v->parent = u->parent; +} + +struct TreeNode* minimum(struct TreeNode *node) { + while (node->left != NULL) { + node = node->left; + } + return node; +} + +void deleteFixup(struct binary_tree *binary_tree, struct TreeNode *x, int thread_id) { + while (x != binary_tree->root && (x == NULL || x->color == BLACK)) { + + if (x == NULL){ + break; + } + + if ( x == x->parent->left) { + struct TreeNode *w = x->parent->right; + if (w->color == RED) { + w->color = BLACK; + x->parent->color = RED; + leftRotate(binary_tree, x->parent, thread_id); + w = x->parent->right; + } + if ((w->left == NULL || w->left->color == BLACK) && (w->right == NULL || w->right->color == BLACK)) { + w->color = RED; + x = x->parent; + } else { + if (w->right == NULL || w->right->color == BLACK) { + if (w->left != NULL) w->left->color = BLACK; + w->color = RED; + rightRotate(binary_tree, w, thread_id); + w = x->parent->right; + } + w->color = x->parent->color; + x->parent->color = BLACK; + if (w->right != NULL) w->right->color = BLACK; + leftRotate(binary_tree, x->parent, thread_id); + x = binary_tree->root; + } + } else { + struct TreeNode *w = x->parent->left; + if (w->color == RED) { + w->color = BLACK; + x->parent->color = RED; + rightRotate(binary_tree, x->parent, thread_id); + w = x->parent->left; + } + if ((w->right == NULL || w->right->color == BLACK) && (w->left == NULL || w->left->color == BLACK)) { + w->color = RED; + x = x->parent; + } else { + if (w->left == NULL || w->left->color == BLACK) { + if (w->right != NULL) w->right->color = BLACK; + w->color = RED; + leftRotate(binary_tree, w, thread_id); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = BLACK; + if (w->left != NULL) w->left->color = BLACK; + rightRotate(binary_tree, x->parent, thread_id); + x = binary_tree->root; + } + } + } + if (x != NULL) x->color = BLACK; +} + +void removeNode(struct binary_tree *binary_tree, struct TreeNode *z, int thread_id) { + struct TreeNode *y = z; + struct TreeNode *x = NULL; + Color y_original_color = y->color; + + if ( z->left != NULL && z->right != NULL ) { + y = minimum(z->right); + + int exe_time_y = get_sum_exe_time_of_node(binary_tree, y, thread_id); + struct TreeNode* cur = z->right; + while (cur != y) { + cur->left_subtree_sum -= exe_time_y; + cur = cur->left; + } + + void *remove_data = z->data; + z->data = y->data; + z->dup_next = y->dup_next; + y->data = remove_data; + y->dup_next = NULL; + + removeNode(binary_tree, y, thread_id); + return; + } + + if (z->left == NULL) { + x = z->right; + transplant(binary_tree, z, z->right); + + } else if (z->right == NULL) { + x = z->left; + transplant(binary_tree, z, z->left); + + } + + deleteNode(binary_tree, z); + if (y_original_color == BLACK) { + deleteFixup(binary_tree, x, thread_id); + } +} + +// Function to delete a value from a binary search tree +void delete_i(struct binary_tree *binary_tree, void *data, bool *deleted, int thread_id) { + + assert(binary_tree != NULL); + *deleted = false; + + struct TreeNode* z = binary_tree->root; + while (z != NULL && binary_tree->get_priority_fn(z->data) != binary_tree->get_priority_fn(data)) { + if (binary_tree->get_priority_fn(data) < binary_tree->get_priority_fn(z->data)) { + z->left_subtree_sum -= binary_tree->get_execution_cost_fn(data, thread_id); + z = z->left; + } + else { + z = z->right; + } + } + + if (z != NULL) { + //if there are duplicated nodes in Z, + //we just need to remove duplicated one. + if (z->dup_next != NULL) { + struct TreeNode* cur = z; + struct TreeNode* prev = NULL; + while (cur && cur->data != data) { + prev = cur; + cur = cur->dup_next; + } + + //if the removed node is the head of the linkedlist; + if (cur == z) { + //copy the data from the removed node. + struct TreeNode* newroot = z->dup_next; + newroot->color = z->color; + newroot->left_subtree_sum = z->left_subtree_sum; + newroot->left = z->left; + if (newroot->left != NULL) { + newroot->left->parent = newroot; + } + newroot->right = z->right; + if (newroot->right != NULL) { + newroot->right->parent = newroot; + } + + newroot->parent = z->parent; + + if (z->parent) { + if (z->parent->left == z) { + z->parent->left = newroot; + } else { + z->parent->right = newroot; + } + } + deleteNode(binary_tree, z); + *deleted = true; + } else { //remove the node from the link list; + prev->dup_next = cur->dup_next; + deleteNode(binary_tree, cur); + *deleted = true; + } + } else{ + if (z->data == data) { + removeNode(binary_tree, z, thread_id); + *deleted = true; + } + } + } else { + *deleted = false; + return; + } +} + +// Function to find a value in a binary search tree (non-recursive) +/*struct TreeNode* find(struct TreeNode* root, int val) { + while (root != NULL) { + if (val == root->val) { + return root; // Return the node if value is found + } else if (val < root->val) { + root = root->left; // Move to left subtree if value is less + } else { + root = root->right; // Move to right subtree if value is greater + } + } + return NULL; // Return NULL if value is not found or if the tree is empty +}*/ + +bool is_empty(struct binary_tree *binary_tree) { + assert(binary_tree != NULL); + + return binary_tree->root == NULL; +} + +void inorder(struct binary_tree *binary_tree, struct TreeNode* root) +{ + assert(binary_tree != NULL); + + if(root == NULL) + return; + inorder(binary_tree, root->left); + printf("%lu ", binary_tree->get_priority_fn(root->data)); + inorder(binary_tree, root->right); +} +// return the sum of nodes' execution time that less than the target priority +uint64_t findMaxValueLessThan(struct binary_tree *binary_tree, struct TreeNode* root, void *target, int thread_id) { + assert(binary_tree != NULL); + + if (root == NULL) { + return 0; + } + if (binary_tree->get_priority_fn(target) == binary_tree->get_priority_fn(root->data)) { + return root->left_subtree_sum; + } else if (binary_tree->get_priority_fn(target) < binary_tree->get_priority_fn(root->data)) { + return findMaxValueLessThan(binary_tree, root->left, target, thread_id); + } else { + return get_sum_exe_time_of_node(binary_tree, root, thread_id) + root->left_subtree_sum + findMaxValueLessThan(binary_tree, root->right, target, thread_id); + } + +} + +struct TreeNode* makeEmpty(struct binary_tree *binary_tree, struct TreeNode* root) +{ + assert(binary_tree != NULL); + + if(root != NULL) { + makeEmpty(binary_tree, root->left); + makeEmpty(binary_tree, root->right); + deleteNode(binary_tree, root); + } + return NULL; +} + diff --git a/runtime/include/current_sandbox.h b/runtime/include/current_sandbox.h index fa84dff94..2e9b5522e 100644 --- a/runtime/include/current_sandbox.h +++ b/runtime/include/current_sandbox.h @@ -7,6 +7,7 @@ /* current sandbox that is active.. */ extern thread_local struct sandbox *worker_thread_current_sandbox; +extern struct sandbox* current_sandboxes[1024]; void current_sandbox_start(void); @@ -47,7 +48,8 @@ current_sandbox_set(struct sandbox *sandbox) .wasi_context = NULL, }; worker_thread_current_sandbox = NULL; - runtime_worker_threads_deadline[worker_thread_idx] = UINT64_MAX; + current_sandboxes[global_worker_thread_idx] = NULL; + runtime_worker_threads_deadline[global_worker_thread_idx] = UINT64_MAX; } else { sledge_abi__current_wasm_module_instance.wasi_context = sandbox->wasi_context; memcpy(&sledge_abi__current_wasm_module_instance.abi.memory, &sandbox->memory->abi, @@ -56,7 +58,8 @@ current_sandbox_set(struct sandbox *sandbox) wasm_globals_update_if_used(&sandbox->globals, 0, &sledge_abi__current_wasm_module_instance.abi.wasmg_0); worker_thread_current_sandbox = sandbox; - runtime_worker_threads_deadline[worker_thread_idx] = sandbox->absolute_deadline; + current_sandboxes[global_worker_thread_idx] = sandbox; + runtime_worker_threads_deadline[global_worker_thread_idx] = sandbox->absolute_deadline; } } diff --git a/runtime/include/deque.h b/runtime/include/deque.h index ee5d62ada..9a6467858 100644 --- a/runtime/include/deque.h +++ b/runtime/include/deque.h @@ -7,6 +7,7 @@ #ifndef DEQUE_H #define DEQUE_H +#include "lock.h" /* * This was implemented by referring to: * https://github.com/cpp-taskflow/cpp-taskflow/blob/9c28ccec910346a9937c40db7bdb542262053f9c/taskflow/executor/workstealing.hpp @@ -30,6 +31,7 @@ \ volatile long top; \ volatile long bottom; \ + lock_t lock; \ }; \ \ static inline void deque_init_##name(struct deque_##name *q, size_t sz) \ @@ -39,7 +41,7 @@ if (sz) { \ /* only for size with pow of 2 */ \ /* assert((sz & (sz - 1)) == 0); */ \ - assert(sz <= DEQUE_MAX_SZ); \ + assert(sz && (sz & (sz - 1)) == 0 && sz <= DEQUE_MAX_SZ); \ } else { \ sz = DEQUE_MAX_SZ; \ } \ @@ -58,7 +60,7 @@ /* nope, fixed size only */ \ if (q->size - 1 < (cb - ct)) return -ENOSPC; \ \ - q->wrk[cb] = *w; \ + q->wrk[cb & (q->size - 1)] = *w; \ __sync_synchronize(); \ if (__sync_bool_compare_and_swap(&q->bottom, cb, cb + 1) == false) assert(0); \ \ @@ -110,7 +112,7 @@ /* Empty */ \ if (ct >= cb) return -ENOENT; \ \ - *w = deque->wrk[ct]; \ + *w = deque->wrk[ct & (deque->size - 1)]; \ if (__sync_bool_compare_and_swap(&deque->top, ct, ct + 1) == false) return -EAGAIN; \ \ return 0; \ diff --git a/runtime/include/dispatcher.h b/runtime/include/dispatcher.h new file mode 100644 index 000000000..2c288ec1a --- /dev/null +++ b/runtime/include/dispatcher.h @@ -0,0 +1,25 @@ +#pragma once + +#include "dispatcher_options.h" + +static inline char * +dispatcher_print(enum DISPATCHER variant) +{ + switch (variant) { + case DISPATCHER_EDF_INTERRUPT: + return "EDF_INTERRUPT"; + case DISPATCHER_DARC: + return "DARC"; + case DISPATCHER_SHINJUKU: + return "SHINJUKU"; + case DISPATCHER_TO_GLOBAL_QUEUE: + return "DISPATCHER_TO_GLOBAL_QUEUE"; + case DISPATCHER_RR: + return "DISPATCHER_RR"; + case DISPATCHER_JSQ: + return "DISPATCHER_JSQ"; + case DISPATCHER_LLD: + return "DISPATCHER_LLD"; + } +} + diff --git a/runtime/include/dispatcher_options.h b/runtime/include/dispatcher_options.h new file mode 100644 index 000000000..167e17924 --- /dev/null +++ b/runtime/include/dispatcher_options.h @@ -0,0 +1,15 @@ +#pragma once + +enum DISPATCHER +{ + DISPATCHER_EDF_INTERRUPT = 0, + DISPATCHER_DARC = 1, + DISPATCHER_SHINJUKU = 2, + DISPATCHER_TO_GLOBAL_QUEUE = 3, + DISPATCHER_RR = 4, + DISPATCHER_JSQ = 5, + DISPATCHER_LLD = 6 +}; + +extern enum DISPATCHER dispatcher; + diff --git a/runtime/include/erpc_handler.h b/runtime/include/erpc_handler.h new file mode 100644 index 000000000..4991d3716 --- /dev/null +++ b/runtime/include/erpc_handler.h @@ -0,0 +1,13 @@ +#pragma once + +#include +void edf_interrupt_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void darc_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void shinjuku_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void enqueue_to_global_queue_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void rr_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void rr_req_handler_without_interrupt(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void jsq_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void jsq_req_handler_without_interrupt(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void lld_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +void lld_req_handler_without_interrupt(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); diff --git a/runtime/include/http_router.h b/runtime/include/http_router.h index 3b8320610..bb19ee348 100644 --- a/runtime/include/http_router.h +++ b/runtime/include/http_router.h @@ -3,13 +3,17 @@ #include #include +#include "erpc_handler.h" +#include "erpc_c_interface.h" #include "http.h" #include "module.h" #include "route_latency.h" #include "route.h" #include "route_config.h" #include "vec.h" +#include "dispatcher_options.h" +extern struct route *route_array; typedef struct route route_t; VEC(route_t) @@ -19,6 +23,10 @@ static inline void http_router_init(http_router_t *router, size_t capacity) { vec_route_t_init(router, capacity); + /* Use the request type as the array index, so skip index 0 since request index + starts from 1 + */ + route_array = (struct route*) calloc(capacity + 1, sizeof(struct route)); } static inline int @@ -30,22 +38,71 @@ http_router_add_route(http_router_t *router, struct route_config *config, struct assert(config->route != NULL); assert(config->http_resp_content_type != NULL); - struct route route = { .route = config->route, - .module = module, - .relative_deadline_us = config->relative_deadline_us, - .relative_deadline = (uint64_t)config->relative_deadline_us - * runtime_processor_speed_MHz, - .response_content_type = config->http_resp_content_type }; + struct route route = { .route = config->route, + .request_type = config->request_type, + .module = module, + .relative_deadline_us = config->relative_deadline_us, + .relative_deadline = (uint64_t)config->relative_deadline_us + * runtime_processor_speed_MHz, + .expected_execution_cycle = config->expected_execution_cycle, + .response_content_type = config->http_resp_content_type, + .group_id = config->group_id}; route_latency_init(&route.latency); http_route_total_init(&route.metrics); + + /* Register RPC request handler */ + if (dispatcher == DISPATCHER_EDF_INTERRUPT) { + if (erpc_register_req_func(config->request_type, edf_interrupt_req_handler, 0) != 0) { + panic("register erpc function for EDF_INTERRUPT dispatcher failed\n"); + } + } else if (dispatcher == DISPATCHER_DARC) { + if (erpc_register_req_func(config->request_type, darc_req_handler, 0) != 0) { + panic("register erpc function for DARC dispatcher failed\n"); + } + } else if (dispatcher == DISPATCHER_SHINJUKU) { + if (erpc_register_req_func(config->request_type, shinjuku_req_handler, 0) != 0) { + panic("register erpc function for Shinjuku dispatcher failed\n"); + } + } else if (dispatcher == DISPATCHER_TO_GLOBAL_QUEUE) { + if (erpc_register_req_func(config->request_type, enqueue_to_global_queue_req_handler, 0) != 0) { + panic("register erpc function for enqueuing to global queue failed\n"); + } + } else if (dispatcher == DISPATCHER_RR) { + //if (erpc_register_req_func(config->request_type, rr_req_handler, 0) != 0) { + if (erpc_register_req_func(config->request_type, rr_req_handler_without_interrupt, 0) != 0) { + panic("register erpc function for round robain distribution failed\n"); + } + } else if (dispatcher == DISPATCHER_JSQ) { + //if (erpc_register_req_func(config->request_type, jsq_req_handler, 0) != 0) { + if (erpc_register_req_func(config->request_type, jsq_req_handler_without_interrupt, 0) != 0) { + panic("register erpc function for join shortest queue distribution failed\n"); + } + } else if (dispatcher == DISPATCHER_LLD && scheduler == SCHEDULER_EDF) { + /* Dispatcher assign requests to workers with LLD + interrupt, and each worker schedule its + local queue tasks with EDF + */ + //if (erpc_register_req_func(config->request_type, lld_req_handler, 0) != 0) { + if (erpc_register_req_func(config->request_type, lld_req_handler_without_interrupt, 0) != 0) { + panic("register erpc function for leatest load distribution with edf failed\n"); + } + } else if (dispatcher == DISPATCHER_LLD && scheduler == SCHEDULER_FIFO) { + /* Dispatcher assign requests to workers with LLD and no interrupt, and each worker schedule + its local queue tasks with Round Robin with fixed interval preemption + */ + if (erpc_register_req_func(config->request_type, lld_req_handler_without_interrupt, 0) != 0) { + panic("register erpc function for leatest load distribution with fifo failed\n"); + } + } + /* Admissions Control */ uint64_t expected_execution = (uint64_t)config->expected_execution_us * runtime_processor_speed_MHz; - admissions_info_initialize(&route.admissions_info, config->admissions_percentile, expected_execution, + admissions_info_initialize(&route.admissions_info, route.request_type, config->admissions_percentile, expected_execution, route.relative_deadline); int rc = vec_route_t_push(router, route); + route_array[config->request_type] = route; if (unlikely(rc == -1)) { return -1; } return 0; @@ -63,6 +120,26 @@ http_router_match_route(http_router_t *router, char *route) return NULL; } +static inline struct route * +http_router_match_request_type(http_router_t *router, uint8_t request_type) +{ + /* + for (int i = 0; i < router->length; i++) { + if (request_type == router->buffer[i].request_type) { + return &router->buffer[i]; + } + } + + return NULL; + */ + + if (route_array[request_type].request_type == request_type) { + return &route_array[request_type]; + } else { + return NULL; + } +} + static inline void http_router_foreach(http_router_t *router, void (*cb)(route_t *, void *, void *), void *arg_one, void *arg_two) { diff --git a/runtime/include/listener_thread.h b/runtime/include/listener_thread.h index 31bd2c16e..55f954aa6 100644 --- a/runtime/include/listener_thread.h +++ b/runtime/include/listener_thread.h @@ -6,13 +6,21 @@ #include "http_session.h" #include "module.h" -#define LISTENER_THREAD_CORE_ID 1 +#define LISTENER_THREAD_START_CORE_ID 2 +#define DIPATCH_ROUNTE_ERROR "Did not match any routes" +#define WORK_ADMITTED_ERROR "Work is not admitted" +#define SANDBOX_ALLOCATION_ERROR "Failed to allocate a sandbox" +#define GLOBAL_QUEUE_ERROR "Failed to add sandbox to global queue" -extern pthread_t listener_thread_id; +#define MAX_DISPATCHER 10 +#define MAX_REQUEST_TYPE MODULE_DATABASE_CAPACITY -void listener_thread_initialize(void); -noreturn void *listener_thread_main(void *dummy); +extern thread_local pthread_t listener_thread_id; + +void listener_thread_initialize(uint8_t thread_id); +void *listener_thread_main(void *dummy); void listener_thread_register_http_session(struct http_session *http); +void dispatcher_send_response(void *req_handle, char* msg, size_t msg_len); /** * Used to determine if running in the context of a listener thread diff --git a/runtime/include/local_cleanup_queue.h b/runtime/include/local_cleanup_queue.h index 9df6107aa..b2c302605 100644 --- a/runtime/include/local_cleanup_queue.h +++ b/runtime/include/local_cleanup_queue.h @@ -3,5 +3,5 @@ #include "sandbox_types.h" void local_cleanup_queue_add(struct sandbox *sandbox); -void local_cleanup_queue_free(); +int local_cleanup_queue_free(uint64_t *duration, uint64_t *ret); void local_cleanup_queue_initialize(); diff --git a/runtime/include/local_preempted_fifo_queue.h b/runtime/include/local_preempted_fifo_queue.h new file mode 100644 index 000000000..9bd0e83ec --- /dev/null +++ b/runtime/include/local_preempted_fifo_queue.h @@ -0,0 +1,7 @@ +#pragma once + +#include "request_fifo_queue.h" + +void local_preempted_fifo_queue_init(); +struct sandbox * pop_worker_preempted_queue(int worker_id, struct request_fifo_queue * queue, uint64_t * tsc); +int push_to_preempted_queue(struct sandbox *sandbox, uint64_t tsc); diff --git a/runtime/include/local_runqueue.h b/runtime/include/local_runqueue.h index 432eea108..56879a943 100644 --- a/runtime/include/local_runqueue.h +++ b/runtime/include/local_runqueue.h @@ -6,19 +6,56 @@ /* Returns pointer back if successful, null otherwise */ typedef void (*local_runqueue_add_fn_t)(struct sandbox *); +typedef void (*local_runqueue_add_fn_t_idx)(int index, struct sandbox *); +typedef uint64_t (*local_runqueue_try_add_fn_t_idx)(int index, struct sandbox *, bool *need_interrupt); +typedef uint32_t (*local_runqueue_try_add_and_get_len_fn_t_idx)(int index, struct sandbox *, bool *need_interrupt); +typedef uint64_t (*local_runqueue_try_add_and_get_load_fn_t_idx)(int index, struct sandbox *, bool *need_interrupt); typedef bool (*local_runqueue_is_empty_fn_t)(void); +typedef bool (*local_runqueue_is_empty_fn_t_idx)(int index); typedef void (*local_runqueue_delete_fn_t)(struct sandbox *sandbox); typedef struct sandbox *(*local_runqueue_get_next_fn_t)(); +typedef int (*local_runqueue_get_height_fn_t)(); +typedef int (*local_runqueue_get_length_fn_t)(); +typedef int (*local_runqueue_get_length_fn_t_idx)(int index); +typedef void (*local_runqueue_print_in_order_fn_t_idx)(int index); struct local_runqueue_config { - local_runqueue_add_fn_t add_fn; - local_runqueue_is_empty_fn_t is_empty_fn; - local_runqueue_delete_fn_t delete_fn; - local_runqueue_get_next_fn_t get_next_fn; + local_runqueue_add_fn_t add_fn; + local_runqueue_add_fn_t_idx add_fn_idx; + local_runqueue_try_add_fn_t_idx try_add_fn_idx; + local_runqueue_try_add_and_get_len_fn_t_idx try_add_and_get_len_fn_t_idx; + local_runqueue_try_add_and_get_load_fn_t_idx try_add_and_get_load_fn_t_idx; + local_runqueue_is_empty_fn_t is_empty_fn; + local_runqueue_is_empty_fn_t_idx is_empty_fn_idx; + local_runqueue_delete_fn_t delete_fn; + local_runqueue_get_next_fn_t get_next_fn; + local_runqueue_get_height_fn_t get_height_fn; + local_runqueue_get_length_fn_t get_length_fn; + local_runqueue_get_length_fn_t_idx get_length_fn_idx; + local_runqueue_print_in_order_fn_t_idx print_in_order_fn_idx; }; void local_runqueue_add(struct sandbox *); +void local_runqueue_add_index(int index, struct sandbox *); +uint64_t local_runqueue_try_add_index(int index, struct sandbox *, bool *need_interrupt); +uint32_t local_runqueue_add_and_get_length_index(int index, struct sandbox *, bool *need_interrupt); +uint64_t local_runqueue_add_and_get_load_index(int index, struct sandbox *, bool *need_interrupt); void local_runqueue_delete(struct sandbox *); bool local_runqueue_is_empty(); +bool local_runqueue_is_empty_index(int index); struct sandbox *local_runqueue_get_next(); void local_runqueue_initialize(struct local_runqueue_config *config); +int local_runqueue_get_height(); +int local_runqueue_get_length(); +int local_runqueue_get_length_index(int index); +void local_runqueue_print_in_order(int index); + +void worker_queuing_cost_initialize(); + +void worker_queuing_cost_increment(int index, uint64_t cost); + +void worker_queuing_cost_decrement(int index, uint64_t cost); + +uint64_t get_local_queue_load(int index); + +void wakeup_worker(int index); diff --git a/runtime/include/local_runqueue_binary_tree.h b/runtime/include/local_runqueue_binary_tree.h new file mode 100644 index 000000000..b4970a386 --- /dev/null +++ b/runtime/include/local_runqueue_binary_tree.h @@ -0,0 +1,3 @@ +#pragma once + +void local_runqueue_binary_tree_initialize(); diff --git a/runtime/include/local_runqueue_circular_queue.h b/runtime/include/local_runqueue_circular_queue.h new file mode 100644 index 000000000..15a27f3dc --- /dev/null +++ b/runtime/include/local_runqueue_circular_queue.h @@ -0,0 +1,4 @@ +#pragma once + +void local_runqueue_circular_queue_initialize(); + diff --git a/runtime/include/lock.h b/runtime/include/lock.h index 1ff0242c0..7e454762d 100644 --- a/runtime/include/lock.h +++ b/runtime/include/lock.h @@ -4,8 +4,8 @@ #include #include +#include "types.h" #include "arch/getcycles.h" -#include "runtime.h" /* A linked list of nodes */ diff --git a/runtime/include/memlogging.h b/runtime/include/memlogging.h new file mode 100644 index 000000000..8584620fc --- /dev/null +++ b/runtime/include/memlogging.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +struct mem_logging_obj { + uint32_t log_size; + char *logger; + uint32_t offset; + FILE* fout; +}; + +void mem_log_init(uint32_t log_size, char const* file); +void mem_log_init2(uint32_t log_size, FILE* file); +void mem_log(char const * fmt, ...); +void dump_log_to_file(); diff --git a/runtime/include/module.h b/runtime/include/module.h index b2ab6ad83..544e2a080 100644 --- a/runtime/include/module.h +++ b/runtime/include/module.h @@ -9,7 +9,6 @@ #include "admissions_info.h" #include "current_wasm_module_instance.h" #include "panic.h" -#include "pool.h" #include "sledge_abi_symbols.h" #include "tcp_server.h" #include "types.h" @@ -17,16 +16,9 @@ #include "wasm_stack.h" #include "wasm_memory.h" #include "wasm_table.h" +#include "runtime.h" -extern thread_local int worker_thread_idx; - -INIT_POOL(wasm_memory, wasm_memory_free) -INIT_POOL(wasm_stack, wasm_stack_free) - -struct module_pool { - struct wasm_memory_pool memory; - struct wasm_stack_pool stack; -} CACHE_PAD_ALIGNED; +extern thread_local int global_worker_thread_idx; struct module { char *path; @@ -38,7 +30,6 @@ struct module { _Atomic uint32_t reference_count; /* ref count how many instances exist here. */ struct sledge_abi__wasm_table *indirect_table; - struct module_pool *pools; } CACHE_PAD_ALIGNED; /******************************** @@ -112,24 +103,6 @@ module_alloc_table(struct module *module) return 0; } -static inline void -module_initialize_pools(struct module *module) -{ - for (int i = 0; i < runtime_worker_threads_count; i++) { - wasm_memory_pool_init(&module->pools[i].memory, false); - wasm_stack_pool_init(&module->pools[i].stack, false); - } -} - -static inline void -module_deinitialize_pools(struct module *module) -{ - for (int i = 0; i < runtime_worker_threads_count; i++) { - wasm_memory_pool_deinit(&module->pools[i].memory); - wasm_stack_pool_deinit(&module->pools[i].stack); - } -} - /** * Invoke a module's initialize_memory * @param module - the module whose memory we are initializing @@ -168,7 +141,7 @@ module_allocate_stack(struct module *module) { assert(module != NULL); - struct wasm_stack *stack = wasm_stack_pool_remove_nolock(&module->pools[worker_thread_idx].stack); + struct wasm_stack *stack = runtime_stack_pool_remove(global_worker_thread_idx); if (stack == NULL) { stack = wasm_stack_alloc(module->stack_size); @@ -178,11 +151,14 @@ module_allocate_stack(struct module *module) return stack; } -static inline void +static inline uint64_t module_free_stack(struct module *module, struct wasm_stack *stack) { + uint64_t now = __getcycles(); wasm_stack_reinit(stack); - wasm_stack_pool_add_nolock(&module->pools[worker_thread_idx].stack, stack); + uint64_t d = __getcycles() - now; + runtime_stack_pool_add(global_worker_thread_idx, stack); + return d; } static inline struct wasm_memory * @@ -199,7 +175,7 @@ module_allocate_linear_memory(struct module *module) assert(starting_bytes <= (uint64_t)UINT32_MAX + 1); assert(max_bytes <= (uint64_t)UINT32_MAX + 1); - struct wasm_memory *linear_memory = wasm_memory_pool_remove_nolock(&module->pools[worker_thread_idx].memory); + struct wasm_memory *linear_memory = runtime_linear_memory_pool_remove(global_worker_thread_idx); if (linear_memory == NULL) { linear_memory = wasm_memory_alloc(starting_bytes, max_bytes); if (unlikely(linear_memory == NULL)) return NULL; @@ -212,5 +188,21 @@ static inline void module_free_linear_memory(struct module *module, struct wasm_memory *memory) { wasm_memory_reinit(memory, module->abi.starting_pages * WASM_PAGE_SIZE); - wasm_memory_pool_add_nolock(&module->pools[worker_thread_idx].memory, memory); + runtime_linear_memory_pool_add(global_worker_thread_idx, memory); +} + +static inline void +module_preallocate_memory(struct module *module) { + /* preallocate two pairs of linear and stack memory */ + struct wasm_stack *stack1 = module_allocate_stack(module); + struct wasm_memory *memory1 = module_allocate_linear_memory(module); + + struct wasm_stack *stack2 = module_allocate_stack(module); + struct wasm_memory *memory2 = module_allocate_linear_memory(module); + + module_free_linear_memory(module, memory1); + module_free_linear_memory(module, memory2); + + module_free_stack(module, stack1); + module_free_stack(module, stack2); } diff --git a/runtime/include/module_database.h b/runtime/include/module_database.h index 0c09ec55d..24d1b4605 100644 --- a/runtime/include/module_database.h +++ b/runtime/include/module_database.h @@ -2,7 +2,7 @@ #include "module.h" -#define MODULE_DATABASE_CAPACITY 128 +#define MODULE_DATABASE_CAPACITY 1024 struct module_database { struct module *modules[MODULE_DATABASE_CAPACITY]; diff --git a/runtime/include/perf_window.h b/runtime/include/perf_window.h index 5b2b37f93..8df4b118f 100644 --- a/runtime/include/perf_window.h +++ b/runtime/include/perf_window.h @@ -36,7 +36,7 @@ perf_window_initialize(struct perf_window *perf_window) static inline void perf_window_swap(struct perf_window *perf_window, uint16_t first_by_duration_idx, uint16_t second_by_duration_idx) { - assert(lock_is_locked(&perf_window->lock)); + //assert(lock_is_locked(&perf_window->lock)); assert(perf_window != NULL); assert(first_by_duration_idx < PERF_WINDOW_CAPACITY); assert(second_by_duration_idx < PERF_WINDOW_CAPACITY); @@ -95,7 +95,7 @@ perf_window_add(struct perf_window *perf_window, uint64_t newest_execution_time) 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"); + //if (unlikely(!lock_is_locked(&perf_window->lock))) panic("lock not held when calling perf_window_add\n"); /* If perf window is empty, fill all elements with newest_execution_time */ if (perf_window->count == 0) { @@ -170,6 +170,24 @@ perf_window_get_percentile(struct perf_window *perf_window, uint8_t percentile, return perf_window->by_duration[idx].execution_time; } + +static inline uint64_t +perf_window_get_mean(struct perf_window *perf_window) { + assert(perf_window != NULL); + uint64_t sum = 0; + + for (int i = 0; i < PERF_WINDOW_CAPACITY; i++) { + sum += perf_window->by_duration[i].execution_time; + } + + if (perf_window->count == 0) { + printf("perf_window count is 0\n"); + return 0; + } + uint64_t mean = sum / PERF_WINDOW_CAPACITY; + return mean / runtime_processor_speed_MHz; +} + /** * Returns the total count of executions * @returns total count diff --git a/runtime/include/ps_list.h b/runtime/include/ps_list.h index fb2ec30ff..7a893d6b1 100644 --- a/runtime/include/ps_list.h +++ b/runtime/include/ps_list.h @@ -49,6 +49,7 @@ #ifndef PS_LIST_H #define PS_LIST_H +#include "lock.h" struct ps_list { struct ps_list *n, *p; }; @@ -60,6 +61,7 @@ struct ps_list { */ struct ps_list_head { struct ps_list l; + lock_t lock; }; #define PS_LIST_DEF_NAME list diff --git a/runtime/include/request_fifo_queue.h b/runtime/include/request_fifo_queue.h new file mode 100644 index 000000000..f380ca97f --- /dev/null +++ b/runtime/include/request_fifo_queue.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +#define RQUEUE_QUEUE_LEN 4096 + +struct request_fifo_queue { + struct sandbox *rqueue[RQUEUE_QUEUE_LEN]; + unsigned int rqueue_tail; + unsigned int rqueue_head; + uint64_t tsqueue[RQUEUE_QUEUE_LEN]; +}; + diff --git a/runtime/include/request_typed_deque.h b/runtime/include/request_typed_deque.h new file mode 100644 index 000000000..0adca59ca --- /dev/null +++ b/runtime/include/request_typed_deque.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include + +#define RDEQUE_LEN 65535 + +struct request_typed_deque { + uint8_t type; + uint64_t deadline; + struct sandbox *rqueue[RDEQUE_LEN]; + int front; + int rear; + int size; + int length; + uint64_t tsqueue[RDEQUE_LEN]; +}; + +struct request_typed_deque * request_typed_deque_init(uint8_t type, int size); +bool isFull(struct request_typed_deque * queue); +bool isEmpty(struct request_typed_deque * queue); +int getLength(struct request_typed_deque * queue); +void insertfront(struct request_typed_deque * queue, struct sandbox * sandbox, uint64_t ts); +void insertrear(struct request_typed_deque * queue, struct sandbox * sandbox, uint64_t ts); +void deletefront(struct request_typed_deque * queue); +void deleterear(struct request_typed_deque * queue); +struct sandbox * getFront(struct request_typed_deque * queue, uint64_t * ts); +struct sandbox * getRear(struct request_typed_deque * queue, uint64_t * ts); diff --git a/runtime/include/request_typed_queue.h b/runtime/include/request_typed_queue.h new file mode 100644 index 000000000..e00e905a1 --- /dev/null +++ b/runtime/include/request_typed_queue.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include + +#define RQUEUE_LEN 4096 +#define MAX_WORKERS 32 + +extern uint32_t runtime_worker_group_size; + +struct request_msg { + uint8_t *msg; + size_t size; +}; + +struct request_typed_queue { + double ratio; + struct sandbox *rqueue[RQUEUE_LEN]; + unsigned int rqueue_tail; + unsigned int rqueue_head; + uint64_t tsqueue[RQUEUE_LEN]; + + // Profiling variables + uint64_t windows_mean_ns; + uint64_t windows_count; + uint64_t delay; + + //DARC variables + uint32_t res_workers[MAX_WORKERS]; //record the worker id from 0 to the maximum id for a listener, + //not the true worker id. It's value is the index of worker_list + uint32_t n_resas; + uint64_t last_resa; + uint32_t stealable_workers[MAX_WORKERS]; + uint32_t n_stealable; + uint32_t type_group; + uint64_t max_delay; + double prev_demand; + +}; + +/* + * n_resas the number of reserved workers. Leaving the last n_resas workers as the reserved workers + */ +struct request_typed_queue * request_typed_queue_init(uint8_t type, uint32_t n_resas); + +int push_to_rqueue(struct sandbox *sandbox, struct request_typed_queue *rtype, uint64_t tsc); diff --git a/runtime/include/route.h b/runtime/include/route.h index a57493279..b239d753b 100644 --- a/runtime/include/route.h +++ b/runtime/include/route.h @@ -11,12 +11,15 @@ /* Assumption: entrypoint is always _start. This should be enhanced later */ struct route { char *route; + uint8_t request_type; struct http_route_total metrics; struct module *module; /* HTTP State */ - uint32_t relative_deadline_us; - uint64_t relative_deadline; /* cycles */ - char *response_content_type; - struct admissions_info admissions_info; - struct perf_window latency; + uint32_t relative_deadline_us; + uint64_t relative_deadline; /* cycles */ + uint64_t expected_execution_cycle; /* in cycles */ + char *response_content_type; + struct admissions_info admissions_info; + struct perf_window latency; + uint32_t group_id; }; diff --git a/runtime/include/route_config.h b/runtime/include/route_config.h index 78b743b93..66e3c29c7 100644 --- a/runtime/include/route_config.h +++ b/runtime/include/route_config.h @@ -4,6 +4,7 @@ #include #include +#include "dispatcher_options.h" #include "admissions_control.h" #include "runtime.h" #include "scheduler_options.h" @@ -11,6 +12,9 @@ enum route_config_member { route_config_member_route, + route_config_member_request_type, + route_config_member_n_resas, + route_config_member_group_id, route_config_member_path, route_config_member_admissions_percentile, route_config_member_expected_execution_us, @@ -19,11 +23,17 @@ enum route_config_member route_config_member_len }; +extern int groups[route_config_member_len]; + struct route_config { char *route; + uint8_t request_type; + uint32_t n_resas; + uint32_t group_id; char *path; uint8_t admissions_percentile; uint32_t expected_execution_us; + uint64_t expected_execution_cycle; /* exepected exectuion in cycle */ uint32_t relative_deadline_us; char *http_resp_content_type; }; @@ -43,6 +53,8 @@ static inline void route_config_print(struct route_config *config) { printf("[Route] Route: %s\n", config->route); + printf("[Route] Request type: %hhu\n", config->request_type); + printf("[Route] Request type: %hhu\n", config->request_type); printf("[Route] Path: %s\n", config->path); printf("[Route] Admissions Percentile: %hhu\n", config->admissions_percentile); printf("[Route] Expected Execution (us): %u\n", config->expected_execution_us); @@ -63,13 +75,19 @@ route_config_validate(struct route_config *config, bool *did_set) return -1; } + if (did_set[route_config_member_request_type] == false) { + + fprintf(stderr, "request type field is required\n"); + return -1; + } + if (did_set[route_config_member_path] == false) { fprintf(stderr, "path field is required\n"); return -1; } if (did_set[route_config_member_http_resp_content_type] == false) { - debuglog("http_resp_content_type not set, defaulting to text/plain\n"); + fprintf(stderr, "http_resp_content_type not set, defaulting to text/plain\n"); config->http_resp_content_type = "text/plain"; } @@ -114,6 +132,32 @@ route_config_validate(struct route_config *config, bool *did_set) } #endif } + + if (dispatcher == DISPATCHER_DARC) { + if (did_set[route_config_member_n_resas] == false) { + fprintf(stderr, "n-resas is required for DARC\n"); + return -1; + } + if (did_set[route_config_member_group_id] == false) { + fprintf(stderr, "group id is required for DARC\n"); + return -1; + } + + if (config->group_id <= 0) { + fprintf(stderr, "group id must be larger than 0\n"); + return -1; + } + + if (groups[config->group_id] == 0) { + groups[config->group_id] = config->n_resas; + } else { + if (groups[config->group_id] != config->n_resas) { + fprintf(stderr, "same group id %d has different n-resas config, must be same\n", config->group_id); + return -1; + } + } + } + return 0; } diff --git a/runtime/include/route_config_parse.h b/runtime/include/route_config_parse.h index f3a1be088..e22dd52d4 100644 --- a/runtime/include/route_config_parse.h +++ b/runtime/include/route_config_parse.h @@ -7,6 +7,9 @@ #include "route_config.h" static const char *route_config_json_keys[route_config_member_len] = { "route", + "request-type", + "n-resas", + "group-id", "path", "admissions-percentile", "expected-execution-us", @@ -56,7 +59,34 @@ route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t if (route_config_set_key_once(did_set, route_config_member_route) == -1) return -1; config->route = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start); - } else if (strcmp(key, route_config_json_keys[route_config_member_path]) == 0) { + } else if (strcmp(key, route_config_json_keys[route_config_member_request_type]) == 0) { + if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (route_config_set_key_once(did_set, route_config_member_request_type) == -1) + return -1; + + int rc = parse_uint8_t(tokens[i], json_buf, + route_config_json_keys[route_config_member_request_type], + &config->request_type); + if (rc < 0) return -1; + } else if (strcmp(key, route_config_json_keys[route_config_member_n_resas]) == 0) { + if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (route_config_set_key_once(did_set, route_config_member_n_resas) == -1) + return -1; + + int rc = parse_uint32_t(tokens[i], json_buf, + route_config_json_keys[route_config_member_n_resas], + &config->n_resas); + if (rc < 0) return -1; + } else if (strcmp(key, route_config_json_keys[route_config_member_group_id]) == 0) { + if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (route_config_set_key_once(did_set, route_config_member_group_id) == -1) + return -1; + + int rc = parse_uint32_t(tokens[i], json_buf, + route_config_json_keys[route_config_member_group_id], + &config->group_id); + if (rc < 0) return -1; + } else if (strcmp(key, route_config_json_keys[route_config_member_path]) == 0) { if (!is_nonempty_string(tokens[i], key)) return -1; if (route_config_set_key_once(did_set, route_config_member_path) == -1) return -1; @@ -79,6 +109,8 @@ route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t route_config_json_keys[route_config_member_expected_execution_us], &config->expected_execution_us); if (rc < 0) return -1; + config->expected_execution_cycle = (uint64_t)config->expected_execution_us * runtime_processor_speed_MHz; + assert(config->expected_execution_cycle != 0); } else if (strcmp(key, route_config_json_keys[route_config_member_relative_deadline_us]) == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; if (route_config_set_key_once(did_set, route_config_member_relative_deadline_us) == -1) diff --git a/runtime/include/runtime.h b/runtime/include/runtime.h index 4fe0ec27b..2fce5d64b 100644 --- a/runtime/include/runtime.h +++ b/runtime/include/runtime.h @@ -1,14 +1,29 @@ #pragma once #include +#include #include /* for epoll_create1(), epoll_ctl(), struct epoll_event */ #include /* for pid_t */ #include #include +#include "types.h" +#include "pool.h" +#include "wasm_stack.h" +#include "wasm_memory.h" #include "likely.h" #include "types.h" +INIT_POOL(wasm_memory, wasm_memory_free) +INIT_POOL(wasm_stack, wasm_stack_free) + +struct memory_pool { + struct wasm_memory_pool memory; + struct wasm_stack_pool stack; +} CACHE_PAD_ALIGNED; + + +extern struct memory_pool *memory_pools; /** * Optimizing compilers and modern CPUs reorder instructions however it sees fit. This means that the resulting * execution order may differ from the order of our source code. If there is a variable protecting a critical section, @@ -24,9 +39,9 @@ #define RUNTIME_LOG_FILE "sledge.log" #define RUNTIME_MAX_EPOLL_EVENTS 128 -#define RUNTIME_MAX_TENANT_COUNT 32 +#define RUNTIME_MAX_TENANT_COUNT 65535 /* Use UDP port to index tenent */ #define RUNTIME_RELATIVE_DEADLINE_US_MAX 3600000000 /* One Hour. Fits in uint32_t */ -#define RUNTIME_RUNQUEUE_SIZE 256 /* Minimum guaranteed size. Might grow! */ +#define RUNTIME_RUNQUEUE_SIZE 2560000 /* Minimum guaranteed size. Might grow! */ #define RUNTIME_TENANT_QUEUE_SIZE 4096 enum RUNTIME_SIGALRM_HANDLER @@ -35,6 +50,7 @@ enum RUNTIME_SIGALRM_HANDLER RUNTIME_SIGALRM_HANDLER_TRIAGED = 1 }; +extern _Atomic uint64_t request_index; extern pid_t runtime_pid; extern bool runtime_preemption_enabled; extern bool runtime_worker_spinloop_pause_enabled; @@ -42,8 +58,11 @@ extern uint32_t runtime_processor_speed_MHz; extern uint32_t runtime_quantum_us; extern enum RUNTIME_SIGALRM_HANDLER runtime_sigalrm_handler; extern pthread_t *runtime_worker_threads; +extern pthread_t *runtime_listener_threads; extern uint32_t runtime_worker_threads_count; +extern uint32_t runtime_listener_threads_count; extern int *runtime_worker_threads_argument; +extern int *runtime_listener_threads_argument; extern uint64_t *runtime_worker_threads_deadline; extern uint64_t runtime_boot_timestamp; @@ -67,3 +86,52 @@ runtime_print_sigalrm_handler(enum RUNTIME_SIGALRM_HANDLER variant) return "TRIAGED"; } } + +static inline void +request_index_initialize() +{ + atomic_init(&request_index, 0); +} +static inline uint64_t +request_index_increment() +{ + return atomic_fetch_add(&request_index, 1); +} + +static inline void +runtime_initialize_pools() +{ + for (int i = 0; i < runtime_worker_threads_count; i++) { + wasm_memory_pool_init(&memory_pools[i].memory, false); + wasm_stack_pool_init(&memory_pools[i].stack, false); + } +} + +static inline void +runtime_deinitialize_pools() +{ + for (int i = 0; i < runtime_worker_threads_count; i++) { + wasm_memory_pool_deinit(&memory_pools[i].memory); + wasm_stack_pool_deinit(&memory_pools[i].stack); + } +} + +static inline struct wasm_stack * +runtime_stack_pool_remove(int thread_id) { + return wasm_stack_pool_remove_nolock(&memory_pools[thread_id].stack); +} + +static inline void +runtime_stack_pool_add(int thread_id, struct wasm_stack * stack) { + wasm_stack_pool_add_nolock(&memory_pools[thread_id].stack, stack); +} + +static inline struct wasm_memory * +runtime_linear_memory_pool_remove(int thread_id) { + return wasm_memory_pool_remove_nolock(&memory_pools[thread_id].memory); +} + +static inline void +runtime_linear_memory_pool_add(int thread_id, struct wasm_memory * memory) { + wasm_memory_pool_add_nolock(&memory_pools[thread_id].memory, memory); +} diff --git a/runtime/include/sandbox_functions.h b/runtime/include/sandbox_functions.h index 754d40c9c..f2010cbb8 100644 --- a/runtime/include/sandbox_functions.h +++ b/runtime/include/sandbox_functions.h @@ -12,12 +12,17 @@ * Public API * **************************/ +extern struct sandbox* current_sandboxes[1024]; +extern bool runtime_exponential_service_time_simulation_enabled; +extern struct perf_window * worker_perf_windows[1024]; struct sandbox *sandbox_alloc(struct module *module, struct http_session *session, struct route *route, - struct tenant *tenant, uint64_t admissions_estimate); + struct tenant *tenant, uint64_t admissions_estimate, void *req_handle, + uint8_t rpc_id); int sandbox_prepare_execution_environment(struct sandbox *sandbox); -void sandbox_free(struct sandbox *sandbox); +uint64_t sandbox_free(struct sandbox *sandbox, uint64_t *ret); void sandbox_main(struct sandbox *sandbox); void sandbox_switch_to(struct sandbox *next_sandbox); +void sandbox_send_response(struct sandbox *sandbox, uint8_t response_code); /** * Free Linear Memory, leaving stack in place @@ -47,10 +52,61 @@ sandbox_get_module(struct sandbox *sandbox) static inline uint64_t sandbox_get_priority(void *element) { + assert(element != NULL); struct sandbox *sandbox = (struct sandbox *)element; return sandbox->absolute_deadline; } +static inline uint64_t +sandbox_get_execution_cost(void *element, int thread_id) +{ + assert(element != NULL); + struct sandbox *sandbox = (struct sandbox *)element; + if (runtime_exponential_service_time_simulation_enabled) { + assert(sandbox->estimated_cost != 0); + if (sandbox == current_sandboxes[thread_id]) { + /* This sandbox is running, get the running lasting time until now and + calculate the remaining execution time + */ + uint64_t run_cycles = __getcycles() - sandbox->timestamp_of.last_state_change; + return sandbox->estimated_cost > (sandbox->duration_of_state[SANDBOX_RUNNING_SYS] + + sandbox->duration_of_state[SANDBOX_RUNNING_USER] + run_cycles) ? + sandbox->estimated_cost - + sandbox->duration_of_state[SANDBOX_RUNNING_SYS] - + sandbox->duration_of_state[SANDBOX_RUNNING_USER] - run_cycles : 0; + } else { + + return sandbox->estimated_cost > (sandbox->duration_of_state[SANDBOX_RUNNING_SYS] + + sandbox->duration_of_state[SANDBOX_RUNNING_USER]) ? + sandbox->estimated_cost - + sandbox->duration_of_state[SANDBOX_RUNNING_SYS] - + sandbox->duration_of_state[SANDBOX_RUNNING_USER] : 0; + } + } else { + uint32_t uid = sandbox->route->admissions_info.uid; + uint64_t estimated_cost = perf_window_get_percentile(&worker_perf_windows[thread_id][uid], + sandbox->route->admissions_info.percentile, + sandbox->route->admissions_info.control_index); + + if (sandbox == current_sandboxes[thread_id]) { + uint64_t run_cycles = __getcycles() - sandbox->timestamp_of.last_state_change; + return estimated_cost > (sandbox->duration_of_state[SANDBOX_RUNNING_SYS] + + sandbox->duration_of_state[SANDBOX_RUNNING_USER] + run_cycles) ? + estimated_cost - + sandbox->duration_of_state[SANDBOX_RUNNING_SYS] - + sandbox->duration_of_state[SANDBOX_RUNNING_USER] - run_cycles : 0; + + } else { + return estimated_cost > (sandbox->duration_of_state[SANDBOX_RUNNING_SYS] + + sandbox->duration_of_state[SANDBOX_RUNNING_USER]) ? + estimated_cost - + sandbox->duration_of_state[SANDBOX_RUNNING_SYS] - + sandbox->duration_of_state[SANDBOX_RUNNING_USER] : 0; + } + + } +} + static inline void sandbox_process_scheduler_updates(struct sandbox *sandbox) { diff --git a/runtime/include/sandbox_perf_log.h b/runtime/include/sandbox_perf_log.h index a7a96de56..ab588dcf0 100644 --- a/runtime/include/sandbox_perf_log.h +++ b/runtime/include/sandbox_perf_log.h @@ -3,8 +3,11 @@ #include "pretty_print.h" #include "runtime.h" #include "sandbox_types.h" +#include "memlogging.h" +extern _Atomic uint32_t local_queue_length[1024]; extern FILE *sandbox_perf_log; +extern thread_local int global_worker_thread_idx; /** * @brief Prints headers for the per-sandbox perf logs @@ -13,9 +16,7 @@ static inline void sandbox_perf_log_print_header() { if (sandbox_perf_log == NULL) { perror("sandbox perf log"); } - fprintf(sandbox_perf_log, "id,tenant,route,state,deadline,actual,queued,uninitialized,allocated,initialized," - "runnable,interrupted,preempted," - "running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory\n"); + fprintf(sandbox_perf_log, "tid,rid,SLO,total,execution,queued,rtype,init,cleanup,deadline,queuelen,estimated\n"); } /** @@ -26,26 +27,69 @@ sandbox_perf_log_print_header() static inline void sandbox_perf_log_print_entry(struct sandbox *sandbox) { + if (sandbox->state != SANDBOX_COMPLETE && sandbox->state != SANDBOX_ERROR) { + /* only logging when the sandbox is complete or error */ + printf("return state %d\n", sandbox->state); + return; + } /* If the log was not defined by an environment variable, early out */ if (sandbox_perf_log == NULL) return; - uint64_t queued_duration = sandbox->timestamp_of.dispatched - sandbox->timestamp_of.allocation; + uint64_t queued_duration = (sandbox->timestamp_of.dispatched - sandbox->timestamp_of.allocation) / runtime_processor_speed_MHz; + bool miss_deadline = sandbox->timestamp_of.completion > sandbox->absolute_deadline ? true : false; + uint64_t total_time = (sandbox->timestamp_of.completion - sandbox->timestamp_of.allocation) / runtime_processor_speed_MHz; + uint64_t execution_time = (sandbox->duration_of_state[SANDBOX_RUNNING_SYS] + sandbox->duration_of_state[SANDBOX_RUNNING_USER]) + / runtime_processor_speed_MHz; + + uint64_t cleanup = sandbox->timestamp_of.cleanup / runtime_processor_speed_MHz; + uint64_t deadline = sandbox->relative_deadline / runtime_processor_speed_MHz; + uint64_t other = sandbox->timestamp_of.other / runtime_processor_speed_MHz; + /* + uint64_t t1 = sandbox->ret[0] / runtime_processor_speed_MHz; + uint64_t t2 = sandbox->ret[1] / runtime_processor_speed_MHz; + uint64_t t3 = sandbox->ret[2] / runtime_processor_speed_MHz; + uint64_t t4 = sandbox->ret[3] / runtime_processor_speed_MHz; + uint64_t t5 = sandbox->ret[4] / runtime_processor_speed_MHz; + */ + uint64_t estimated_time = sandbox->estimated_cost / runtime_processor_speed_MHz; + + uint64_t init_time = sandbox->duration_of_state[SANDBOX_INITIALIZED] / runtime_processor_speed_MHz; + if (miss_deadline) { + /*mem_log("tid %d %u miss %lu %lu %lu %s %lu %lu %lu f%d %lu %lu %lu %lu %lu %lu\n", + global_worker_thread_idx, sandbox->id, total_time, execution_time, queued_duration, + sandbox->route->route,init_time, cleanup, deadline, sandbox->context_switch_to, other, t1,t2,t3,t4,t5); + */ + mem_log("%d %u miss %lu %lu %lu %u %lu %lu %lu %u %lu\n", + global_worker_thread_idx, sandbox->id, total_time, execution_time, queued_duration, + sandbox->route->request_type, init_time, cleanup, deadline,local_queue_length[global_worker_thread_idx], estimated_time); + + } else { + /*mem_log("tid %d %u meet %lu %lu %lu %s %lu %lu %lu f%d %lu %lu %lu %lu %lu %lu\n", + global_worker_thread_idx, sandbox->id, total_time, execution_time, queued_duration, + sandbox->route->route,init_time, cleanup, deadline, sandbox->context_switch_to, other, t1,t2,t3,t4,t5); + */ + mem_log("%d %u meet %lu %lu %lu %u %lu %lu %lu %lu %lu\n", + global_worker_thread_idx, sandbox->id, total_time, execution_time, queued_duration, + sandbox->route->request_type, init_time, cleanup, deadline,local_queue_length[global_worker_thread_idx], estimated_time); + + } /* * Assumption: A sandbox is never able to free pages. If linear memory management * 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", + /*mem_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], - sandbox->duration_of_state[SANDBOX_INITIALIZED], sandbox->duration_of_state[SANDBOX_RUNNABLE], - sandbox->duration_of_state[SANDBOX_INTERRUPTED], sandbox->duration_of_state[SANDBOX_PREEMPTED], - sandbox->duration_of_state[SANDBOX_RUNNING_SYS], sandbox->duration_of_state[SANDBOX_RUNNING_USER], - sandbox->duration_of_state[SANDBOX_ASLEEP], sandbox->duration_of_state[SANDBOX_RETURNED], - sandbox->duration_of_state[SANDBOX_COMPLETE], sandbox->duration_of_state[SANDBOX_ERROR], + sandbox->route->relative_deadline/runtime_processor_speed_MHz, sandbox->total_time/runtime_processor_speed_MHz, queued_duration, + sandbox->duration_of_state[SANDBOX_UNINITIALIZED]/runtime_processor_speed_MHz, sandbox->duration_of_state[SANDBOX_ALLOCATED]/runtime_processor_speed_MHz, + sandbox->duration_of_state[SANDBOX_INITIALIZED]/runtime_processor_speed_MHz, sandbox->duration_of_state[SANDBOX_RUNNABLE]/runtime_processor_speed_MHz, + sandbox->duration_of_state[SANDBOX_INTERRUPTED]/runtime_processor_speed_MHz, sandbox->duration_of_state[SANDBOX_PREEMPTED]/runtime_processor_speed_MHz, + sandbox->duration_of_state[SANDBOX_RUNNING_SYS]/runtime_processor_speed_MHz, sandbox->duration_of_state[SANDBOX_RUNNING_USER]/runtime_processor_speed_MHz, + sandbox->duration_of_state[SANDBOX_ASLEEP]/runtime_processor_speed_MHz, sandbox->duration_of_state[SANDBOX_RETURNED]/runtime_processor_speed_MHz, + sandbox->duration_of_state[SANDBOX_COMPLETE]/runtime_processor_speed_MHz, sandbox->duration_of_state[SANDBOX_ERROR]/runtime_processor_speed_MHz, runtime_processor_speed_MHz); + */ } static inline void diff --git a/runtime/include/sandbox_set_as_complete.h b/runtime/include/sandbox_set_as_complete.h index 9ef7dd58f..3cc3b949a 100644 --- a/runtime/include/sandbox_set_as_complete.h +++ b/runtime/include/sandbox_set_as_complete.h @@ -28,7 +28,7 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state) switch (last_state) { case SANDBOX_RETURNED: { - sandbox->timestamp_of.completion = now; + //sandbox->timestamp_of.completion = now; break; } default: { @@ -45,14 +45,19 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state) sandbox_state_history_append(&sandbox->state_history, SANDBOX_COMPLETE); sandbox_state_totals_increment(SANDBOX_COMPLETE); sandbox_state_totals_decrement(last_state); - + //---------xiaosu--------------- + //printf("id %lu total running sys %lu, total running user %lu\n", sandbox->id, sandbox->duration_of_state[SANDBOX_RUNNING_SYS], + // sandbox->duration_of_state[SANDBOX_RUNNING_USER]); + //----------xiaosu------------- /* Admissions Control Post Processing */ admissions_info_update(&sandbox->route->admissions_info, sandbox->duration_of_state[SANDBOX_RUNNING_USER] + sandbox->duration_of_state[SANDBOX_RUNNING_SYS]); + perf_window_per_thread_update(&sandbox->route->admissions_info, sandbox->duration_of_state[SANDBOX_RUNNING_USER] + + sandbox->duration_of_state[SANDBOX_RUNNING_SYS]); admissions_control_subtract(sandbox->admissions_estimate); /* Terminal State Logging for Sandbox */ - sandbox_perf_log_print_entry(sandbox); + //sandbox_perf_log_print_entry(sandbox); sandbox_summarize_page_allocations(sandbox); route_latency_add(&sandbox->route->latency, sandbox->total_time); diff --git a/runtime/include/sandbox_set_as_error.h b/runtime/include/sandbox_set_as_error.h index 39020ed95..63e2682e2 100644 --- a/runtime/include/sandbox_set_as_error.h +++ b/runtime/include/sandbox_set_as_error.h @@ -58,13 +58,14 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state) admissions_control_subtract(sandbox->admissions_estimate); /* Return HTTP session to listener core to be written back to client */ - http_session_set_response_header(sandbox->http, 500); - sandbox->http->state = HTTP_SESSION_EXECUTION_COMPLETE; - http_session_send_response(sandbox->http, (void_star_cb)listener_thread_register_http_session); - sandbox->http = NULL; + //http_session_set_response_header(sandbox->http, 500); + + //sandbox->http->state = HTTP_SESSION_EXECUTION_COMPLETE; + //http_session_send_response(sandbox->http, (void_star_cb)listener_thread_register_http_session); + //sandbox->http = NULL; /* Terminal State Logging */ - sandbox_perf_log_print_entry(sandbox); + //sandbox_perf_log_print_entry(sandbox); sandbox_summarize_page_allocations(sandbox); /* State Change Hooks */ diff --git a/runtime/include/sandbox_set_as_initialized.h b/runtime/include/sandbox_set_as_initialized.h index 9ff7771a1..e7f06a2ce 100644 --- a/runtime/include/sandbox_set_as_initialized.h +++ b/runtime/include/sandbox_set_as_initialized.h @@ -40,7 +40,7 @@ sandbox_set_as_initialized(struct sandbox *sandbox, sandbox_state_t last_state) sandbox->timestamp_of.last_state_change = now; sandbox_state_history_append(&sandbox->state_history, SANDBOX_INITIALIZED); sandbox_state_totals_increment(SANDBOX_INITIALIZED); - sandbox_state_totals_decrement(last_state); + //sandbox_state_totals_decrement(last_state); /* State Change Hooks */ sandbox_state_transition_from_hook(sandbox, last_state); diff --git a/runtime/include/sandbox_set_as_interrupted.h b/runtime/include/sandbox_set_as_interrupted.h index e2ee3699d..41cc08797 100644 --- a/runtime/include/sandbox_set_as_interrupted.h +++ b/runtime/include/sandbox_set_as_interrupted.h @@ -26,6 +26,10 @@ sandbox_set_as_interrupted(struct sandbox *sandbox, sandbox_state_t last_state) assert(now > sandbox->timestamp_of.last_state_change); sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change; sandbox->duration_of_state[last_state] += sandbox->last_state_duration; + //------------xiaosu------------------- + //printf("id %lu interrupt, current ts %lu last state %d last duration %lu total duration %lu\n", sandbox->id, now, last_state, + // sandbox->last_state_duration, sandbox->duration_of_state[last_state]); + //-------------xiaosu------------------ sandbox->timestamp_of.last_state_change = now; /* We do not append SANDBOX_INTERRUPTED to the sandbox_state_history because it would quickly fill the buffer */ sandbox_state_totals_increment(SANDBOX_INTERRUPTED); diff --git a/runtime/include/sandbox_set_as_preempted.h b/runtime/include/sandbox_set_as_preempted.h index 3d30c93f1..d0025fbc1 100644 --- a/runtime/include/sandbox_set_as_preempted.h +++ b/runtime/include/sandbox_set_as_preempted.h @@ -48,6 +48,8 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state) /* State Change Hooks */ sandbox_state_transition_from_hook(sandbox, last_state); sandbox_state_transition_to_hook(sandbox, SANDBOX_PREEMPTED); + /* Update TS when sandbox was preempted */ + sandbox->srsf_stop_running_ts = now; } static inline void diff --git a/runtime/include/sandbox_set_as_returned.h b/runtime/include/sandbox_set_as_returned.h index 2eec1bf2d..37dce98fd 100644 --- a/runtime/include/sandbox_set_as_returned.h +++ b/runtime/include/sandbox_set_as_returned.h @@ -14,6 +14,8 @@ #include "sandbox_state_transition.h" #include "sandbox_types.h" +extern struct sandbox* current_sandboxes[1024]; +extern thread_local int global_worker_thread_idx; /** * Transitions a sandbox to the SANDBOX_RETURNED state. * This occurs when a sandbox is executing and runs to completion. @@ -33,6 +35,7 @@ sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state) switch (last_state) { case SANDBOX_RUNNING_SYS: { local_runqueue_delete(sandbox); + current_sandboxes[global_worker_thread_idx] = NULL; sandbox_free_linear_memory(sandbox); break; } @@ -51,11 +54,6 @@ sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state) sandbox_state_totals_increment(SANDBOX_RETURNED); sandbox_state_totals_decrement(last_state); - http_session_set_response_header(sandbox->http, 200); - sandbox->http->state = HTTP_SESSION_EXECUTION_COMPLETE; - http_session_send_response(sandbox->http, (void_star_cb)listener_thread_register_http_session); - sandbox->http = NULL; - /* State Change Hooks */ sandbox_state_transition_from_hook(sandbox, last_state); sandbox_state_transition_to_hook(sandbox, SANDBOX_RETURNED); diff --git a/runtime/include/sandbox_set_as_runnable.h b/runtime/include/sandbox_set_as_runnable.h index 6b404d53d..115a59158 100644 --- a/runtime/include/sandbox_set_as_runnable.h +++ b/runtime/include/sandbox_set_as_runnable.h @@ -30,8 +30,7 @@ sandbox_set_as_runnable(struct sandbox *sandbox, sandbox_state_t last_state) switch (last_state) { case SANDBOX_INITIALIZED: { - sandbox->timestamp_of.dispatched = now; - local_runqueue_add(sandbox); + //sandbox->timestamp_of.dispatched = now; break; } case SANDBOX_ASLEEP: { diff --git a/runtime/include/sandbox_set_as_running_sys.h b/runtime/include/sandbox_set_as_running_sys.h index 5c00013aa..ec8c6840c 100644 --- a/runtime/include/sandbox_set_as_running_sys.h +++ b/runtime/include/sandbox_set_as_running_sys.h @@ -25,7 +25,7 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) switch (last_state) { case SANDBOX_RUNNING_USER: { assert(sandbox == current_sandbox_get()); - assert(runtime_worker_threads_deadline[worker_thread_idx] == sandbox->absolute_deadline); + assert(runtime_worker_threads_deadline[global_worker_thread_idx] == sandbox->absolute_deadline); break; } case SANDBOX_RUNNABLE: { @@ -42,6 +42,12 @@ sandbox_set_as_running_sys(struct sandbox *sandbox, sandbox_state_t last_state) assert(now > sandbox->timestamp_of.last_state_change); sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change; sandbox->duration_of_state[last_state] += sandbox->last_state_duration; + //--------------xiaosu------------------ + //if (last_state == SANDBOX_RUNNING_USER) { + // printf("id %lu running sys, current ts %lu last running user duration %lu total running user duration %lu\n", + // sandbox->id, now, sandbox->last_state_duration, sandbox->duration_of_state[last_state]); + //} + //-------------xiaosu------------------- sandbox->timestamp_of.last_state_change = now; sandbox_state_history_append(&sandbox->state_history, SANDBOX_RUNNING_SYS); sandbox_state_totals_increment(SANDBOX_RUNNING_SYS); diff --git a/runtime/include/sandbox_set_as_running_user.h b/runtime/include/sandbox_set_as_running_user.h index bf6ade7ac..aff21f103 100644 --- a/runtime/include/sandbox_set_as_running_user.h +++ b/runtime/include/sandbox_set_as_running_user.h @@ -21,10 +21,12 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) switch (last_state) { case SANDBOX_RUNNING_SYS: { assert(sandbox == current_sandbox_get()); - assert(runtime_worker_threads_deadline[worker_thread_idx] == sandbox->absolute_deadline); + assert(runtime_worker_threads_deadline[global_worker_thread_idx] == sandbox->absolute_deadline); break; } case SANDBOX_PREEMPTED: { + /* Sandbox resumes, update its RS */ + sandbox->srsf_remaining_slack = sandbox->srsf_remaining_slack - (__getcycles() - sandbox->srsf_stop_running_ts); break; } default: { @@ -38,6 +40,15 @@ sandbox_set_as_running_user(struct sandbox *sandbox, sandbox_state_t last_state) assert(now > sandbox->timestamp_of.last_state_change); sandbox->last_state_duration = now - sandbox->timestamp_of.last_state_change; sandbox->duration_of_state[last_state] += sandbox->last_state_duration; + //------------xiaosu------------------ + /*if (last_state == SANDBOX_RUNNING_SYS) { + printf("id %lu running user, last running sys, current ts %lu total running user %lu total running sys %lu\n", + sandbox->id, now, sandbox->duration_of_state[SANDBOX_RUNNING_USER], sandbox->duration_of_state[last_state]); + } else { + printf("id %lu running user, last preempt, current ts %lu total running user %lu total preempt %lu\n", + sandbox->id, now, sandbox->duration_of_state[SANDBOX_RUNNING_USER], sandbox->duration_of_state[last_state]); + }*/ + //-------------xiaosu---------------- sandbox->timestamp_of.last_state_change = now; sandbox_state_history_append(&sandbox->state_history, SANDBOX_RUNNING_USER); sandbox_state_totals_increment(SANDBOX_RUNNING_USER); diff --git a/runtime/include/sandbox_types.h b/runtime/include/sandbox_types.h index 796c4b3ae..30fb71818 100644 --- a/runtime/include/sandbox_types.h +++ b/runtime/include/sandbox_types.h @@ -24,6 +24,8 @@ struct sandbox_timestamps { uint64_t allocation; /* Timestamp when sandbox is allocated */ uint64_t dispatched; /* Timestamp when a sandbox is first added to a worker's runqueue */ uint64_t completion; /* Timestamp when sandbox runs to completion */ + uint64_t cleanup; /* Time duration of cleaning up the previous sandboxes */ + uint64_t other; /* Time duration of only sandbox_free */ #ifdef LOG_SANDBOX_MEMORY_PROFILE uint32_t page_allocations[SANDBOX_PAGE_ALLOCATION_TIMESTAMP_COUNT]; size_t page_allocations_size; @@ -66,8 +68,28 @@ struct sandbox { deadline (cycles) */ uint64_t total_time; /* Total time from Request to Response */ + void *rpc_handler; + uint8_t rpc_id; + uint8_t *rpc_request_body; + size_t rpc_request_body_size; + /* Runtime state used by WASI */ + int cursor; /* Sandbox cursor (offset from body pointer) */ + struct auto_buf response_body; + //size_t response_body_written; + /* System Interface State */ int32_t return_value; wasi_context_t *wasi_context; - + int context_switch_to; /* 1 means context switch to base, 2 means context swtich to next sandbox */ + uint64_t ret[5]; + uint64_t estimated_cost; /* estimated execution cost */ + uint64_t relative_deadline; + int global_worker_thread_idx; /* which thread in a global view processes this sandbox. + pause and restore the sandbox must be done in the same thread */ + int group_worker_thread_idx; /* what's the thread index in the group */ + uint64_t start_ts; /* the start timestamp when dispatcher assign this sandbox to a worker */ + /* For SRSF */ + uint64_t srsf_stop_running_ts; /* record the timestamp of stopping running of this sandbox, in cycles */ + int64_t srsf_remaining_slack; + } PAGE_ALIGNED; diff --git a/runtime/include/scheduler.h b/runtime/include/scheduler.h index 69c96e7ab..066a20456 100644 --- a/runtime/include/scheduler.h +++ b/runtime/include/scheduler.h @@ -11,6 +11,8 @@ #include "global_request_scheduler_mtds.h" #include "local_runqueue.h" #include "local_runqueue_minheap.h" +#include "local_runqueue_binary_tree.h" +#include "local_runqueue_circular_queue.h" #include "local_runqueue_list.h" #include "local_cleanup_queue.h" #include "local_runqueue_mtds.h" @@ -23,7 +25,22 @@ #include "sandbox_set_as_interrupted.h" #include "sandbox_set_as_running_user.h" #include "scheduler_options.h" - +#include "sandbox_perf_log.h" +#include "request_typed_queue.h" +#include "listener_thread.h" +#include "local_preempted_fifo_queue.h" + +extern struct timespec startT[1024]; +extern bool runtime_worker_busy_loop_enabled; +extern thread_local uint32_t interrupts; +extern thread_local bool pthread_stop; +extern uint32_t runtime_worker_group_size; +extern _Atomic uint32_t free_workers[10]; +extern thread_local int dispatcher_id; +extern pthread_mutex_t mutexs[1024]; +extern pthread_cond_t conds[1024]; +extern sem_t semlock[1024]; +extern uint32_t runtime_fifo_queue_batch_size; /** * This scheduler provides for cooperative and preemptive multitasking in a OS process's userspace. @@ -78,7 +95,7 @@ scheduler_mtds_get_next() enum MULTI_TENANCY_CLASS local_mt_class = MT_DEFAULT; struct sandbox *global = NULL; - if (local) local_mt_class = local->tenant->pwt_sandboxes[worker_thread_idx].mt_class; + if (local) local_mt_class = local->tenant->pwt_sandboxes[global_worker_thread_idx].mt_class; uint64_t global_guaranteed_deadline = global_request_scheduler_mtds_guaranteed_peek(); uint64_t global_default_deadline = global_request_scheduler_mtds_default_peek(); @@ -112,24 +129,23 @@ scheduler_edf_get_next() /* Get the deadline of the sandbox at the head of the local queue */ struct sandbox *local = local_runqueue_get_next(); uint64_t local_deadline = local == NULL ? UINT64_MAX : local->absolute_deadline; - struct sandbox *global = NULL; - - uint64_t global_deadline = global_request_scheduler_peek(); - /* Try to pull and allocate from the global queue if earlier - * This will be placed at the head of the local runqueue */ - if (global_deadline < local_deadline) { - if (global_request_scheduler_remove_if_earlier(&global, local_deadline) == 0) { - assert(global != NULL); - assert(global->absolute_deadline < local_deadline); - sandbox_prepare_execution_environment(global); - assert(global->state == SANDBOX_INITIALIZED); - sandbox_set_as_runnable(global, SANDBOX_INITIALIZED); + if (local != NULL) { + if (local->state == SANDBOX_INITIALIZED) { + /* add by xiaosu */ + uint64_t now = __getcycles(); + local->timestamp_of.dispatched = now; + local->duration_of_state[SANDBOX_INITIALIZED] = 0; + local->timestamp_of.last_state_change = now; + /* end by xiaosu */ + sandbox_prepare_execution_environment(local); + //printf("sandbox state %d\n", local->state); + assert(local->state == SANDBOX_INITIALIZED); + sandbox_set_as_runnable(local, SANDBOX_INITIALIZED); } } - /* Return what is at the head of the local runqueue or NULL if empty */ - return local_runqueue_get_next(); + return local; } static inline struct sandbox * @@ -140,19 +156,43 @@ scheduler_fifo_get_next() struct sandbox *global = NULL; if (local == NULL) { - /* If the local runqueue is empty, pull from global request scheduler */ - if (global_request_scheduler_remove(&global) < 0) goto done; + if (disable_get_req_from_GQ) { + return NULL; + } - sandbox_prepare_execution_environment(global); - sandbox_set_as_runnable(global, SANDBOX_INITIALIZED); + /* If the local runqueue is empty, pull a batch of requests from global request scheduler */ + for (int i = 0; i < runtime_fifo_queue_batch_size; i++ ) { + if (global_request_scheduler_remove(&global) < 0) break; + + if (global->state == SANDBOX_INITIALIZED) { + /* add by xiaosu */ + uint64_t now = __getcycles(); + global->timestamp_of.dispatched = now; + global->duration_of_state[SANDBOX_INITIALIZED] = 0; + global->timestamp_of.last_state_change = now; + /* end by xiaosu */ + sandbox_prepare_execution_environment(global); + local_runqueue_add(global); + sandbox_set_as_runnable(global, SANDBOX_INITIALIZED); + } + } } else if (local == current_sandbox_get()) { /* Execute Round Robin Scheduling Logic if the head is the current sandbox */ local_runqueue_list_rotate(); - } - - -done: - return local_runqueue_get_next(); + } + + local = local_runqueue_get_next(); + if (local && local->state == SANDBOX_INITIALIZED) { + /* add by xiaosu */ + uint64_t now = __getcycles(); + local->timestamp_of.dispatched = now; + local->duration_of_state[SANDBOX_INITIALIZED] = 0; + local->timestamp_of.last_state_change = now; + /* end by xiaosu */ + sandbox_prepare_execution_environment(local); + sandbox_set_as_runnable(local, SANDBOX_INITIALIZED); + } + return local; } static inline struct sandbox * @@ -204,7 +244,13 @@ scheduler_runqueue_initialize() local_runqueue_mtds_initialize(); break; case SCHEDULER_EDF: - local_runqueue_minheap_initialize(); + if (dispatcher == DISPATCHER_SHINJUKU || dispatcher == DISPATCHER_DARC) { + local_runqueue_circular_queue_initialize(); + } else if (dispatcher == DISPATCHER_EDF_INTERRUPT) { + local_runqueue_binary_tree_initialize(); + } else { + local_runqueue_minheap_initialize(); + } break; case SCHEDULER_FIFO: local_runqueue_list_initialize(); @@ -255,6 +301,7 @@ scheduler_preemptive_switch_to(ucontext_t *interrupted_context, struct sandbox * { /* Switch to next sandbox */ switch (next->ctxt.variant) { + /* This sandbox is a new sandbox */ case ARCH_CONTEXT_VARIANT_FAST: { assert(next->state == SANDBOX_RUNNABLE); arch_context_restore_fast(&interrupted_context->uc_mcontext, &next->ctxt); @@ -262,10 +309,14 @@ scheduler_preemptive_switch_to(ucontext_t *interrupted_context, struct sandbox * sandbox_set_as_running_sys(next, SANDBOX_RUNNABLE); break; } + /* This sandbox is a preempted sandbox */ case ARCH_CONTEXT_VARIANT_SLOW: { assert(next->state == SANDBOX_PREEMPTED); arch_context_restore_slow(&interrupted_context->uc_mcontext, &next->ctxt); current_sandbox_set(next); + //-----------xiaosu-------------- + //printf("id %lu resume from slow\n", next->id); + //-----------xiaosu-------------- sandbox_set_as_running_user(next, SANDBOX_PREEMPTED); break; } @@ -301,7 +352,7 @@ scheduler_process_policy_specific_updates_on_interrupts(struct sandbox *interrup } /** - * Called by the SIGALRM handler after a quantum + * Called by the SIGALRM handler after a quantum, this function can only be called by SHINJUKU or EDF_INTERRUPT * Assumes the caller validates that there is something to preempt * @param interrupted_context - The context of our user-level Worker thread * @returns the sandbox that the scheduler chose to run @@ -316,15 +367,43 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) struct sandbox *interrupted_sandbox = current_sandbox_get(); assert(interrupted_sandbox != NULL); assert(interrupted_sandbox->state == SANDBOX_INTERRUPTED); - // printf ("Worker #%d interrupted sandbox #%lu\n", worker_thread_idx, interrupted_sandbox->id); + // printf ("Worker #%d interrupted sandbox #%lu\n", global_worker_thread_idx, interrupted_sandbox->id); scheduler_process_policy_specific_updates_on_interrupts(interrupted_sandbox); + /* Delete current sandbox from local queue if dispatcher is DISPATCHER_SHINJUKU */ + if (dispatcher == DISPATCHER_SHINJUKU) { + if (local_runqueue_get_length() > 1) { + local_runqueue_delete(interrupted_sandbox); + sandbox_preempt(interrupted_sandbox); + // Write back global at idx 0 + wasm_globals_set_i64(&interrupted_sandbox->globals, 0, sledge_abi__current_wasm_module_instance.abi.wasmg_0, + true); + arch_context_save_slow(&interrupted_sandbox->ctxt, &interrupted_context->uc_mcontext); + int ret = push_to_preempted_queue(interrupted_sandbox, __getcycles()); + assert(ret != -1); + + interrupts++; + struct sandbox *next = scheduler_get_next(); + assert(next != NULL); + scheduler_preemptive_switch_to(interrupted_context, next); + } else { + /* current sandbox shouldn't be interrupted becuase it is the only request in the local queue + so return directly, the current context isn't switched and will resume when single handler + returns + */ + sandbox_interrupt_return(interrupted_sandbox, SANDBOX_RUNNING_USER); + return; + } + return; + } + struct sandbox *next = scheduler_get_next(); - /* Assumption: the current sandbox is on the runqueue, so the scheduler should always return something */ + + /* Assumption: the current sandbox is on the runqueue, so the scheduler should always return something */ assert(next != NULL); - /* If current equals next, no switch is necessary, so resume execution */ - if (interrupted_sandbox == next) { + /* If current equals next, no switch is necessary, or currend deadline is ealier than the next deadline or its RS <= 0, just resume execution */ + if (interrupted_sandbox == next || interrupted_sandbox->absolute_deadline <= next->absolute_deadline || interrupted_sandbox->srsf_remaining_slack <= 0) { sandbox_interrupt_return(interrupted_sandbox, SANDBOX_RUNNING_USER); return; } @@ -340,8 +419,9 @@ scheduler_preemptive_sched(ucontext_t *interrupted_context) // Write back global at idx 0 wasm_globals_set_i64(&interrupted_sandbox->globals, 0, sledge_abi__current_wasm_module_instance.abi.wasmg_0, true); - arch_context_save_slow(&interrupted_sandbox->ctxt, &interrupted_context->uc_mcontext); + + interrupts++; scheduler_preemptive_switch_to(interrupted_context, next); } @@ -387,12 +467,15 @@ scheduler_switch_to_base_context(struct arch_context *current_context) arch_context_switch(current_context, &worker_thread_base_context); } - /* The idle_loop is executed by the base_context. This should not be called directly */ static inline void scheduler_idle_loop() { - while (true) { + uint64_t cleanup_cost = 0; + uint64_t other = 0; + int cleanup_count = 0; + uint64_t ret[5] = {0}; + while (!pthread_stop) { /* Assumption: only called by the "base context" */ assert(current_sandbox_get() == NULL); @@ -402,14 +485,49 @@ scheduler_idle_loop() /* Switch to a sandbox if one is ready to run */ struct sandbox *next_sandbox = scheduler_get_next(); if (next_sandbox != NULL) { + //printf("poped a sandbox\n"); + next_sandbox->timestamp_of.cleanup += cleanup_cost; + next_sandbox->timestamp_of.other += other; + next_sandbox->context_switch_to = 1; + next_sandbox->ret[0] += ret[0]; + next_sandbox->ret[1] += ret[1]; + next_sandbox->ret[2] += ret[2]; + next_sandbox->ret[3] += ret[3]; + next_sandbox->ret[4] += ret[4]; + cleanup_cost = 0; + other = 0; + ret[0] = ret[1] = ret[2] = ret[3] = ret[4] = 0; scheduler_cooperative_switch_to(&worker_thread_base_context, next_sandbox); } - /* Clear the cleanup queue */ - local_cleanup_queue_free(); - - /* Improve the performance of spin-wait loops (works only if preemptions enabled) */ - if (runtime_worker_spinloop_pause_enabled) pause(); + uint64_t now = __getcycles(); + uint64_t duration = 0; + uint64_t ret_inner[5] = {0}; + cleanup_count = local_cleanup_queue_free(&duration, ret_inner); + if (cleanup_count != 0 && cleanup_cost == 0) { + cleanup_cost += __getcycles() - now; + other += duration; + ret[0] += ret_inner[0]; + ret[1] += ret_inner[1]; + ret[2] += ret_inner[2]; + ret[3] += ret_inner[3]; + ret[4] += ret_inner[4]; + } + /* If queue is empty, then sleep to wait for the condition variable or sempahore */ + if (!runtime_worker_busy_loop_enabled) { + /*pthread_mutex_lock(&mutexs[global_worker_thread_idx]); + if (local_runqueue_is_empty()) { + pthread_cond_wait(&conds[global_worker_thread_idx], &mutexs[global_worker_thread_idx]); + } + pthread_mutex_unlock(&mutexs[global_worker_thread_idx]); + */ + /* Semaphore could lost signal and cause worker block for a short time decrasing performance, + but based on test, it seems Semaphore has a better performance than condition variable + */ + if (local_runqueue_is_empty()) { + sem_wait(&semlock[global_worker_thread_idx]); + } + } } } @@ -423,8 +541,11 @@ scheduler_idle_loop() static inline void scheduler_cooperative_sched(bool add_to_cleanup_queue) { + //uint64_t now = __getcycles(); struct sandbox *exiting_sandbox = current_sandbox_get(); + assert(exiting_sandbox != NULL); + //uint8_t dispatcher_id = exiting_sandbox->rpc_id; /* Clearing current sandbox indicates we are entering the cooperative scheduler */ current_sandbox_set(NULL); @@ -439,9 +560,18 @@ scheduler_cooperative_sched(bool add_to_cleanup_queue) /* Deferred signals should have been cleared by this point */ assert(deferred_sigalrm == 0); + uint64_t now = __getcycles(); + uint64_t duration = 0; + uint64_t ret[5] = {0}; /* We have not added ourself to the cleanup queue, so we can free */ - local_cleanup_queue_free(); - + local_cleanup_queue_free(&duration, ret); + exiting_sandbox->timestamp_of.cleanup += (__getcycles() - now); + exiting_sandbox->timestamp_of.other += duration; + exiting_sandbox->ret[0] += ret[0]; + exiting_sandbox->ret[1] += ret[1]; + exiting_sandbox->ret[2] += ret[2]; + exiting_sandbox->ret[3] += ret[3]; + exiting_sandbox->ret[4] += ret[4]; /* Switch to a sandbox if one is ready to run */ struct sandbox *next_sandbox = scheduler_get_next(); @@ -459,15 +589,21 @@ scheduler_cooperative_sched(bool add_to_cleanup_queue) if (add_to_cleanup_queue) local_cleanup_queue_add(exiting_sandbox); /* Do not touch sandbox struct after this point! */ + /* Logging this sandbox to memory */ + sandbox_perf_log_print_entry(exiting_sandbox); if (next_sandbox != NULL) { + next_sandbox->context_switch_to = 2; scheduler_cooperative_switch_to(exiting_context, next_sandbox); } else { - scheduler_switch_to_base_context(exiting_context); + if (dispatcher == DISPATCHER_DARC) { + int virtual_id = global_worker_thread_idx - dispatcher_id * runtime_worker_group_size; + atomic_fetch_or(&free_workers[dispatcher_id], 1 << virtual_id); + } + scheduler_switch_to_base_context(exiting_context); } } - static inline bool scheduler_worker_would_preempt(int worker_idx) { diff --git a/runtime/include/scheduler_options.h b/runtime/include/scheduler_options.h index 930dbd640..b918f4664 100644 --- a/runtime/include/scheduler_options.h +++ b/runtime/include/scheduler_options.h @@ -9,3 +9,4 @@ enum SCHEDULER }; extern enum SCHEDULER scheduler; +extern bool disable_get_req_from_GQ; diff --git a/runtime/include/software_interrupt.h b/runtime/include/software_interrupt.h index e1f048c53..319f1e5b6 100644 --- a/runtime/include/software_interrupt.h +++ b/runtime/include/software_interrupt.h @@ -31,7 +31,7 @@ software_interrupt_mask_signal(int signal) sigset_t set; int return_code; - assert(signal == SIGALRM || signal == SIGUSR1 || signal == SIGFPE || signal == SIGSEGV); + assert(signal == SIGALRM || signal == SIGUSR1 || signal == SIGFPE || signal == SIGSEGV || signal == SIGINT); /* all threads created by the calling thread will have signal blocked */ sigemptyset(&set); sigaddset(&set, signal); @@ -55,7 +55,7 @@ software_interrupt_unmask_signal(int signal) sigset_t set; int return_code; - assert(signal == SIGALRM || signal == SIGUSR1 || signal == SIGFPE || signal == SIGSEGV); + assert(signal == SIGALRM || signal == SIGUSR1 || signal == SIGFPE || signal == SIGSEGV || signal == SIGINT); /* all threads created by the calling thread will have signal unblocked */ sigemptyset(&set); sigaddset(&set, signal); @@ -96,3 +96,5 @@ void software_interrupt_arm_timer(void); void software_interrupt_cleanup(void); void software_interrupt_disarm_timer(void); void software_interrupt_initialize(void); +bool sandbox_is_preemptable(void *sandbox); +void preempt_worker(int thread_id); diff --git a/runtime/include/software_interrupt_counts.h b/runtime/include/software_interrupt_counts.h index 9daa5afe5..ed6d72d3a 100644 --- a/runtime/include/software_interrupt_counts.h +++ b/runtime/include/software_interrupt_counts.h @@ -46,8 +46,8 @@ static inline void software_interrupt_counts_deferred_sigalrm_max_update(int deferred_sigalrm_count) { #ifdef LOG_SOFTWARE_INTERRUPT_COUNTS - if (unlikely(deferred_sigalrm_count > software_interrupt_counts_deferred_sigalrm_max[worker_thread_idx])) { - software_interrupt_counts_deferred_sigalrm_max[worker_thread_idx] = deferred_sigalrm_count; + if (unlikely(deferred_sigalrm_count > software_interrupt_counts_deferred_sigalrm_max[global_worker_thread_idx])) { + software_interrupt_counts_deferred_sigalrm_max[global_worker_thread_idx] = deferred_sigalrm_count; } #endif } @@ -56,7 +56,7 @@ static inline void software_interrupt_counts_deferred_sigalrm_replay_increment() { #ifdef LOG_SOFTWARE_INTERRUPT_COUNTS - atomic_fetch_add(&software_interrupt_counts_deferred_sigalrm_replay[worker_thread_idx], 1); + atomic_fetch_add(&software_interrupt_counts_deferred_sigalrm_replay[global_worker_thread_idx], 1); #endif } @@ -64,7 +64,7 @@ static inline void software_interrupt_counts_sigalrm_kernel_increment() { #ifdef LOG_SOFTWARE_INTERRUPT_COUNTS - atomic_fetch_add(&software_interrupt_counts_sigalrm_kernel[worker_thread_idx], 1); + atomic_fetch_add(&software_interrupt_counts_sigalrm_kernel[global_worker_thread_idx], 1); #endif } @@ -72,7 +72,7 @@ static inline void software_interrupt_counts_sigalrm_thread_increment() { #ifdef LOG_SOFTWARE_INTERRUPT_COUNTS - atomic_fetch_add(&software_interrupt_counts_sigalrm_thread[worker_thread_idx], 1); + atomic_fetch_add(&software_interrupt_counts_sigalrm_thread[global_worker_thread_idx], 1); #endif } @@ -80,7 +80,7 @@ static inline void software_interrupt_counts_sigfpe_increment() { #ifdef LOG_SOFTWARE_INTERRUPT_COUNTS - atomic_fetch_add(&software_interrupt_counts_sigfpe[worker_thread_idx], 1); + atomic_fetch_add(&software_interrupt_counts_sigfpe[global_worker_thread_idx], 1); #endif } @@ -88,7 +88,7 @@ static inline void software_interrupt_counts_sigsegv_increment() { #ifdef LOG_SOFTWARE_INTERRUPT_COUNTS - atomic_fetch_add(&software_interrupt_counts_sigsegv[worker_thread_idx], 1); + atomic_fetch_add(&software_interrupt_counts_sigsegv[global_worker_thread_idx], 1); #endif } @@ -96,7 +96,7 @@ static inline void software_interrupt_counts_sigusr_increment() { #ifdef LOG_SOFTWARE_INTERRUPT_COUNTS - atomic_fetch_add(&software_interrupt_counts_sigusr[worker_thread_idx], 1); + atomic_fetch_add(&software_interrupt_counts_sigusr[global_worker_thread_idx], 1); #endif } diff --git a/runtime/include/tenant.h b/runtime/include/tenant.h index 615bcead3..113fdca35 100644 --- a/runtime/include/tenant.h +++ b/runtime/include/tenant.h @@ -35,6 +35,9 @@ struct tenant_global_request_queue { struct tenant { enum epoll_tag tag; /* Tag must be first member */ char *name; + uint16_t port; + struct route_config *routes_config; + size_t routes_len; struct tcp_server tcp_server; http_router_t router; struct module_database module_db; diff --git a/runtime/include/tenant_functions.h b/runtime/include/tenant_functions.h index 423f6b6d8..65cb23e6a 100644 --- a/runtime/include/tenant_functions.h +++ b/runtime/include/tenant_functions.h @@ -13,6 +13,16 @@ #include "tenant_config.h" #include "priority_queue.h" #include "sandbox_functions.h" +#include "request_typed_queue.h" +#include "request_typed_deque.h" +#include "memlogging.h" + +extern thread_local uint32_t n_rtypes; +extern thread_local struct request_typed_queue *request_type_queue[MAX_REQUEST_TYPE]; +extern thread_local struct request_typed_deque *request_type_deque[MAX_REQUEST_TYPE]; +extern thread_local uint8_t dispatcher_thread_idx; +extern thread_local struct perf_window perf_window_per_thread[1024]; +extern thread_local int global_worker_thread_idx; int tenant_database_add(struct tenant *tenant); struct tenant *tenant_database_find_by_name(char *name); @@ -77,19 +87,19 @@ tenant_policy_specific_init(struct tenant *tenant, struct tenant_config *config) static inline struct tenant * tenant_alloc(struct tenant_config *config) { - struct tenant *existing_tenant = tenant_database_find_by_name(config->name); - if (existing_tenant != NULL) panic("Tenant %s is already initialized\n", existing_tenant->name); - - existing_tenant = tenant_database_find_by_port(config->port); + struct tenant *existing_tenant = tenant_database_find_by_port(config->port); if (existing_tenant != NULL) panic("Tenant %s is already configured with port %u\n", existing_tenant->name, config->port); struct tenant *tenant = (struct tenant *)calloc(1, sizeof(struct tenant)); /* Move name */ - tenant->tag = EPOLL_TAG_TENANT_SERVER_SOCKET; - tenant->name = config->name; - config->name = NULL; + tenant->tag = EPOLL_TAG_TENANT_SERVER_SOCKET; + tenant->name = config->name; + tenant->port = config->port; + tenant->routes_config = config->routes; + tenant->routes_len = config->routes_len; + config->name = NULL; tcp_server_init(&tenant->tcp_server, config->port); http_router_init(&tenant->router, config->routes_len); @@ -162,3 +172,46 @@ get_next_timeout_of_tenant(uint64_t replenishment_period) */ int tenant_listen(struct tenant *tenant); int listener_thread_register_tenant(struct tenant *tenant); + +static inline void +tenant_preallocate_memory(struct tenant *tenant, void *arg1, void *arg2) { + struct module **modules = tenant->module_db.modules; + size_t count = tenant->module_db.count; + for(int i = 0; i < count; i++) { + module_preallocate_memory(modules[i]); + } +} + +static inline void +tenant_perf_window_init(struct tenant *tenant, void *arg1, void *arg2) { + for(int i = 0; i < tenant->router.length; i++) { + perf_window_initialize(&perf_window_per_thread[i]); + } +} + +static inline void +tenant_request_typed_queue_init(struct tenant *tenant, void *arg1, void *arg2) { + + if (dispatcher == DISPATCHER_SHINJUKU) { + for(int i = 0; i < tenant->routes_len; i++) { + request_type_deque[tenant->routes_config[i].request_type - 1] = + request_typed_deque_init(tenant->routes_config[i].request_type, 4096); + n_rtypes++; + } + } else if (dispatcher == DISPATCHER_DARC) { + for(int i = 0; i < tenant->routes_len; i++) { + if (request_type_queue[tenant->routes_config[i].group_id - 1] == NULL) { + request_type_queue[tenant->routes_config[i].group_id - 1] = + request_typed_queue_init(tenant->routes_config[i].group_id, tenant->routes_config[i].n_resas); + n_rtypes++; + } + } + } +} + +static inline void +tenat_perf_window_print_mean(struct tenant *tenant, void *arg1, void *arg2) { + for(int i = 0; i < tenant->router.length; i++) { + mem_log("tid %d admssion id %u exec mean %lu\n", global_worker_thread_idx, i, perf_window_get_mean(&perf_window_per_thread[i])); + } +} diff --git a/runtime/include/wasm_stack.h b/runtime/include/wasm_stack.h index 95d5ac3a9..b471f5e28 100644 --- a/runtime/include/wasm_stack.h +++ b/runtime/include/wasm_stack.h @@ -28,6 +28,7 @@ struct wasm_stack { uint8_t *high; /* The highest address of the stack. Grows down from here */ uint8_t *low; /* The address of the lowest useabe address. Above guard page */ uint8_t *buffer; /* Points base address of backing heap allocation (Guard Page) */ + uint8_t *sp; /* Stack pointer */ }; static struct wasm_stack *wasm_stack_alloc(uint64_t capacity); @@ -60,6 +61,7 @@ wasm_stack_init(struct wasm_stack *wasm_stack, uint64_t capacity) wasm_stack->low = wasm_stack->buffer + /* guard page */ PAGE_SIZE; wasm_stack->capacity = capacity; wasm_stack->high = wasm_stack->low + capacity; + wasm_stack->sp = wasm_stack->low; /* Set the initial bytes to read / write */ rc = mprotect(wasm_stack->low, capacity, PROT_READ | PROT_WRITE); @@ -124,7 +126,6 @@ wasm_stack_reinit(struct wasm_stack *wasm_stack) assert(wasm_stack->buffer != NULL); assert(wasm_stack->low == wasm_stack->buffer + /* guard page */ PAGE_SIZE); assert(wasm_stack->high == wasm_stack->low + wasm_stack->capacity); - - explicit_bzero(wasm_stack->low, wasm_stack->capacity); + //explicit_bzero(wasm_stack->sp, wasm_stack->high - wasm_stack->sp); ps_list_init_d(wasm_stack); } diff --git a/runtime/include/worker_thread.h b/runtime/include/worker_thread.h index c006f3812..de435ab70 100644 --- a/runtime/include/worker_thread.h +++ b/runtime/include/worker_thread.h @@ -5,6 +5,6 @@ #include "runtime.h" extern thread_local struct arch_context worker_thread_base_context; -extern thread_local int worker_thread_idx; +extern thread_local int global_worker_thread_idx; void *worker_thread_main(void *return_code); diff --git a/runtime/increase_req_type.patch b/runtime/increase_req_type.patch new file mode 100644 index 000000000..481dfa2ac --- /dev/null +++ b/runtime/increase_req_type.patch @@ -0,0 +1,433 @@ +diff --git a/runtime/include/erpc_handler.h b/runtime/include/erpc_handler.h +index eefaf13..47324f8 100644 +--- a/runtime/include/erpc_handler.h ++++ b/runtime/include/erpc_handler.h +@@ -1,10 +1,10 @@ + #pragma once + + #include +-void edf_interrupt_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); ++void edf_interrupt_req_handler(void *req_handle, uint16_t req_type, uint8_t *msg, size_t size, uint16_t port); + void darc_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); + void shinjuku_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +-void enqueue_to_global_queue_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); ++void enqueue_to_global_queue_req_handler(void *req_handle, uint16_t req_type, uint8_t *msg, size_t size, uint16_t port); + void rr_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); + void jsq_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); + void lld_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port); +diff --git a/runtime/include/http_router.h b/runtime/include/http_router.h +index cce8883..e0da7ee 100644 +--- a/runtime/include/http_router.h ++++ b/runtime/include/http_router.h +@@ -118,7 +118,7 @@ http_router_match_route(http_router_t *router, char *route) + } + + static inline struct route * +-http_router_match_request_type(http_router_t *router, uint8_t request_type) ++http_router_match_request_type(http_router_t *router, uint16_t request_type) + { + /* + for (int i = 0; i < router->length; i++) { +diff --git a/runtime/include/json_parse.h b/runtime/include/json_parse.h +index c99aa2a..4c5b3c4 100644 +--- a/runtime/include/json_parse.h ++++ b/runtime/include/json_parse.h +@@ -7,7 +7,7 @@ + + #include "tenant_config_parse.h" + +-#define JSON_TOKENS_CAPACITY 16384 ++#define JSON_TOKENS_CAPACITY 65535 + + /** + * Parses a JSON file into an array of tenant configs +diff --git a/runtime/include/module_database.h b/runtime/include/module_database.h +index 24d1b46..32902dc 100644 +--- a/runtime/include/module_database.h ++++ b/runtime/include/module_database.h +@@ -2,7 +2,7 @@ + + #include "module.h" + +-#define MODULE_DATABASE_CAPACITY 1024 ++#define MODULE_DATABASE_CAPACITY 20000 + + struct module_database { + struct module *modules[MODULE_DATABASE_CAPACITY]; +diff --git a/runtime/include/route.h b/runtime/include/route.h +index b239d75..9afaafa 100644 +--- a/runtime/include/route.h ++++ b/runtime/include/route.h +@@ -11,7 +11,7 @@ + /* Assumption: entrypoint is always _start. This should be enhanced later */ + struct route { + char *route; +- uint8_t request_type; ++ uint16_t request_type; + struct http_route_total metrics; + struct module *module; + /* HTTP State */ +diff --git a/runtime/include/route_config.h b/runtime/include/route_config.h +index 66e3c29..d25f5e9 100644 +--- a/runtime/include/route_config.h ++++ b/runtime/include/route_config.h +@@ -27,7 +27,7 @@ extern int groups[route_config_member_len]; + + struct route_config { + char *route; +- uint8_t request_type; ++ uint16_t request_type; + uint32_t n_resas; + uint32_t group_id; + char *path; +@@ -53,8 +53,7 @@ static inline void + route_config_print(struct route_config *config) + { + printf("[Route] Route: %s\n", config->route); +- printf("[Route] Request type: %hhu\n", config->request_type); +- printf("[Route] Request type: %hhu\n", config->request_type); ++ printf("[Route] Request type: %u\n", config->request_type); + printf("[Route] Path: %s\n", config->path); + printf("[Route] Admissions Percentile: %hhu\n", config->admissions_percentile); + printf("[Route] Expected Execution (us): %u\n", config->expected_execution_us); +diff --git a/runtime/include/route_config_parse.h b/runtime/include/route_config_parse.h +index e22dd52..662a13a 100644 +--- a/runtime/include/route_config_parse.h ++++ b/runtime/include/route_config_parse.h +@@ -64,7 +64,7 @@ route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t + if (route_config_set_key_once(did_set, route_config_member_request_type) == -1) + return -1; + +- int rc = parse_uint8_t(tokens[i], json_buf, ++ int rc = parse_uint16_t(tokens[i], json_buf, + route_config_json_keys[route_config_member_request_type], + &config->request_type); + if (rc < 0) return -1; +diff --git a/runtime/include/tenant.h b/runtime/include/tenant.h +index 113fdca..e33aacc 100644 +--- a/runtime/include/tenant.h ++++ b/runtime/include/tenant.h +@@ -38,6 +38,7 @@ struct tenant { + uint16_t port; + struct route_config *routes_config; + size_t routes_len; ++ size_t route_replicas; + struct tcp_server tcp_server; + http_router_t router; + struct module_database module_db; +diff --git a/runtime/include/tenant_config.h b/runtime/include/tenant_config.h +index d907718..8bedede 100644 +--- a/runtime/include/tenant_config.h ++++ b/runtime/include/tenant_config.h +@@ -14,6 +14,7 @@ enum tenant_config_member + tenant_config_member_port, + tenant_config_member_replenishment_period_us, + tenant_config_member_max_budget_us, ++ tenant_config_member_route_replicas, + tenant_config_member_routes, + tenant_config_member_len + }; +@@ -23,6 +24,7 @@ struct tenant_config { + uint16_t port; + uint32_t replenishment_period_us; + uint32_t max_budget_us; ++ uint32_t route_replicas; + struct route_config *routes; + size_t routes_len; + }; +@@ -37,6 +39,7 @@ tenant_config_deinit(struct tenant_config *config) + for (int i = 0; i < config->routes_len; i++) { route_config_deinit(&config->routes[i]); } + free(config->routes); + config->routes = NULL; ++ config->route_replicas = 0; + config->routes_len = 0; + } + +@@ -47,6 +50,7 @@ tenant_config_print(struct tenant_config *config) + printf("[Tenant] Path: %d\n", config->port); + printf("[Tenant] Replenishment Period (us): %u\n", config->replenishment_period_us); + printf("[Tenant] Max Budget (us): %u\n", config->max_budget_us); ++ printf("[Tenant] Route replicas (us): %u\n", config->route_replicas); + printf("[Tenant] Routes Size: %zu\n", config->routes_len); + for (int i = 0; i < config->routes_len; i++) { route_config_print(&config->routes[i]); } + } +diff --git a/runtime/include/tenant_config_parse.h b/runtime/include/tenant_config_parse.h +index 14b7aef..6515015 100644 +--- a/runtime/include/tenant_config_parse.h ++++ b/runtime/include/tenant_config_parse.h +@@ -10,7 +10,7 @@ + #include "tenant_config.h" + + static const char *tenant_config_json_keys[tenant_config_member_len] = { "name", "port", "replenishment-period-us", +- "max-budget-us", "routes" }; ++ "max-budget-us", "route-replicas", "routes" }; + + static inline int + tenant_config_set_key_once(bool *did_set, enum tenant_config_member member) +@@ -79,7 +79,15 @@ tenant_config_parse(struct tenant_config *config, const char *json_buf, jsmntok_ + tenant_config_json_keys[tenant_config_member_max_budget_us], + &config->max_budget_us); + if (rc < 0) return -1; +- } else if (strcmp(key, tenant_config_json_keys[tenant_config_member_routes]) == 0) { ++ } else if (strcmp(key, tenant_config_json_keys[tenant_config_member_route_replicas]) == 0) { ++ if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; ++ if (tenant_config_set_key_once(did_set, tenant_config_member_route_replicas) == -1) return -1; ++ ++ int rc = parse_uint32_t(tokens[i], json_buf, ++ tenant_config_json_keys[tenant_config_member_route_replicas], ++ &config->route_replicas); ++ if (rc < 0) return -1; ++ } else if (strcmp(key, tenant_config_json_keys[tenant_config_member_routes]) == 0) { + if (!has_valid_type(tokens[i], key, JSMN_ARRAY, json_buf)) return -1; + if (tenant_config_set_key_once(did_set, tenant_config_member_routes) == -1) return -1; + +diff --git a/runtime/include/tenant_functions.h b/runtime/include/tenant_functions.h +index 65cb23e..f1c4c66 100644 +--- a/runtime/include/tenant_functions.h ++++ b/runtime/include/tenant_functions.h +@@ -21,7 +21,7 @@ extern thread_local uint32_t n_rtypes; + extern thread_local struct request_typed_queue *request_type_queue[MAX_REQUEST_TYPE]; + extern thread_local struct request_typed_deque *request_type_deque[MAX_REQUEST_TYPE]; + extern thread_local uint8_t dispatcher_thread_idx; +-extern thread_local struct perf_window perf_window_per_thread[1024]; ++extern thread_local struct perf_window *perf_window_per_thread; + extern thread_local int global_worker_thread_idx; + + int tenant_database_add(struct tenant *tenant); +@@ -84,6 +84,105 @@ tenant_policy_specific_init(struct tenant *tenant, struct tenant_config *config) + return 0; + } + ++static inline char* ++generate_new_path(const char* full_name, int number, const char* fixed_part) { ++ const char* suffix_position = strstr(full_name, fixed_part); ++ if (!suffix_position) { ++ panic("Wrong path name"); ++ } ++ ++ size_t name_length = suffix_position - full_name; ++ ++ char number_str[6]; ++ snprintf(number_str, sizeof(number_str), "%d", number); ++ size_t number_length = strlen(number_str); ++ ++ size_t new_length = name_length + number_length + strlen(fixed_part) + 1; ++ char* new_name = (char*)malloc(new_length); ++ if (!new_name) { ++ panic("Failed to allocate memory for path"); ++ } ++ ++ strncpy(new_name, full_name, name_length); ++ new_name[name_length] = '\0'; ++ ++ strcat(new_name, number_str); ++ strcat(new_name, fixed_part); ++ ++ return new_name; ++} ++ ++static inline struct route_config * ++duplicate_route(int index, struct route_config *src_route) { ++ struct route_config *dup_route = (struct route_config *)malloc(sizeof(struct route_config)); ++ ++ // Allocate memory for route with additional space for index ++ size_t route_len = strlen(src_route->route) + 6; // 20 is enough to hold the index as string ++ dup_route->route = (char *)malloc(route_len); ++ strcpy(dup_route->route, src_route->route); ++ sprintf(dup_route->route + strlen(dup_route->route), "%d", index); ++ ++ // Allocate memory for path with additional space for index ++ dup_route->path = generate_new_path(src_route->path, index, ".wasm.so"); ++ ++ // Copy other fields ++ dup_route->request_type = index; ++ dup_route->n_resas = src_route->n_resas; ++ dup_route->admissions_percentile = src_route->admissions_percentile; ++ dup_route->expected_execution_us = src_route->expected_execution_us; ++ dup_route->expected_execution_cycle = src_route->expected_execution_cycle; ++ dup_route->relative_deadline_us = src_route->relative_deadline_us; ++ ++ if (src_route->http_resp_content_type != NULL) { ++ dup_route->http_resp_content_type = strdup(src_route->http_resp_content_type); ++ } else { ++ dup_route->http_resp_content_type = NULL; ++ } ++ ++ return dup_route; ++} ++ ++static inline void ++create_replica_routes(struct tenant *tenant, struct tenant_config *config) ++{ ++ for (int i = 0; i < config->routes_len; i++) { ++ struct module *module = module_database_find_by_path(&tenant->module_db, config->routes[i].path); ++ if (module == NULL) { ++ for (int j = 1; j <= config->route_replicas; j++) { ++ struct route_config *route_cfg = duplicate_route(j, &config->routes[i]); ++ //printf("name %s type %d route %s path %s\n", config->name, route_cfg->request_type, route_cfg->route, route_cfg->path); ++ assert(module_database_find_by_path(&tenant->module_db, route_cfg->path) == NULL); ++ ++ /* Ownership of path moves here */ ++ struct module * new_module = module_alloc(route_cfg->path); ++ ++ if (new_module != NULL) { ++ module_database_add(&tenant->module_db, new_module); ++ route_cfg->path = NULL; ++ } ++ /* Ownership of config's route and http_resp_content_type move here */ ++ int rc = http_router_add_route(&tenant->router, route_cfg, new_module); ++ if (unlikely(rc != 0)) { ++ panic("Tenant %s defined %lu routes, but router failed to grow beyond %lu\n", tenant->name, ++ config->routes_len, tenant->router.capacity); ++ } ++ ++ route_cfg->route = NULL; ++ route_cfg->http_resp_content_type = NULL; ++ } ++ } else { ++ panic("No allowed duplicate route path when route_replicas is larger then 1\n"); ++ } ++ ++ free(config->routes[i].path); ++ config->routes[i].path = NULL; ++ free(config->routes[i].http_resp_content_type); ++ config->routes[i].http_resp_content_type = NULL; ++ free(config->routes[i].route); ++ config->routes[i].route = NULL; ++ } ++} ++ + static inline struct tenant * + tenant_alloc(struct tenant_config *config) + { +@@ -94,26 +193,36 @@ tenant_alloc(struct tenant_config *config) + struct tenant *tenant = (struct tenant *)calloc(1, sizeof(struct tenant)); + + /* Move name */ +- tenant->tag = EPOLL_TAG_TENANT_SERVER_SOCKET; +- tenant->name = config->name; +- tenant->port = config->port; +- tenant->routes_config = config->routes; +- tenant->routes_len = config->routes_len; +- config->name = NULL; ++ tenant->tag = EPOLL_TAG_TENANT_SERVER_SOCKET; ++ tenant->name = config->name; ++ tenant->port = config->port; ++ tenant->routes_config = config->routes; ++ tenant->routes_len = config->routes_len; ++ tenant->route_replicas = config->route_replicas; ++ config->name = NULL; + + tcp_server_init(&tenant->tcp_server, config->port); +- http_router_init(&tenant->router, config->routes_len); ++ if (config->route_replicas > 1) { ++ http_router_init(&tenant->router, config->route_replicas); ++ } else { ++ http_router_init(&tenant->router, config->routes_len); ++ } ++ + module_database_init(&tenant->module_db); + map_init(&tenant->scratch_storage); + + /* Deferrable Server init */ + tenant_policy_specific_init(tenant, config); + +- for (int i = 0; i < config->routes_len; i++) { ++ if (config->route_replicas > 1) { ++ create_replica_routes(tenant, config); ++ } else { ++ for (int i = 0; i < config->routes_len; i++) { + struct module *module = module_database_find_by_path(&tenant->module_db, config->routes[i].path); + if (module == NULL) { + /* Ownership of path moves here */ + module = module_alloc(config->routes[i].path); ++ //printf("name %s type %d route %s path %s\n", config->name, config->routes[i].request_type, config->routes[i].route, config->routes[i].path); + if (module != NULL) { + module_database_add(&tenant->module_db, module); + config->routes[i].path = NULL; +@@ -134,8 +243,9 @@ tenant_alloc(struct tenant_config *config) + + config->routes[i].route = NULL; + config->routes[i].http_resp_content_type = NULL; +- } ++ } + ++ } + return tenant; + } + +diff --git a/runtime/src/admissions_info.c b/runtime/src/admissions_info.c +index 711db96..443b227 100644 +--- a/runtime/src/admissions_info.c ++++ b/runtime/src/admissions_info.c +@@ -3,7 +3,7 @@ + #include "debuglog.h" + #include "perf_window.h" + +-extern thread_local struct perf_window perf_window_per_thread[1024]; ++extern thread_local struct perf_window *perf_window_per_thread; + /** + * Initializes perf window + * @param admissions_info +diff --git a/runtime/src/listener_thread.c b/runtime/src/listener_thread.c +index ad40b6a..9a00119 100644 +--- a/runtime/src/listener_thread.c ++++ b/runtime/src/listener_thread.c +@@ -474,7 +474,7 @@ on_client_socket_epoll_event(struct epoll_event *evt) + * @param msg the payload of the rpc request. It is the input parameter fot the function + * @param size the size of the msg + */ +-void edf_interrupt_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { ++void edf_interrupt_req_handler(void *req_handle, uint16_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); +@@ -1547,7 +1547,7 @@ void shinjuku_dispatch() { + } + } + +-void enqueue_to_global_queue_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { ++void enqueue_to_global_queue_req_handler(void *req_handle, uint16_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); +diff --git a/runtime/src/worker_thread.c b/runtime/src/worker_thread.c +index 70a919e..478f8df 100644 +--- a/runtime/src/worker_thread.c ++++ b/runtime/src/worker_thread.c +@@ -24,16 +24,17 @@ + * Worker Thread State * + **************************/ + +-extern pthread_mutex_t mutexs[1024]; +-extern pthread_cond_t conds[1024]; +-extern sem_t semlock[1024]; +- +-_Atomic uint32_t local_queue_length[1024] = {0}; +-uint32_t max_local_queue_length[1024] = {0}; +-uint32_t max_local_queue_height[1024] = {0}; +-extern struct perf_window * worker_perf_windows[1024]; // index is worker id +-thread_local struct perf_window perf_window_per_thread[1024]; // index is route unique id +-struct sandbox* current_sandboxes[1024] = { NULL }; ++extern pthread_mutex_t mutexs[1024]; //maximum index is number of threads ++extern pthread_cond_t conds[1024]; //maximum index is number of threads ++extern sem_t semlock[1024]; //maximum index is number of threads ++ ++_Atomic uint32_t local_queue_length[1024] = {0}; //maximum index is number of threads ++uint32_t max_local_queue_length[1024] = {0}; //maximum index is number of threads ++uint32_t max_local_queue_height[1024] = {0}; //maximum index is number of threads ++extern struct perf_window * worker_perf_windows[1024]; //index is worker id. maximum index is the number of threads ++//thread_local struct perf_window perf_window_per_thread[1024]; // index is route unique id ++thread_local struct perf_window *perf_window_per_thread = NULL; //index is route unique id, maximum index is the number of modules ++struct sandbox* current_sandboxes[1024] = { NULL }; //maximum index is number of threads + extern uint32_t runtime_worker_group_size; + + extern FILE *sandbox_perf_log; +@@ -102,6 +103,7 @@ worker_thread_main(void *argument) + // runtime_set_pthread_prio(pthread_self(), 2); + pthread_setschedprio(pthread_self(), -20); + ++ perf_window_per_thread = (struct perf_window*) malloc(sizeof(struct perf_window) * MODULE_DATABASE_CAPACITY); + preallocate_memory(); + perf_window_init(); + condition_variable_init(); diff --git a/runtime/src/admissions_info.c b/runtime/src/admissions_info.c index 540a24d05..711db9614 100644 --- a/runtime/src/admissions_info.c +++ b/runtime/src/admissions_info.c @@ -3,17 +3,19 @@ #include "debuglog.h" #include "perf_window.h" +extern thread_local struct perf_window perf_window_per_thread[1024]; /** * Initializes perf window * @param admissions_info */ void -admissions_info_initialize(struct admissions_info *admissions_info, uint8_t percentile, uint64_t expected_execution, +admissions_info_initialize(struct admissions_info *admissions_info, uint8_t uid, uint8_t percentile, uint64_t expected_execution, uint64_t relative_deadline) { -#ifdef ADMISSIONS_CONTROL +//#ifdef ADMISSIONS_CONTROL assert(relative_deadline > 0); assert(expected_execution > 0); + admissions_info->uid = uid; admissions_info->relative_deadline = relative_deadline; admissions_info->estimate = admissions_control_calculate_estimate(expected_execution, relative_deadline); debuglog("Initial Estimate: %lu\n", admissions_info->estimate); @@ -29,9 +31,13 @@ admissions_info_initialize(struct admissions_info *admissions_info, uint8_t perc debuglog("Percentile: %u\n", admissions_info->percentile); debuglog("Control Index: %d\n", admissions_info->control_index); #endif -#endif +//#endif } +void perf_window_per_thread_update(struct admissions_info *admissions_info, uint64_t execution_duration) { + uint32_t uid = admissions_info->uid; + perf_window_add(&perf_window_per_thread[uid], execution_duration); +} /* * Adds an execution value to the perf window and calculates and caches and updated estimate diff --git a/runtime/src/arch_context.c b/runtime/src/arch_context.c index c359927d6..cdc6db11b 100644 --- a/runtime/src/arch_context.c +++ b/runtime/src/arch_context.c @@ -14,6 +14,7 @@ */ noreturn void __attribute__((noinline)) arch_context_restore_preempted(void) { - pthread_kill(pthread_self(), SIGUSR1); + pthread_t tid = pthread_self(); + pthread_kill(tid, SIGUSR1); panic("Unexpectedly reached code after sending self SIGUSR1\n"); } diff --git a/runtime/src/current_sandbox.c b/runtime/src/current_sandbox.c index feaf6702d..75061dc04 100644 --- a/runtime/src/current_sandbox.c +++ b/runtime/src/current_sandbox.c @@ -54,15 +54,24 @@ current_sandbox_exit() switch (exiting_sandbox->state) { case SANDBOX_RETURNED: sandbox_exit_success(exiting_sandbox); + sandbox_send_response(exiting_sandbox, 0); break; case SANDBOX_RUNNING_SYS: + sandbox_send_response(exiting_sandbox, 1); sandbox_exit_error(exiting_sandbox); break; default: panic("Cooperatively switching from a sandbox in a non-terminal %s state\n", sandbox_state_stringify(exiting_sandbox->state)); } - + //---------xiaosu----------------- + /*char str[4096] = {0}; + sprintf(str, "id %lu ", exiting_sandbox->id); + for (int i = 0; i < exiting_sandbox->state_history.size; i++) { + sprintf(str + strlen(str), "sate %d ", exiting_sandbox->state_history.buffer[i]); + } + printf("%s\n", str);*/ + //-----------xiaosu--------------- scheduler_cooperative_sched(true); /* The scheduler should never switch back to completed sandboxes */ @@ -127,18 +136,16 @@ current_sandbox_init() /* Initialize Arguments. First arg is the module name. Subsequent args are query parameters */ char *args[HTTP_MAX_QUERY_PARAM_COUNT + 1]; args[0] = sandbox->module->path; - for (int i = 0; i < sandbox->http->http_request.query_params_count; i++) - args[i + 1] = (char *)sandbox->http->http_request.query_params[i].value; - options.argc = sandbox->http->http_request.query_params_count + 1; + options.argc = 1; options.argv = (const char **)&args; sandbox->wasi_context = wasi_context_init(&options); sledge_abi__current_wasm_module_instance.wasi_context = sandbox->wasi_context; assert(sandbox->wasi_context != NULL); sandbox_return(sandbox); - - /* Initialize sandbox globals. Needs to run in user state */ + + /* Initialize sandbox globals. Needs to run in user state */ module_initialize_globals(current_module); return sandbox; @@ -160,7 +167,9 @@ current_sandbox_fini() sandbox->timestamp_of.completion = __getcycles(); sandbox->total_time = sandbox->timestamp_of.completion - sandbox->timestamp_of.allocation; - + //-----------xiaosu----------------------- + //printf("id %lu finish, total time %lu, current ts %lu\n", sandbox->id, sandbox->total_time, sandbox->timestamp_of.completion); + //----------xiaosu------------------------- assert(sandbox->state == SANDBOX_RUNNING_SYS); done: @@ -189,6 +198,8 @@ current_sandbox_start(void) int rc = sigsetjmp(sandbox->ctxt.start_buf, 1); if (rc == 0) { struct module *current_module = sandbox_get_module(sandbox); + /* Sandbox starts running, update its RS */ + sandbox->srsf_remaining_slack = sandbox->srsf_remaining_slack - (__getcycles() - sandbox->srsf_stop_running_ts); sandbox->return_value = module_entrypoint(current_module); } else { current_sandbox_wasm_trap_handler(rc); diff --git a/runtime/src/dispatcher.c b/runtime/src/dispatcher.c new file mode 100644 index 000000000..027fcf65c --- /dev/null +++ b/runtime/src/dispatcher.c @@ -0,0 +1,4 @@ +#include "dispatcher.h" + +enum DISPATCHER dispatcher = DISPATCHER_EDF_INTERRUPT; + diff --git a/runtime/src/global_request_scheduler_deque.c b/runtime/src/global_request_scheduler_deque.c index af151b47f..f40280e86 100644 --- a/runtime/src/global_request_scheduler_deque.c +++ b/runtime/src/global_request_scheduler_deque.c @@ -3,7 +3,6 @@ #include "runtime.h" #define GLOBAL_REQUEST_SCHEDULER_DEQUE_CAPACITY (1 << 19) - static struct deque_sandbox *global_request_scheduler_deque; /* TODO: Should this be used??? */ @@ -19,7 +18,14 @@ global_request_scheduler_deque_add(struct sandbox *sandbox) { int return_code = 1; + /* For mulitiple threads calling deque_push_sandbox(), use spin-lock instead of mutex-lock + * to reduce context-switch overhead. + * For single thread calling deque_push_sandbox(), avoid any lock since no race condition + */ + lock_node_t node = {}; + lock_lock(&global_request_scheduler_deque->lock, &node); return_code = deque_push_sandbox(global_request_scheduler_deque, &sandbox); + lock_unlock(&global_request_scheduler_deque->lock, &node); if (return_code != 0) return NULL; return sandbox; @@ -56,6 +62,7 @@ global_request_scheduler_deque_initialize() /* Note: Below is a Macro */ deque_init_sandbox(global_request_scheduler_deque, GLOBAL_REQUEST_SCHEDULER_DEQUE_CAPACITY); + lock_init(&global_request_scheduler_deque->lock); /* Register Function Pointers for Abstract Scheduling API */ struct global_request_scheduler_config config = { .add_fn = global_request_scheduler_deque_add, diff --git a/runtime/src/global_request_scheduler_minheap.c b/runtime/src/global_request_scheduler_minheap.c index cee34f9ce..cdafecabf 100644 --- a/runtime/src/global_request_scheduler_minheap.c +++ b/runtime/src/global_request_scheduler_minheap.c @@ -19,7 +19,7 @@ global_request_scheduler_minheap_add(struct sandbox *sandbox) { assert(sandbox); assert(global_request_scheduler_minheap); - if (unlikely(!listener_thread_is_running())) panic("%s is only callable by the listener thread\n", __func__); + //if (unlikely(!listener_thread_is_running())) panic("%s is only callable by the listener thread\n", __func__); int return_code = priority_queue_enqueue(global_request_scheduler_minheap, sandbox); diff --git a/runtime/src/http_router.c b/runtime/src/http_router.c new file mode 100644 index 000000000..be04584dd --- /dev/null +++ b/runtime/src/http_router.c @@ -0,0 +1,3 @@ +#include + +struct route *route_array = NULL; diff --git a/runtime/src/libc/wasi_impl_serverless.c b/runtime/src/libc/wasi_impl_serverless.c index d53af20ba..8e3081fa0 100644 --- a/runtime/src/libc/wasi_impl_serverless.c +++ b/runtime/src/libc/wasi_impl_serverless.c @@ -113,7 +113,7 @@ wasi_context_init(wasi_options_t *options) } /* Seed Random */ - srandom(time(NULL)); + //srandom(time(NULL)); /* TODO: Preopens */ @@ -659,23 +659,21 @@ wasi_snapshot_preview1_backing_fd_read(wasi_context_t *context, __wasi_fd_t fd, /* Non-blocking copy on stdin */ if (fd == STDIN_FILENO) { struct sandbox *current_sandbox = current_sandbox_get(); - struct http_request *current_request = ¤t_sandbox->http->http_request; - int old_read = current_request->cursor; - int bytes_to_read = current_request->body_length - old_read; - assert(current_request->body_length >= 0); + int old_read = current_sandbox->cursor; + int bytes_to_read = current_sandbox->rpc_request_body_size - old_read; for (int i = 0; i < iovs_len; i++) { if (bytes_to_read == 0) goto done; int amount_to_copy = iovs[i].buf_len > bytes_to_read ? bytes_to_read : iovs[i].buf_len; - memcpy(iovs[i].buf, current_request->body + current_request->cursor, amount_to_copy); - current_request->cursor += amount_to_copy; - bytes_to_read = current_request->body_length - current_request->cursor; + memcpy(iovs[i].buf, current_sandbox->rpc_request_body + current_sandbox->cursor, amount_to_copy); + current_sandbox->cursor += amount_to_copy; + bytes_to_read = current_sandbox->rpc_request_body_size - current_sandbox->cursor; } done: - *nwritten_retptr = current_request->cursor - old_read; + *nwritten_retptr = current_sandbox->cursor - old_read; return __WASI_ERRNO_SUCCESS; } @@ -796,7 +794,7 @@ wasi_snapshot_preview1_backing_fd_write(wasi_context_t *context, __wasi_fd_t fd, debuglog("STDERR from Sandbox: %.*s", iovs[i].buf_len, iovs[i].buf); } #endif - rc = fwrite(iovs[i].buf, 1, iovs[i].buf_len, s->http->response_body.handle); + rc = fwrite(iovs[i].buf, 1, iovs[i].buf_len, s->response_body.handle); if (rc != iovs[i].buf_len) return __WASI_ERRNO_FBIG; nwritten += rc; diff --git a/runtime/src/listener_thread.c b/runtime/src/listener_thread.c index 7856016f9..ad40b6aaf 100644 --- a/runtime/src/listener_thread.c +++ b/runtime/src/listener_thread.c @@ -1,6 +1,8 @@ #include #include +#include "erpc_handler.h" +#include "erpc_c_interface.h" #include "arch/getcycles.h" #include "global_request_scheduler.h" #include "listener_thread.h" @@ -12,7 +14,69 @@ #include "tenant.h" #include "tenant_functions.h" #include "http_session_perf_log.h" - +#include "sandbox_set_as_runnable.h" +#include "request_typed_queue.h" +#include "request_typed_deque.h" +#include "local_preempted_fifo_queue.h" + +#define INTERRUPT_INTERVAL 15 +#define BASE_SERVICE_TIME 10 +#define LOSS_PERCENTAGE 0.11 + +thread_local int current_active_workers = 1; +thread_local int next_loop_start_index = -1; +thread_local uint64_t total_requests = 0; +uint64_t base_simulated_service_time = 0; +uint64_t shinjuku_interrupt_interval = 0; +thread_local uint32_t global_queue_length = 0; +thread_local uint32_t max_queue_length = 0; + +pthread_mutex_t mutexs[1024]; +pthread_cond_t conds[1024]; +sem_t semlock[1024]; + +uint32_t worker_old_sandbox[1024] = {0}; +uint32_t worker_new_sandbox[1024] = {0}; +struct perf_window * worker_perf_windows[1024]; +struct priority_queue * worker_queues[1024]; +struct binary_tree * worker_binary_trees[1024]; +struct ps_list_head * worker_fifo_queue[1024]; +struct request_fifo_queue * worker_circular_queue[1024]; +struct request_fifo_queue * worker_preempted_queue[1024]; + +extern pthread_t *runtime_worker_threads; +extern uint64_t wakeup_thread_cycles; +extern bool runtime_autoscaling_enabled; +extern FILE *sandbox_perf_log; +extern bool runtime_exponential_service_time_simulation_enabled; +extern _Atomic uint64_t worker_queuing_cost[1024]; +//struct sandbox *urgent_request[1024] = { NULL }; +extern uint32_t runtime_worker_threads_count; +extern thread_local bool pthread_stop; +extern _Atomic uint64_t request_index; +extern uint32_t runtime_worker_group_size; +extern struct sandbox* current_sandboxes[1024]; + +thread_local uint32_t rr_index = 0; +thread_local uint32_t current_reserved = 0; +thread_local uint32_t dispatcher_try_interrupts = 0; +thread_local uint32_t worker_start_id; +thread_local uint32_t worker_end_id; +uint64_t requests_counter[MAX_DISPATCHER][MAX_REQUEST_TYPE] = {0}; +thread_local uint32_t worker_list[MAX_WORKERS]; // record the worker's true id(0 - N workers - 1). worker_list[0] - worker_list[2] +_Atomic uint32_t free_workers[MAX_DISPATCHER] = {0}; // the index is the dispatercher id, free_workers[dispatcher_id] + // is decimal value of the bitmap of avaliable workers. + // For example, if there are 3 workers available, the bitmap + // will be 111, then the free_workers[dispatcher_id] is 7 + +thread_local struct request_typed_queue *request_type_queue[MAX_REQUEST_TYPE]; // key is group ID +thread_local struct request_typed_deque *request_type_deque[MAX_REQUEST_TYPE]; + +thread_local uint32_t n_rtypes = 0; + +time_t t_start; +extern bool first_request_comming; +extern pthread_t *runtime_listener_threads; static void listener_thread_unregister_http_session(struct http_session *http); static void panic_on_epoll_error(struct epoll_event *evt); @@ -31,30 +95,44 @@ static void on_client_response_sent(struct http_session *session); */ int listener_thread_epoll_file_descriptor; -pthread_t listener_thread_id; +thread_local uint8_t dispatcher_thread_idx; +thread_local pthread_t listener_thread_id; +thread_local bool is_listener = false; + + +void typed_queue_init() { + tenant_database_foreach(tenant_request_typed_queue_init, NULL, NULL); +} /** * Initializes the listener thread, pinned to core 0, and starts to listen for requests */ void -listener_thread_initialize(void) +listener_thread_initialize(uint8_t thread_id) { printf("Starting listener thread\n"); + cpu_set_t cs; CPU_ZERO(&cs); - CPU_SET(LISTENER_THREAD_CORE_ID, &cs); + CPU_SET(LISTENER_THREAD_START_CORE_ID + thread_id, &cs); + printf("cpu affinity core for listener is %d\n", LISTENER_THREAD_START_CORE_ID + thread_id); /* Setup epoll */ listener_thread_epoll_file_descriptor = epoll_create1(0); assert(listener_thread_epoll_file_descriptor >= 0); - int ret = pthread_create(&listener_thread_id, NULL, listener_thread_main, NULL); + /* Pass the value we want the threads to use when indexing into global arrays of per-thread values */ + runtime_listener_threads_argument[thread_id] = thread_id; + + int ret = pthread_create(&runtime_listener_threads[thread_id], NULL, listener_thread_main, (void *)&runtime_listener_threads_argument[thread_id]); + /* Sleep 1 second to wait for listener_thread_main start up and set DPDK control threads cpu affinity */ + sleep(1); + listener_thread_id = runtime_listener_threads[thread_id]; assert(ret == 0); + /* Set listener thread to a different cpu affinity to seperate from DPDK control threads */ ret = pthread_setaffinity_np(listener_thread_id, sizeof(cpu_set_t), &cs); assert(ret == 0); - ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs); - assert(ret == 0); printf("\tListener core thread: %lx\n", listener_thread_id); } @@ -242,7 +320,7 @@ on_client_request_received(struct http_session *session) /* Allocate a Sandbox */ session->state = HTTP_SESSION_EXECUTING; - struct sandbox *sandbox = sandbox_alloc(route->module, session, route, session->tenant, work_admitted); + struct sandbox *sandbox = sandbox_alloc(route->module, session, route, session->tenant, work_admitted, NULL, 0); if (unlikely(sandbox == NULL)) { debuglog("Failed to allocate sandbox\n"); session->state = HTTP_SESSION_EXECUTION_COMPLETE; @@ -254,7 +332,8 @@ on_client_request_received(struct http_session *session) /* If the global request scheduler is full, return a 429 to the client */ if (unlikely(global_request_scheduler_add(sandbox) == NULL)) { debuglog("Failed to add sandbox to global queue\n"); - sandbox_free(sandbox); + uint64_t ret[5] = {0}; + sandbox_free(sandbox, ret); session->state = HTTP_SESSION_EXECUTION_COMPLETE; http_session_set_response_header(session, 429); on_client_response_header_sending(session); @@ -388,6 +467,1158 @@ on_client_socket_epoll_event(struct epoll_event *evt) } } +/** + * @brief Request routing function + * @param req_handle used by eRPC internal, it is used to send out the response packet + * @param req_type the type of the request. Each function has a unique reqest type id + * @param msg the payload of the rpc request. It is the input parameter fot the function + * @param size the size of the msg + */ +void edf_interrupt_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + + uint64_t min_waiting_serving_time = UINT64_MAX; + int thread_id = 0; /* This thread can server the request by waiting for a while */ + int candidate_thread_with_interrupt = -1; /* This thread can server the request immediately by interrupting + the current one */ + + next_loop_start_index++; + if (next_loop_start_index == current_active_workers) { + next_loop_start_index = 0; + } + + //uint64_t waiting_times[3] = {0}; + int violate_deadline_workers = 0; + for (uint32_t i = next_loop_start_index; i < next_loop_start_index + current_active_workers; ++i) { + int true_idx = i % current_active_workers; + bool need_interrupt; + uint64_t waiting_serving_time = local_runqueue_try_add_index(worker_list[true_idx], sandbox, &need_interrupt); + /*waiting_times[true_idx] = waiting_serving_time; + if (waiting_times[true_idx] != 0) { + mem_log("listener %d %d queue:\n", dispatcher_thread_idx, worker_list[true_idx]); + local_runqueue_print_in_order(worker_list[true_idx]); + }*/ + /* The local queue is empty, the worker is idle, can be served this request immediately + * without interrupting + */ + if (waiting_serving_time == 0 && need_interrupt == false) { + local_runqueue_add_index(worker_list[true_idx], sandbox); + //mem_log("listener %d %d is idle, choose it\n", dispatcher_thread_idx, worker_list[true_idx]); + return; + } else if (waiting_serving_time == 0 && need_interrupt == true) {//The worker can serve the request immediately + // by interrupting the current one + /* We already have a candidate worker, continue to find a + * better worker without needing interrupt or a worker that has the minimum total amount of work + */ + if (candidate_thread_with_interrupt != -1) { + if (worker_queuing_cost[worker_list[true_idx]] < worker_queuing_cost[candidate_thread_with_interrupt]) { + candidate_thread_with_interrupt = worker_list[true_idx]; + } + } else { + candidate_thread_with_interrupt = worker_list[true_idx]; + } + } else { + if (min_waiting_serving_time > waiting_serving_time) { + min_waiting_serving_time = waiting_serving_time; + thread_id = worker_list[true_idx]; + } + /* Check flag and if need to autoscale */ + //if (runtime_autoscaling_enabled && + // (min_waiting_serving_time + sandbox->estimated_cost + (wakeup_thread_cycles) >= sandbox->relative_deadline)) { + // violate_deadline_workers++; + //} + } + } + + /* If no any worker can meet the current request deadline, scale up */ + //if (runtime_autoscaling_enabled && current_active_workers < runtime_worker_group_size + // && violate_deadline_workers == current_active_workers) { + /* Add the current request to the scaling up worker */ + // local_runqueue_add_index(current_active_workers, sandbox); + // current_active_workers++; + // printf("current_active_workers %d\n", current_active_workers); + // return; + //} + + if (candidate_thread_with_interrupt != -1) { + //urgent_request[candidate_thread_with_interrupt] = sandbox; + local_runqueue_add_index(candidate_thread_with_interrupt, sandbox); + //mem_log("listener %d %d can be interrupted immediately for req deadline %lu\n", dispatcher_thread_idx, + // candidate_thread_with_interrupt, sandbox->absolute_deadline); + + preempt_worker(candidate_thread_with_interrupt); + dispatcher_try_interrupts++; + } else { + local_runqueue_add_index(thread_id, sandbox); + /*mem_log("listener %d %d has the min waiting time for req deadline %lu. 0:%lu 1:%lu 2:%lu\n", + dispatcher_thread_idx, thread_id, sandbox->absolute_deadline, waiting_times[0], waiting_times[1], waiting_times[2]); + local_runqueue_print_in_order(thread_id); + int fake_id = thread_id % runtime_worker_group_size; + for (int i = 0; i < runtime_worker_group_size; i++) { + if (fake_id != i) { + mem_log("listener %d %d queue:\n", dispatcher_thread_idx, worker_list[i]); + local_runqueue_print_in_order(worker_list[i]); + } + }*/ + } +} + +void jsq_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + + uint64_t min_queue_len = UINT64_MAX; + int thread_id = 0; /* This thread can server the request by waiting for a while */ + int candidate_thread_with_interrupt = -1; /* This thread can server the request immediately by interrupting + the current one */ + + next_loop_start_index++; + if (next_loop_start_index == current_active_workers) { + next_loop_start_index = 0; + } + + for (uint32_t i = next_loop_start_index; i < next_loop_start_index + current_active_workers; ++i) { + int true_idx = i % current_active_workers; + bool need_interrupt; + uint64_t waiting_queue_len = local_runqueue_add_and_get_length_index(worker_list[true_idx], sandbox, &need_interrupt); + /* The local queue is empty, the worker is idle, can be served this request immediately + * without interrupting + */ + if (waiting_queue_len == 0 && need_interrupt == false) { + local_runqueue_add_index(worker_list[true_idx], sandbox); + //mem_log("listener %d %d is idle, choose it\n", dispatcher_thread_idx, worker_list[true_idx]); + return; + } else if (waiting_queue_len == 0 && need_interrupt == true) {//The worker can serve the request immediately + // by interrupting the current one + /* We already have a candidate worker, continue to find a + * better worker without needing interrupt or a worker that has the minimum queue length + */ + + candidate_thread_with_interrupt = worker_list[true_idx]; + } else { + if (min_queue_len > waiting_queue_len) { + min_queue_len = waiting_queue_len; + thread_id = worker_list[true_idx]; + } + } + } + + if (candidate_thread_with_interrupt != -1) { + //urgent_request[candidate_thread_with_interrupt] = sandbox; + local_runqueue_add_index(candidate_thread_with_interrupt, sandbox); + //mem_log("listener %d %d can be interrupted immediately for req deadline %lu\n", dispatcher_thread_idx, + // candidate_thread_with_interrupt, sandbox->absolute_deadline); + + preempt_worker(candidate_thread_with_interrupt); + dispatcher_try_interrupts++; + } else { + local_runqueue_add_index(thread_id, sandbox); + } +} + +void lld_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + + uint64_t min_load = UINT64_MAX; + int thread_id = 0; /* This thread can server the request by waiting for a while */ + int candidate_thread_with_interrupt = -1; /* This thread can server the request immediately by interrupting + the current one */ + + next_loop_start_index++; + if (next_loop_start_index == current_active_workers) { + next_loop_start_index = 0; + } + + //uint64_t waiting_times[3] = {0}; + int violate_deadline_workers = 0; + for (uint32_t i = next_loop_start_index; i < next_loop_start_index + current_active_workers; ++i) { + int true_idx = i % current_active_workers; + bool need_interrupt; + uint64_t load = local_runqueue_add_and_get_load_index(worker_list[true_idx], sandbox, &need_interrupt); + /* The local queue is empty, the worker is idle, can be served this request immediately + * without interrupting + */ + if (load == 0 && need_interrupt == false) { + local_runqueue_add_index(worker_list[true_idx], sandbox); + //mem_log("listener %d %d is idle, choose it\n", dispatcher_thread_idx, worker_list[true_idx]); + return; + } else if (load == 0 && need_interrupt == true) {//The worker can serve the request immediately + // by interrupting the current one + /* We already have a candidate worker, continue to find a + * better worker without needing interrupt or a worker that has the minimum queue length + */ + + candidate_thread_with_interrupt = worker_list[true_idx]; + } else { + if (min_load > load) { + min_load = load; + thread_id = worker_list[true_idx]; + } + } + } + + if (candidate_thread_with_interrupt != -1) { + //urgent_request[candidate_thread_with_interrupt] = sandbox; + local_runqueue_add_index(candidate_thread_with_interrupt, sandbox); + //mem_log("listener %d %d can be interrupted immediately for req deadline %lu\n", dispatcher_thread_idx, + // candidate_thread_with_interrupt, sandbox->absolute_deadline); + + preempt_worker(candidate_thread_with_interrupt); + dispatcher_try_interrupts++; + } else { + local_runqueue_add_index(thread_id, sandbox); + } +} + +void rr_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + + int thread_id = 0; /* This thread can server the request by waiting for a while */ + int candidate_thread_with_interrupt = -1; /* This thread can server the request immediately by interrupting + the current one */ + + next_loop_start_index++; + if (next_loop_start_index == current_active_workers) { + next_loop_start_index = 0; + } + + for (uint32_t i = next_loop_start_index; i < next_loop_start_index + current_active_workers; ++i) { + int true_idx = i % current_active_workers; + bool need_interrupt; + uint64_t waiting_queue_len = local_runqueue_add_and_get_length_index(worker_list[true_idx], sandbox, &need_interrupt); + /* The local queue is empty, the worker is idle, can be served this request immediately + * without interrupting + */ + if (waiting_queue_len == 0 && need_interrupt == false) { + local_runqueue_add_index(worker_list[true_idx], sandbox); + //mem_log("listener %d %d is idle, choose it\n", dispatcher_thread_idx, worker_list[true_idx]); + return; + } else if (waiting_queue_len == 0 && need_interrupt == true) {//The worker can serve the request immediately + // by interrupting the current one + /* We already have a candidate worker, continue to find a + * better worker without needing interrupt or a worker that has the minimum queue length + */ + + candidate_thread_with_interrupt = worker_list[true_idx]; + } + } + + if (candidate_thread_with_interrupt != -1) { + //urgent_request[candidate_thread_with_interrupt] = sandbox; + local_runqueue_add_index(candidate_thread_with_interrupt, sandbox); + //mem_log("listener %d %d can be interrupted immediately for req deadline %lu\n", dispatcher_thread_idx, + // candidate_thread_with_interrupt, sandbox->absolute_deadline); + + preempt_worker(candidate_thread_with_interrupt); + dispatcher_try_interrupts++; + } else { + if (rr_index == worker_end_id + 1) { + rr_index = worker_start_id; + } + local_runqueue_add_index(rr_index, sandbox); + rr_index++; + } +} + +void rr_req_handler_without_interrupt(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + + if (rr_index == worker_end_id + 1) { + rr_index = worker_start_id; + } + local_runqueue_add_index(rr_index, sandbox); + rr_index++; +} + +void jsq_req_handler_without_interrupt(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + + int min_queue_len = INT_MAX; + int min_index = 0; + + next_loop_start_index++; + if (next_loop_start_index == current_active_workers) { + next_loop_start_index = 0; + } + + for(uint32_t i = next_loop_start_index; i < next_loop_start_index + current_active_workers; ++i) { + int true_idx = i % current_active_workers; + int len = local_runqueue_get_length_index(worker_list[true_idx]); + if (len < min_queue_len) { + min_queue_len = len; + min_index = worker_list[true_idx]; + } + } + local_runqueue_add_index(min_index, sandbox); +} + +void lld_req_handler_without_interrupt(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + + next_loop_start_index++; + if (next_loop_start_index == current_active_workers) { + next_loop_start_index = 0; + } + + uint64_t min_load = INT_MAX; + int min_index = 0; + + for(uint32_t i = next_loop_start_index; i < next_loop_start_index + current_active_workers; ++i) { + int true_idx = i % current_active_workers; + uint64_t load = get_local_queue_load(worker_list[true_idx]); + if (load < min_load) { + min_load = load; + min_index = worker_list[true_idx]; + } + } + local_runqueue_add_index(min_index, sandbox); +} +/** + * @brief Request routing function + * @param req_handle used by eRPC internal, it is used to send out the response packet + * @param req_type the type of the request. Each function has a unique reqest type id + * @param msg the payload of the rpc request. It is the input parameter fot the function + * @param size the size of the msg + */ +void darc_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + //TODO: rpc_id is hardcode now + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + push_to_rqueue(sandbox, request_type_queue[route->group_id - 1], __getcycles()); + global_queue_length++; + if (global_queue_length > max_queue_length) { + max_queue_length = global_queue_length; + } + +} + + +/** + * @brief Request routing function + * @param req_handle used by eRPC internal, it is used to send out the response packet + * @param req_type the type of the request. Each function has a unique reqest type id + * @param msg the payload of the rpc request. It is the input parameter fot the function + * @param size the size of the msg + */ +void shinjuku_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + //TODO: rpc_id is hardcode now + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + insertrear(request_type_deque[req_type -1], sandbox, __getcycles()); + global_queue_length++; + if (global_queue_length > max_queue_length) { + max_queue_length = global_queue_length; + } +} + +void drain_queue(struct request_typed_queue *rtype) { + assert(rtype != NULL); + /* queue is not empty and there is free workers */ + while (rtype->rqueue_head > rtype->rqueue_tail && free_workers[dispatcher_thread_idx] > 0) { + uint32_t worker_id = MAX_WORKERS + 1; + // Lookup for a core reserved to this type's group + for (uint32_t i = 0; i < rtype->n_resas; ++i) { + uint32_t candidate = rtype->res_workers[i]; + if ((1 << candidate) & free_workers[dispatcher_thread_idx]) { + worker_id = candidate; + //printf("Using reserved core %u\n", worker_list[worker_id]); + break; + } + } + // Otherwise attempt to steal worker + if (worker_id == MAX_WORKERS + 1) { + for (unsigned int i = 0; i < rtype->n_stealable; ++i) { + uint32_t candidate = rtype->stealable_workers[i]; + if ((1 << candidate) & free_workers[dispatcher_thread_idx]) { + worker_id = candidate; + //printf("Stealing core %u\n", worker_list[worker_id]); + break; + } + } + } + // No peer found + if (worker_id == MAX_WORKERS + 1) { + //printf("No availabe worker\n"); + return; + } + + // Dispatch + struct sandbox *sandbox = rtype->rqueue[rtype->rqueue_tail & (RQUEUE_LEN - 1)]; + //add sandbox to worker's local queue + local_runqueue_add_index(worker_list[worker_id], sandbox); + global_queue_length--; + rtype->rqueue_tail++; + atomic_fetch_xor(&free_workers[dispatcher_thread_idx], 1 << worker_id); + } + +} + +/* Return selected sandbox and selected queue type, not pop it out */ +struct sandbox * shinjuku_peek_selected_sandbox(int *selected_queue_idx) { + float highest_priority = 0; + *selected_queue_idx = -1; + struct sandbox *selected_sandbox = NULL; + + for (uint32_t i = 0; i < n_rtypes; ++i) { + if (isEmpty(request_type_deque[i])) + continue; + uint64_t ts = 0; + /* get the front sandbox of the deque */ + struct sandbox *sandbox = getFront(request_type_deque[i], &ts); + assert(sandbox != NULL); + uint64_t dt = __getcycles() - ts; + float priority = (float)dt / (float)sandbox->relative_deadline; + if (priority > highest_priority) { + highest_priority = priority; + *selected_queue_idx = i; + selected_sandbox = sandbox; + } + + } + + return selected_sandbox; + +} + +void shinjuku_dequeue_selected_sandbox(int selected_queue_type) { + assert(selected_queue_type >= 0 && selected_queue_type < MAX_REQUEST_TYPE); + deletefront(request_type_deque[selected_queue_type]); +} + +/* Return selected sandbox and pop it out */ +struct sandbox * shinjuku_select_sandbox() { + float highest_priority = -1; + int selected_queue_idx = -1; + struct sandbox *selected_sandbox = NULL; + + for (uint32_t i = 0; i < n_rtypes; ++i) { + if (isEmpty(request_type_deque[i])) + continue; + uint64_t ts = 0; + /* get the front sandbox of the deque */ + struct sandbox *sandbox = getFront(request_type_deque[i], &ts); + assert(sandbox != NULL); + uint64_t dt = __getcycles() - ts; + float priority = (float)dt / (float)sandbox->relative_deadline; + if (priority > highest_priority) { + highest_priority = priority; + selected_queue_idx = i; + selected_sandbox = sandbox; + } + } + + if (selected_sandbox != NULL) { + deletefront(request_type_deque[selected_queue_idx]); + } + + return selected_sandbox; + +} + +void darc_dispatch() { + for (uint32_t i = 0; i < n_rtypes; ++i) { + // request_type_queue is a sorted queue, so the loop will dispatch packets from + // the earliest deadline to least earliest deadline + if (request_type_queue[i]->rqueue_head > request_type_queue[i]->rqueue_tail) { + drain_queue(request_type_queue[i]); + } + } +} + +void shinjuku_dispatch_different_core() { + for (uint32_t i = 0; i < runtime_worker_group_size; ++i) { + /* move all preempted sandboxes from worker to dispatcher's typed queue */ + struct request_fifo_queue * preempted_queue = worker_preempted_queue[worker_list[i]]; + assert(preempted_queue != NULL); + uint64_t tsc = 0; + struct sandbox * preempted_sandbox = pop_worker_preempted_queue(worker_list[i], preempted_queue, &tsc); + while(preempted_sandbox != NULL) { + uint8_t req_type = preempted_sandbox->route->request_type; + insertfront(request_type_deque[req_type - 1], preempted_sandbox, tsc); + preempted_sandbox = pop_worker_preempted_queue(worker_list[i], preempted_queue, &tsc); + } + + /* core is idle */ + //if ((1 << i) & free_workers[dispatcher_thread_idx]) { + if (local_runqueue_get_length_index(worker_list[i]) == 0) { + struct sandbox *sandbox = shinjuku_select_sandbox(); + if (!sandbox) return; // queue is empty + + sandbox->start_ts = __getcycles(); + local_runqueue_add_index(worker_list[i], sandbox); + atomic_fetch_xor(&free_workers[dispatcher_thread_idx], 1 << i); + } else { // core is busy + //check if the current sandbox is running longer than the specified time duration + struct sandbox *current = current_sandboxes[worker_list[i]]; + if (!current) continue; //In case that worker thread hasn't call current_sandbox_set to set the current sandbox + uint64_t elapsed_cycles = (__getcycles() - current->start_ts); + if (elapsed_cycles >= shinjuku_interrupt_interval && (current->state == SANDBOX_RUNNING_USER || current->state == SANDBOX_RUNNING_SYS)) { + struct sandbox *sandbox = shinjuku_select_sandbox(); + if (!sandbox) return; // queue is empty + + //preempt the current sandbox and put it back the typed queue, select a new one to send to it + sandbox->start_ts = __getcycles(); + local_runqueue_add_index(worker_list[i], sandbox); + //preempt worker + dispatcher_try_interrupts++; + preempt_worker(worker_list[i]); + } + } + + } + +} + +void shinjuku_dispatch() { + next_loop_start_index++; + if (next_loop_start_index == runtime_worker_group_size) { + next_loop_start_index = 0; + } + + for (uint32_t i = next_loop_start_index; i < next_loop_start_index + runtime_worker_group_size; ++i) { + int true_idx = i % runtime_worker_group_size; + /* move all preempted sandboxes from worker to dispatcher's typed queue */ + struct request_fifo_queue * preempted_queue = worker_preempted_queue[worker_list[true_idx]]; + assert(preempted_queue != NULL); + uint64_t tsc = 0; + struct sandbox * preempted_sandbox = pop_worker_preempted_queue(worker_list[true_idx], preempted_queue, &tsc); + while(preempted_sandbox != NULL) { + uint8_t req_type = preempted_sandbox->route->request_type; + insertfront(request_type_deque[req_type - 1], preempted_sandbox, tsc); + //insertrear(request_type_deque[req_type - 1], preempted_sandbox, tsc); + global_queue_length++; + preempted_sandbox = pop_worker_preempted_queue(worker_list[true_idx], preempted_queue, &tsc); + } + /* core is idle */ + //if ((1 << i) & free_workers[dispatcher_thread_idx]) { + if (local_runqueue_get_length_index(worker_list[true_idx]) == 0) { + struct sandbox *sandbox = shinjuku_select_sandbox(); + if (!sandbox) return; // queue is empty + //while (sandbox->global_worker_thread_idx != -1 && sandbox->global_worker_thread_idx != worker_list[true_idx]) { + while (sandbox->global_worker_thread_idx != -1) { + sandbox->start_ts = __getcycles(); + local_runqueue_add_index(sandbox->global_worker_thread_idx, sandbox); + global_queue_length--; + worker_old_sandbox[worker_list[true_idx]]++; + sandbox = shinjuku_select_sandbox(); + if (!sandbox) return; + } + + sandbox->start_ts = __getcycles(); + local_runqueue_add_index(worker_list[true_idx], sandbox); + global_queue_length--; + worker_new_sandbox[worker_list[true_idx]]++; + atomic_fetch_xor(&free_workers[dispatcher_thread_idx], 1 << true_idx); + } else { // core is busy + //check if the current sandbox is running longer than the specified time duration + struct sandbox *current = current_sandboxes[worker_list[true_idx]]; + if (!current) continue; //In case that worker thread hasn't call current_sandbox_set to set the current sandbox + uint64_t elapsed_cycles = (__getcycles() - current->start_ts); + if (elapsed_cycles >= shinjuku_interrupt_interval && (current->state == SANDBOX_RUNNING_USER || current->state == SANDBOX_RUNNING_SYS)) { + struct sandbox *sandbox = shinjuku_select_sandbox(); + if (!sandbox) return; // queue is empty + + //while (sandbox->global_worker_thread_idx != -1 && sandbox->global_worker_thread_idx != worker_list[true_idx]) { + while (sandbox->global_worker_thread_idx != -1) { + sandbox->start_ts = __getcycles(); + local_runqueue_add_index(sandbox->global_worker_thread_idx, sandbox); + global_queue_length--; + worker_old_sandbox[worker_list[true_idx]]++; + sandbox = shinjuku_select_sandbox(); + if (!sandbox) return; + } + + //preempt the current sandbox and put it back the typed queue, select a new one to send to it + sandbox->start_ts = __getcycles(); + local_runqueue_add_index(worker_list[true_idx], sandbox); + global_queue_length--; + worker_new_sandbox[worker_list[true_idx]]++; + //preempt worker + preempt_worker(worker_list[true_idx]); + dispatcher_try_interrupts++; + } + } + + } +} + +void enqueue_to_global_queue_req_handler(void *req_handle, uint8_t req_type, uint8_t *msg, size_t size, uint16_t port) { + + if (first_request_comming == false){ + t_start = time(NULL); + first_request_comming = true; + } + + uint8_t kMsgSize = 16; + //TODO: rpc_id is hardcode now + + struct tenant *tenant = tenant_database_find_by_port(port); + assert(tenant != NULL); + struct route *route = http_router_match_request_type(&tenant->router, req_type); + if (route == NULL) { + debuglog("Did not match any routes\n"); + dispatcher_send_response(req_handle, DIPATCH_ROUNTE_ERROR, strlen(DIPATCH_ROUNTE_ERROR)); + return; + } + + /* + * Perform admissions control. + * If 0, workload was rejected, so close with 429 "Too Many Requests" and continue + * TODO: Consider providing a Retry-After header + */ + uint64_t work_admitted = admissions_control_decide(route->admissions_info.estimate); + if (work_admitted == 0) { + dispatcher_send_response(req_handle, WORK_ADMITTED_ERROR, strlen(WORK_ADMITTED_ERROR)); + return; + } + + total_requests++; + requests_counter[dispatcher_thread_idx][req_type]++; + /* Allocate a Sandbox */ + //session->state = HTTP_SESSION_EXECUTING; + struct sandbox *sandbox = sandbox_alloc(route->module, NULL, route, tenant, work_admitted, req_handle, dispatcher_thread_idx); + if (unlikely(sandbox == NULL)) { + debuglog("Failed to allocate sandbox\n"); + dispatcher_send_response(req_handle, SANDBOX_ALLOCATION_ERROR, strlen(SANDBOX_ALLOCATION_ERROR)); + return; + } + + /* Reset estimated execution time and relative deadline for exponential service time simulation */ + if (runtime_exponential_service_time_simulation_enabled) { + int exp_num = atoi((const char *)msg); + if (exp_num == 1) { + sandbox->estimated_cost = base_simulated_service_time; + } else { + sandbox->estimated_cost = base_simulated_service_time * exp_num * (1 - LOSS_PERCENTAGE); + } + sandbox->relative_deadline = 10 * sandbox->estimated_cost; + sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->relative_deadline; + } + + /* copy the received data since it will be released by erpc */ + sandbox->rpc_request_body = malloc(size); + if (!sandbox->rpc_request_body) { + panic("malloc request body failed\n"); + } + + memcpy(sandbox->rpc_request_body, msg, size); + sandbox->rpc_request_body_size = size; + //printf("add request to GQ\n"); + global_request_scheduler_add(sandbox); + global_queue_length++; + if (global_queue_length > max_queue_length) { + max_queue_length = global_queue_length; + } +} + +void dispatcher_send_response(void *req_handle, char* msg, size_t msg_len) { + erpc_req_response_enqueue(dispatcher_thread_idx, req_handle, msg, msg_len, 1); +} /** * @brief Execution Loop of the listener core, io_handles HTTP requests, allocates sandbox request objects, and * pushes the sandbox object to the global dequeue @@ -398,9 +1629,49 @@ on_client_socket_epoll_event(struct epoll_event *evt) * listener_thread_epoll_file_descriptor - the epoll file descriptor * */ -noreturn void * +void * listener_thread_main(void *dummy) { + pthread_setname_np(pthread_self(), "dispatcher"); + is_listener = true; + shinjuku_interrupt_interval = INTERRUPT_INTERVAL * runtime_processor_speed_MHz; + base_simulated_service_time = BASE_SERVICE_TIME * runtime_processor_speed_MHz; + + /* Initialize memory logging, set 1M memory for logging */ + mem_log_init2(1024*1024*1024, sandbox_perf_log); + + /* Unmask SIGINT signals */ + software_interrupt_unmask_signal(SIGINT); + + /* Index was passed via argument */ + dispatcher_thread_idx = *(int *)dummy; + + /* init typed queue */ + typed_queue_init(); + + /* calucate the worker start and end id for this listener */ + worker_start_id = dispatcher_thread_idx * runtime_worker_group_size; + worker_end_id = worker_start_id + runtime_worker_group_size - 1; + + int index = 0; + for (uint32_t i = worker_start_id; i <= worker_end_id; i++) { + worker_list[index] = i; + index++; + } + + rr_index = worker_start_id; + if (runtime_autoscaling_enabled) { + current_active_workers = 1; + } else { + current_active_workers = runtime_worker_group_size; + } + printf("listener %d worker_start_id %d worker_end_id %d active worker %d\n", + dispatcher_thread_idx, worker_start_id, worker_end_id, current_active_workers); + + free_workers[dispatcher_thread_idx] = __builtin_powi(2, current_active_workers) - 1; + + printf("free_workers is %u\n", free_workers[dispatcher_thread_idx]); + struct epoll_event epoll_events[RUNTIME_MAX_EPOLL_EVENTS]; metrics_server_init(); @@ -410,7 +1681,50 @@ listener_thread_main(void *dummy) // runtime_set_pthread_prio(pthread_self(), 2); pthread_setschedprio(pthread_self(), -20); - while (true) { + erpc_start(NULL, dispatcher_thread_idx, NULL, 0); + + if (dispatcher == DISPATCHER_EDF_INTERRUPT) { + printf("edf_interrupt....\n"); + while (!pthread_stop) { + erpc_run_event_loop(dispatcher_thread_idx, 1000); + } + } else if (dispatcher == DISPATCHER_DARC) { + printf("darc....\n"); + while (!pthread_stop) { + erpc_run_event_loop_once(dispatcher_thread_idx); // get a group of packets from the NIC and enqueue them to the typed queue + darc_dispatch(); //dispatch packets + } + } else if (dispatcher == DISPATCHER_SHINJUKU) { + printf("shinjuku....\n"); + while (!pthread_stop) { + erpc_run_event_loop_once(dispatcher_thread_idx); // get a group of packets from the NIC and enqueue them to the typed queue + shinjuku_dispatch(); //dispatch packets + } + } else if (dispatcher == DISPATCHER_TO_GLOBAL_QUEUE) { + printf("to global queeu....\n"); + while (!pthread_stop) { + erpc_run_event_loop_once(dispatcher_thread_idx); + } + } else if (dispatcher == DISPATCHER_RR) { + printf("RR....\n"); + while (!pthread_stop) { + erpc_run_event_loop_once(dispatcher_thread_idx); + } + } else if (dispatcher == DISPATCHER_JSQ) { + printf("JSQ....\n"); + while (!pthread_stop) { + erpc_run_event_loop_once(dispatcher_thread_idx); + } + } else if (dispatcher == DISPATCHER_LLD) { + printf("LLD....\n"); + while (!pthread_stop) { + erpc_run_event_loop_once(dispatcher_thread_idx); + } + } + + /* code will end here with eRPC and won't go to the following implementaion */ + while (!pthread_stop) { + printf("pthread_stop is false\n"); /* Block indefinitely on the epoll file descriptor, waiting on up to a max number of events */ int descriptor_count = epoll_wait(listener_thread_epoll_file_descriptor, epoll_events, RUNTIME_MAX_EPOLL_EVENTS, -1); @@ -443,6 +1757,6 @@ listener_thread_main(void *dummy) } } } - - panic("Listener thread unexpectedly broke loop\n"); + return NULL; + //panic("Listener thread unexpectedly broke loop\n"); } diff --git a/runtime/src/local_cleanup_queue.c b/runtime/src/local_cleanup_queue.c index 1587c5b01..46cfdf785 100644 --- a/runtime/src/local_cleanup_queue.c +++ b/runtime/src/local_cleanup_queue.c @@ -37,15 +37,19 @@ local_cleanup_queue_add(struct sandbox *sandbox) * @brief Frees all sandboxes in the thread local cleanup queue * @return void */ -void -local_cleanup_queue_free() +int +local_cleanup_queue_free(uint64_t *duration, uint64_t *ret) { struct sandbox *sandbox_iterator = NULL; struct sandbox *buffer = NULL; + int cleanup_count = 0; ps_list_foreach_del_d(&local_cleanup_queue, sandbox_iterator, buffer) { ps_list_rem_d(sandbox_iterator); - sandbox_free(sandbox_iterator); + *duration = sandbox_free(sandbox_iterator, ret); + cleanup_count++; } + + return cleanup_count; } diff --git a/runtime/src/local_preempted_fifo_queue.c b/runtime/src/local_preempted_fifo_queue.c new file mode 100644 index 000000000..21f587025 --- /dev/null +++ b/runtime/src/local_preempted_fifo_queue.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "panic.h" +#include "likely.h" +#include "request_fifo_queue.h" + +extern thread_local int global_worker_thread_idx; +extern struct request_fifo_queue * worker_preempted_queue[1024]; +thread_local static struct request_fifo_queue * local_preempted_queue = NULL; + +struct request_fifo_queue * request_fifo_queue_init() { + struct request_fifo_queue * queue = (struct request_fifo_queue*) malloc(sizeof(struct request_fifo_queue)); + + assert(queue != NULL); + + queue->rqueue_tail = 0; + queue->rqueue_head = 0; + memset(queue->rqueue, 0, RQUEUE_QUEUE_LEN * sizeof(struct sandbox*)); + return queue; +} + +int push_to_queue(struct request_fifo_queue * queue, struct sandbox *sandbox, uint64_t tsc) { + assert(sandbox != NULL); + assert(queue != NULL); + + uint32_t head = queue->rqueue_head; + + if (unlikely(head - queue->rqueue_tail == RQUEUE_QUEUE_LEN)) { + panic("request fifo queue is full\n"); + return -1; + } else { + queue->tsqueue[head & (RQUEUE_QUEUE_LEN - 1)] = tsc; + queue->rqueue[head & (RQUEUE_QUEUE_LEN - 1)] = sandbox; + queue->rqueue_head++; + return 0; + } + +} + +/* called by worker thread */ +int push_to_preempted_queue(struct sandbox *sandbox, uint64_t tsc) { + assert(sandbox != NULL); + return push_to_queue(local_preempted_queue, sandbox, tsc); +} + +/* + * pop one item from the tail of the queue + * tsc is the timestamp of the popped sandbox, only be set when popped sandbox is not NULL + */ +struct sandbox * pop_queue(struct request_fifo_queue * queue, uint64_t * tsc) { + assert(queue != NULL); + + struct sandbox *popped_sandbox = NULL; + *tsc = 0; + + if (queue->rqueue_head > queue->rqueue_tail) { + popped_sandbox = queue->rqueue[queue->rqueue_tail & (RQUEUE_QUEUE_LEN - 1)]; + *tsc = queue->tsqueue[queue->rqueue_tail & (RQUEUE_QUEUE_LEN - 1)]; + queue->rqueue[queue->rqueue_tail & (RQUEUE_QUEUE_LEN - 1)] = NULL; + queue->rqueue_tail++; + } + + return popped_sandbox; +} + +/* called by dispatcher thread */ +struct sandbox * pop_worker_preempted_queue(int worker_id, struct request_fifo_queue * queue, uint64_t * tsc) { + struct request_fifo_queue * preempted_queue = worker_preempted_queue[worker_id]; + assert(preempted_queue != NULL); + + return pop_queue(preempted_queue, tsc); +} + +void local_preempted_fifo_queue_init() { + local_preempted_queue = request_fifo_queue_init(); + worker_preempted_queue[global_worker_thread_idx] = local_preempted_queue; +} diff --git a/runtime/src/local_runqueue.c b/runtime/src/local_runqueue.c index 6e12f2032..a7e243326 100644 --- a/runtime/src/local_runqueue.c +++ b/runtime/src/local_runqueue.c @@ -6,11 +6,17 @@ #include "local_runqueue.h" -static struct local_runqueue_config local_runqueue; +extern thread_local int global_worker_thread_idx; +extern bool runtime_worker_busy_loop_enabled; +extern pthread_mutex_t mutexs[1024]; +extern pthread_cond_t conds[1024]; +extern sem_t semlock[1024]; -#ifdef LOG_LOCAL_RUNQUEUE -thread_local uint32_t local_runqueue_count = 0; -#endif +extern _Atomic uint64_t worker_queuing_cost[1024]; +static struct local_runqueue_config local_runqueue; +thread_local uint32_t total_complete_requests = 0; +_Atomic uint32_t local_runqueue_count[1024]; +struct timespec startT[1024]; /* Initializes a concrete implementation of the sandbox request scheduler interface */ void @@ -27,12 +33,65 @@ void local_runqueue_add(struct sandbox *sandbox) { assert(local_runqueue.add_fn != NULL); -#ifdef LOG_LOCAL_RUNQUEUE - local_runqueue_count++; -#endif - return local_runqueue.add_fn(sandbox); + local_runqueue.add_fn(sandbox); + //atomic_fetch_add(&local_runqueue_count[global_worker_thread_idx], 1); +} + +void +local_runqueue_add_index(int index, struct sandbox *sandbox) +{ + assert(local_runqueue.add_fn_idx != NULL); + /* wakeup worker if it is empty before we add a new request */ + + if (!runtime_worker_busy_loop_enabled) { + /*pthread_mutex_lock(&mutexs[index]); + if (local_runqueue_is_empty_index(index)) { + local_runqueue.add_fn_idx(index, sandbox); + //atomic_fetch_add(&local_runqueue_count[index], 1); + pthread_mutex_unlock(&mutexs[index]); + clock_gettime(CLOCK_MONOTONIC, &startT[index]); + pthread_cond_signal(&conds[index]); + } else { + pthread_mutex_unlock(&mutexs[index]); + local_runqueue.add_fn_idx(index, sandbox); + }*/ + + /* Semaphore could lost signal and cause worker block for a short time decrasing performance, + but based on test, it seems Semaphore has a better performance than condition variable + */ + if (local_runqueue_is_empty_index(index)) { + local_runqueue.add_fn_idx(index, sandbox); + //clock_gettime(CLOCK_MONOTONIC, &startT[index]); + sem_post(&semlock[index]); + } else { + local_runqueue.add_fn_idx(index, sandbox); + } + } else { + local_runqueue.add_fn_idx(index, sandbox); + } + +} + +uint64_t +local_runqueue_try_add_index(int index, struct sandbox *sandbox, bool *need_interrupt) +{ + assert(local_runqueue.try_add_fn_idx != NULL); + return local_runqueue.try_add_fn_idx(index, sandbox, need_interrupt); } +uint32_t +local_runqueue_add_and_get_length_index(int index, struct sandbox *sandbox, bool *need_interrupt) +{ + assert(local_runqueue.try_add_and_get_len_fn_t_idx != NULL); + return local_runqueue.try_add_and_get_len_fn_t_idx(index, sandbox, need_interrupt); +} + +uint64_t +local_runqueue_add_and_get_load_index(int index, struct sandbox *sandbox, bool *need_interrupt) +{ + assert(local_runqueue.try_add_and_get_load_fn_t_idx != NULL); + return local_runqueue.try_add_and_get_load_fn_t_idx(index, sandbox, need_interrupt); +} /** * Delete a sandbox from the run queue * @param sandbox to delete @@ -41,10 +100,9 @@ void local_runqueue_delete(struct sandbox *sandbox) { assert(local_runqueue.delete_fn != NULL); -#ifdef LOG_LOCAL_RUNQUEUE - local_runqueue_count--; -#endif + total_complete_requests++; local_runqueue.delete_fn(sandbox); + //atomic_fetch_sub(&local_runqueue_count[global_worker_thread_idx], 1); } /** @@ -58,6 +116,37 @@ local_runqueue_is_empty() return local_runqueue.is_empty_fn(); } +/** + * Checks if run queue is empty + * @returns true if empty + */ +bool +local_runqueue_is_empty_index(int index) +{ + assert(local_runqueue.is_empty_fn_idx != NULL); + return local_runqueue.is_empty_fn_idx(index); +} + +/** + * Get height if run queue is a binary search tree + */ + +int local_runqueue_get_height() { + assert(local_runqueue.get_height_fn != NULL); + return local_runqueue.get_height_fn(); +} + +/** + * Get total count of items in the queue + */ +int local_runqueue_get_length() { + assert(local_runqueue.get_length_fn != NULL); + return local_runqueue.get_length_fn(); +} +int local_runqueue_get_length_index(int index) { + assert(local_runqueue.get_length_fn_idx != NULL); + return local_runqueue.get_length_fn_idx(index); +} /** * Get next sandbox from run queue, where next is defined by * @returns sandbox (or NULL?) @@ -68,3 +157,38 @@ local_runqueue_get_next() assert(local_runqueue.get_next_fn != NULL); return local_runqueue.get_next_fn(); }; + +void +worker_queuing_cost_initialize() +{ + for (int i = 0; i < 1024; i++) { + atomic_init(&worker_queuing_cost[i], 0); + //atomic_init(&local_runqueue_count[i], 0); + } +} + +void +worker_queuing_cost_increment(int index, uint64_t cost) +{ + atomic_fetch_add(&worker_queuing_cost[index], cost); +} + +void +worker_queuing_cost_decrement(int index, uint64_t cost) +{ + assert(index >= 0 && index < 1024); + atomic_fetch_sub(&worker_queuing_cost[index], cost); + assert(worker_queuing_cost[index] >= 0); +} + +uint64_t +get_local_queue_load(int index) { + return worker_queuing_cost[index]; +} + +void +wakeup_worker(int index) { + pthread_mutex_lock(&mutexs[index]); + pthread_cond_signal(&conds[index]); + pthread_mutex_unlock(&mutexs[index]); +} diff --git a/runtime/src/local_runqueue_binary_tree.c b/runtime/src/local_runqueue_binary_tree.c new file mode 100644 index 000000000..e40d4c3fe --- /dev/null +++ b/runtime/src/local_runqueue_binary_tree.c @@ -0,0 +1,250 @@ +#include +#include + +#include "software_interrupt.h" +#include "arch/context.h" +#include "current_sandbox.h" +#include "debuglog.h" +#include "global_request_scheduler.h" +#include "local_runqueue.h" +#include "local_runqueue_binary_tree.h" +#include "panic.h" +#include "binary_search_tree.h" +#include "sandbox_functions.h" +#include "runtime.h" +#include "memlogging.h" + +extern thread_local uint8_t dispatcher_thread_idx; +extern _Atomic uint32_t local_queue_length[1024]; +extern uint32_t max_local_queue_length[1024]; +extern uint32_t max_local_queue_height[1024]; +extern bool runtime_exponential_service_time_simulation_enabled; +extern thread_local int global_worker_thread_idx; +extern struct sandbox* current_sandboxes[1024]; +extern struct binary_tree *worker_binary_trees[1024]; +thread_local static struct binary_tree *local_runqueue_binary_tree = NULL; + + +/** + * Checks if the run queue is empty + * @returns true if empty. false otherwise + */ +bool +local_runqueue_binary_tree_is_empty() +{ + assert(local_runqueue_binary_tree != NULL); + + return is_empty(local_runqueue_binary_tree); +} + +/** + * Checks if the run queue is empty + * @returns true if empty. false otherwise + */ +bool +local_runqueue_binary_tree_is_empty_index(int index) +{ + struct binary_tree *binary_tree = worker_binary_trees[index]; + + assert(binary_tree != NULL); + + return is_empty(binary_tree); +} + +/** + * Adds a sandbox to the run queue + * @param sandbox + * @returns pointer to sandbox added + */ +void +local_runqueue_binary_tree_add(struct sandbox *sandbox) +{ + assert(sandbox != NULL); + + lock_node_t node_lock = {}; + lock_lock(&local_runqueue_binary_tree->lock, &node_lock); + local_runqueue_binary_tree->root = insert(local_runqueue_binary_tree, local_runqueue_binary_tree->root, sandbox, global_worker_thread_idx); + lock_unlock(&local_runqueue_binary_tree->lock, &node_lock); +} + +void +local_runqueue_binary_tree_add_index(int index, struct sandbox *sandbox) +{ + assert(sandbox != NULL); + + struct binary_tree *binary_tree = worker_binary_trees[index]; + lock_node_t node_lock = {}; + lock_lock(&binary_tree->lock, &node_lock); + binary_tree->root = insert(binary_tree, binary_tree->root, sandbox, index); + lock_unlock(&binary_tree->lock, &node_lock); + + /*int height = findHeight(binary_tree->root); + if ( height > max_local_queue_height[index]) { + max_local_queue_height[index] = height; + }*/ + + atomic_fetch_add(&local_queue_length[index], 1); + if (local_queue_length[index] > max_local_queue_length[index]) { + max_local_queue_length[index] = local_queue_length[index]; + /*mem_log("listener %d 1:%u 2:%u 3:%u 4:%u 5:%u 6:%u 7:%u 8:%u 9:%u\n", dispatcher_thread_idx, max_local_queue_length[0], + max_local_queue_length[1],max_local_queue_length[2], + max_local_queue_length[3],max_local_queue_length[4], max_local_queue_length[5], max_local_queue_length[6], + max_local_queue_length[7], max_local_queue_length[8]); + */ + } + + /* Set estimated exeuction time for the sandbox */ + if (runtime_exponential_service_time_simulation_enabled == false) { + uint32_t uid = sandbox->route->admissions_info.uid; + uint64_t estimated_execute_cost = perf_window_get_percentile(&worker_perf_windows[index][uid], + sandbox->route->admissions_info.percentile, + sandbox->route->admissions_info.control_index); + /* Use expected execution time in the configuration file as the esitmated execution time + if estimated_execute_cost is 0 + */ + if (estimated_execute_cost == 0) { + estimated_execute_cost = sandbox->route->expected_execution_cycle; + } + sandbox->estimated_cost = estimated_execute_cost; + sandbox->relative_deadline = sandbox->route->relative_deadline; + } + /* Record TS and calcuate RS. SRSF algo: + 1. When reqeust arrives to the queue, record TS and calcuate RS. RS = deadline - execution time + 2. When request starts running, update RS + 3. When request stops, update TS + 4. When request resumes, update RS + */ + sandbox->srsf_stop_running_ts = __getcycles(); + sandbox->srsf_remaining_slack = sandbox->relative_deadline - sandbox->estimated_cost; + worker_queuing_cost_increment(index, sandbox->estimated_cost); +} + +/** + * Deletes a sandbox from the runqueue + * @param sandbox to delete + */ +static void +local_runqueue_binary_tree_delete(struct sandbox *sandbox) +{ + assert(sandbox != NULL); + + lock_node_t node_lock = {}; + lock_lock(&local_runqueue_binary_tree->lock, &node_lock); + bool deleted = false; + local_runqueue_binary_tree->root = delete_i(local_runqueue_binary_tree, local_runqueue_binary_tree->root, sandbox, &deleted, global_worker_thread_idx); + lock_unlock(&local_runqueue_binary_tree->lock, &node_lock); + if (deleted == false) { + panic("Tried to delete sandbox %lu state %d from runqueue %p, but was not present\n", + sandbox->id, sandbox->state, local_runqueue_binary_tree); + } + + atomic_fetch_sub(&local_queue_length[global_worker_thread_idx], 1); + worker_queuing_cost_decrement(global_worker_thread_idx, sandbox->estimated_cost); +} + +/** + * This function determines the next sandbox to run. + * This is the head of the local runqueue + * + * Execute the sandbox at the head of the thread local runqueue + * @return the sandbox to execute or NULL if none are available + */ +struct sandbox * +local_runqueue_binary_tree_get_next() +{ + /* Get the minimum deadline of the sandbox of the local request queue */ + struct TreeNode *node = findMin(local_runqueue_binary_tree, local_runqueue_binary_tree->root); + if (node != NULL) { + return node->data; + } else { + return NULL; + } +} + +/** + * Try but not real add a item to the local runqueue. + * @param index The worker thread id + * @param sandbox Try to add + * @returns The waiting serving time in cycles for this sandbox if adding it to the queue + */ +uint64_t +local_runqueue_binary_tree_try_add_index(int index, struct sandbox *sandbox, bool *need_interrupt) +{ + struct binary_tree *binary_tree = worker_binary_trees[index]; + assert(binary_tree != NULL); + + if (is_empty(binary_tree)) { + /* The worker is idle */ + *need_interrupt = false; + return 0; + } else if (current_sandboxes[index] != NULL && + current_sandboxes[index]->srsf_remaining_slack > 0 && + sandbox_is_preemptable(current_sandboxes[index]) == true && + sandbox_get_priority(sandbox) < sandbox_get_priority(current_sandboxes[index])) { + /* The new one has a higher priority than the current one, need to interrupt the current one */ + *need_interrupt = true; + return 0; + } else { + /* Current sandbox cannot be interrupted because its priority is higher or its RS is 0, just find + a right location to add the new sandbox to the tree + */ + need_interrupt = false; + uint64_t waiting_serving_time = 0; + lock_node_t node_lock = {}; + lock_lock(&binary_tree->lock, &node_lock); + waiting_serving_time = findMaxValueLessThan(binary_tree, binary_tree->root, sandbox, index); + lock_unlock(&binary_tree->lock, &node_lock); + return waiting_serving_time; + } + +} + +int local_runqueue_binary_tree_get_height() { + assert (local_runqueue_binary_tree != NULL); + return findHeight(local_runqueue_binary_tree->root); +} + +int local_runqueue_binary_tree_get_length() { + assert (local_runqueue_binary_tree != NULL); + return getNonDeletedNodeCount(local_runqueue_binary_tree); +} + +int local_runqueue_binary_tree_get_length_index(int index) { + struct binary_tree *binary_tree = worker_binary_trees[index]; + assert(binary_tree != NULL); + + return getNonDeletedNodeCount(binary_tree); +} + +void local_runqueue_print_in_order(int index) { + struct binary_tree *binary_tree = worker_binary_trees[index]; + assert(binary_tree != NULL); + print_tree_in_order(binary_tree); +} + +/** + * Registers the PS variant with the polymorphic interface + */ +void +local_runqueue_binary_tree_initialize() +{ + /* Initialize local state */ + local_runqueue_binary_tree = init_binary_tree(true, sandbox_get_priority, sandbox_get_execution_cost, global_worker_thread_idx, 4096); + + worker_binary_trees[global_worker_thread_idx] = local_runqueue_binary_tree; + /* Register Function Pointers for Abstract Scheduling API */ + struct local_runqueue_config config = { .add_fn = local_runqueue_binary_tree_add, + .add_fn_idx = local_runqueue_binary_tree_add_index, + .try_add_fn_idx = local_runqueue_binary_tree_try_add_index, + .is_empty_fn = local_runqueue_binary_tree_is_empty, + .is_empty_fn_idx = local_runqueue_binary_tree_is_empty_index, + .delete_fn = local_runqueue_binary_tree_delete, + .get_next_fn = local_runqueue_binary_tree_get_next, + .get_height_fn = local_runqueue_binary_tree_get_height, + .get_length_fn = local_runqueue_binary_tree_get_length, + .get_length_fn_idx = local_runqueue_binary_tree_get_length_index, + .print_in_order_fn_idx = local_runqueue_print_in_order + }; + + local_runqueue_initialize(&config); +} diff --git a/runtime/src/local_runqueue_binary_tree.c_redblacktree b/runtime/src/local_runqueue_binary_tree.c_redblacktree new file mode 100644 index 000000000..5a5d88bbe --- /dev/null +++ b/runtime/src/local_runqueue_binary_tree.c_redblacktree @@ -0,0 +1,256 @@ +#include +#include + +#include "software_interrupt.h" +#include "arch/context.h" +#include "current_sandbox.h" +#include "debuglog.h" +#include "global_request_scheduler.h" +#include "local_runqueue.h" +#include "local_runqueue_binary_tree.h" +#include "panic.h" +#include "binary_search_tree.h" +#include "sandbox_functions.h" +#include "runtime.h" +#include "memlogging.h" + +extern thread_local uint8_t dispatcher_thread_idx; +extern _Atomic uint32_t local_queue_length[1024]; +extern uint32_t max_local_queue_length[1024]; +extern uint32_t max_local_queue_height[1024]; +extern bool runtime_exponential_service_time_simulation_enabled; +extern thread_local int global_worker_thread_idx; +extern struct sandbox* current_sandboxes[1024]; +extern struct binary_tree *worker_binary_trees[1024]; +thread_local static struct binary_tree *local_runqueue_binary_tree = NULL; + + +/** + * Checks if the run queue is empty + * @returns true if empty. false otherwise + */ +bool +local_runqueue_binary_tree_is_empty() +{ + assert(local_runqueue_binary_tree != NULL); + + return is_empty(local_runqueue_binary_tree); +} + +/** + * Checks if the run queue is empty + * @returns true if empty. false otherwise + */ +bool +local_runqueue_binary_tree_is_empty_index(int index) +{ + struct binary_tree *binary_tree = worker_binary_trees[index]; + + assert(binary_tree != NULL); + + return is_empty(binary_tree); +} + +/** + * Adds a sandbox to the run queue + * @param sandbox + * @returns pointer to sandbox added + */ +void +local_runqueue_binary_tree_add(struct sandbox *sandbox) +{ + assert(sandbox != NULL); + + lock_node_t node_lock = {}; + lock_lock(&local_runqueue_binary_tree->lock, &node_lock); + insert(local_runqueue_binary_tree, sandbox, global_worker_thread_idx); + lock_unlock(&local_runqueue_binary_tree->lock, &node_lock); +} + +void +local_runqueue_binary_tree_add_index(int index, struct sandbox *sandbox) +{ + assert(sandbox != NULL); + + struct binary_tree *binary_tree = worker_binary_trees[index]; + lock_node_t node_lock = {}; + lock_lock(&binary_tree->lock, &node_lock); + insert(binary_tree, sandbox, index); + lock_unlock(&binary_tree->lock, &node_lock); + + /*int height = findHeight(binary_tree->root); + if ( height > max_local_queue_height[index]) { + max_local_queue_height[index] = height; + }*/ + + atomic_fetch_add(&local_queue_length[index], 1); + if (local_queue_length[index] > max_local_queue_length[index]) { + max_local_queue_length[index] = local_queue_length[index]; + /*mem_log("listener %d 1:%u 2:%u 3:%u 4:%u 5:%u 6:%u 7:%u 8:%u 9:%u\n", dispatcher_thread_idx, max_local_queue_length[0], + max_local_queue_length[1],max_local_queue_length[2], + max_local_queue_length[3],max_local_queue_length[4], max_local_queue_length[5], max_local_queue_length[6], + max_local_queue_length[7], max_local_queue_length[8]); + */ + } + + /* Set estimated exeuction time for the sandbox */ + if (runtime_exponential_service_time_simulation_enabled == false) { + uint32_t uid = sandbox->route->admissions_info.uid; + uint64_t estimated_execute_cost = perf_window_get_percentile(&worker_perf_windows[index][uid], + sandbox->route->admissions_info.percentile, + sandbox->route->admissions_info.control_index); + /* Use expected execution time in the configuration file as the esitmated execution time + if estimated_execute_cost is 0 + */ + if (estimated_execute_cost == 0) { + estimated_execute_cost = sandbox->route->expected_execution_cycle; + } + sandbox->estimated_cost = estimated_execute_cost; + sandbox->relative_deadline = sandbox->route->relative_deadline; + } + /* Record TS and calcuate RS. SRSF algo: + 1. When reqeust arrives to the queue, record TS and calcuate RS. RS = deadline - execution time + 2. When request starts running, update RS + 3. When request stops, update TS + 4. When request resumes, update RS + */ + sandbox->srsf_stop_running_ts = __getcycles(); + sandbox->srsf_remaining_slack = sandbox->relative_deadline - sandbox->estimated_cost; + worker_queuing_cost_increment(index, sandbox->estimated_cost); + //------------------for test------------------------- + /*int high = findHeight(binary_tree->root); + if (high > max_local_queue_height[index]) { + max_local_queue_height[index] = high; + } */ + //---------------------end------------------------- +} + +/** + * Deletes a sandbox from the runqueue + * @param sandbox to delete + */ +static void +local_runqueue_binary_tree_delete(struct sandbox *sandbox) +{ + assert(sandbox != NULL); + + lock_node_t node_lock = {}; + lock_lock(&local_runqueue_binary_tree->lock, &node_lock); + bool deleted = false; + delete_i(local_runqueue_binary_tree, sandbox, &deleted, global_worker_thread_idx); + lock_unlock(&local_runqueue_binary_tree->lock, &node_lock); + if (deleted == false) { + panic("Tried to delete sandbox %lu state %d from runqueue %p, but was not present\n", + sandbox->id, sandbox->state, local_runqueue_binary_tree); + } + + atomic_fetch_sub(&local_queue_length[global_worker_thread_idx], 1); + worker_queuing_cost_decrement(global_worker_thread_idx, sandbox->estimated_cost); +} + +/** + * This function determines the next sandbox to run. + * This is the head of the local runqueue + * + * Execute the sandbox at the head of the thread local runqueue + * @return the sandbox to execute or NULL if none are available + */ +struct sandbox * +local_runqueue_binary_tree_get_next() +{ + /* Get the minimum deadline of the sandbox of the local request queue */ + struct TreeNode *node = findMin(local_runqueue_binary_tree); + if (node != NULL) { + return node->data; + } else { + return NULL; + } +} + +/** + * Try but not real add a item to the local runqueue. + * @param index The worker thread id + * @param sandbox Try to add + * @returns The waiting serving time in cycles for this sandbox if adding it to the queue + */ +uint64_t +local_runqueue_binary_tree_try_add_index(int index, struct sandbox *sandbox, bool *need_interrupt) +{ + struct binary_tree *binary_tree = worker_binary_trees[index]; + assert(binary_tree != NULL); + + if (is_empty(binary_tree)) { + /* The worker is idle */ + *need_interrupt = false; + return 0; + } else if (current_sandboxes[index] != NULL && + current_sandboxes[index]->srsf_remaining_slack > 0 && + sandbox_is_preemptable(current_sandboxes[index]) == true && + sandbox_get_priority(sandbox) < sandbox_get_priority(current_sandboxes[index])) { + /* The new one has a higher priority than the current one, need to interrupt the current one */ + *need_interrupt = true; + return 0; + } else { + /* Current sandbox cannot be interrupted because its priority is higher or its RS is 0, just find + a right location to add the new sandbox to the tree + */ + need_interrupt = false; + uint64_t waiting_serving_time = 0; + lock_node_t node_lock = {}; + lock_lock(&binary_tree->lock, &node_lock); + waiting_serving_time = findMaxValueLessThan(binary_tree, binary_tree->root, sandbox, index); + lock_unlock(&binary_tree->lock, &node_lock); + return waiting_serving_time; + } + +} + +int local_runqueue_binary_tree_get_height() { + assert (local_runqueue_binary_tree != NULL); + return findHeight(local_runqueue_binary_tree->root); +} + +int local_runqueue_binary_tree_get_length() { + assert (local_runqueue_binary_tree != NULL); + return getNonDeletedNodeCount(local_runqueue_binary_tree); +} + +int local_runqueue_binary_tree_get_length_index(int index) { + struct binary_tree *binary_tree = worker_binary_trees[index]; + assert(binary_tree != NULL); + + return getNonDeletedNodeCount(binary_tree); +} + +void local_runqueue_print_in_order(int index) { + struct binary_tree *binary_tree = worker_binary_trees[index]; + assert(binary_tree != NULL); + print_tree_in_order(binary_tree); +} + +/** + * Registers the PS variant with the polymorphic interface + */ +void +local_runqueue_binary_tree_initialize() +{ + /* Initialize local state */ + local_runqueue_binary_tree = init_binary_tree(true, sandbox_get_priority, sandbox_get_execution_cost, global_worker_thread_idx, 4096); + + worker_binary_trees[global_worker_thread_idx] = local_runqueue_binary_tree; + /* Register Function Pointers for Abstract Scheduling API */ + struct local_runqueue_config config = { .add_fn = local_runqueue_binary_tree_add, + .add_fn_idx = local_runqueue_binary_tree_add_index, + .try_add_fn_idx = local_runqueue_binary_tree_try_add_index, + .is_empty_fn = local_runqueue_binary_tree_is_empty, + .is_empty_fn_idx = local_runqueue_binary_tree_is_empty_index, + .delete_fn = local_runqueue_binary_tree_delete, + .get_next_fn = local_runqueue_binary_tree_get_next, + .get_height_fn = local_runqueue_binary_tree_get_height, + .get_length_fn = local_runqueue_binary_tree_get_length, + .get_length_fn_idx = local_runqueue_binary_tree_get_length_index, + .print_in_order_fn_idx = local_runqueue_print_in_order + }; + + local_runqueue_initialize(&config); +} diff --git a/runtime/src/local_runqueue_circular_queue.c b/runtime/src/local_runqueue_circular_queue.c new file mode 100644 index 000000000..bbfa977fa --- /dev/null +++ b/runtime/src/local_runqueue_circular_queue.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include + +#include "local_runqueue.h" +#include "panic.h" +#include "likely.h" +#include "request_fifo_queue.h" + +extern _Atomic uint32_t local_queue_length[1024]; +extern uint32_t max_local_queue_length[1024]; +extern thread_local int global_worker_thread_idx; +extern struct request_fifo_queue * worker_circular_queue[1024]; +thread_local static struct request_fifo_queue * local_runqueue_circular_queue = NULL; + +/* Add item to the head of the queue */ +void +local_runqueue_circular_queue_add(struct sandbox *sandbox) { + assert(sandbox != NULL); + assert(local_runqueue_circular_queue != NULL); + + uint32_t head = local_runqueue_circular_queue->rqueue_head; + + if (unlikely(head - local_runqueue_circular_queue->rqueue_tail == RQUEUE_QUEUE_LEN)) { + panic("local circular runqueue is full\n"); + } else { + local_runqueue_circular_queue->rqueue[head & (RQUEUE_QUEUE_LEN - 1)] = sandbox; + local_runqueue_circular_queue->rqueue_head++; + } + +} + +/* Called by diaptcher thread to add a new sandbox to the local runqueue */ +void +local_runqueue_circular_queue_add_index(int index, struct sandbox *sandbox){ + assert(sandbox != NULL); + + struct request_fifo_queue * local_runqueue = worker_circular_queue[index]; + assert(local_runqueue != NULL); + uint32_t head = local_runqueue->rqueue_head; + + if (unlikely(head - local_runqueue->rqueue_tail == RQUEUE_QUEUE_LEN)) { + panic("local circular runqueue is full\n"); + } else { + local_runqueue->rqueue[head & (RQUEUE_QUEUE_LEN - 1)] = sandbox; + local_runqueue->rqueue_head++; + } + atomic_fetch_add(&local_queue_length[index], 1); + if (local_queue_length[index] > max_local_queue_length[index]) { + max_local_queue_length[index] = local_queue_length[index]; + } +} + +bool +local_runqueue_circular_queue_is_empty() { + assert(local_runqueue_circular_queue != NULL); + return (local_runqueue_circular_queue->rqueue_head == local_runqueue_circular_queue->rqueue_tail); +} + +/** + * Checks if the run queue is empty + * @returns true if empty. false otherwise + */ +bool +local_runqueue_circular_queue_is_empty_index(int index) +{ + struct request_fifo_queue * local_runqueue = worker_circular_queue[index]; + assert(local_runqueue != NULL); + return (local_runqueue->rqueue_head == local_runqueue->rqueue_tail); +} + +/* Called by worker thread to delete item from the tail of the queue, the to be deleted sandbox must be in the tail */ +void +local_runqueue_circular_queue_delete(struct sandbox *sandbox) { + assert(sandbox != 0); + assert(local_runqueue_circular_queue != NULL); + assert(local_runqueue_circular_queue->rqueue[local_runqueue_circular_queue->rqueue_tail & (RQUEUE_QUEUE_LEN - 1)] + == sandbox); + + local_runqueue_circular_queue->rqueue[local_runqueue_circular_queue->rqueue_tail & (RQUEUE_QUEUE_LEN - 1)] + = NULL; + + local_runqueue_circular_queue->rqueue_tail++; + atomic_fetch_sub(&local_queue_length[global_worker_thread_idx], 1); +} + +/* Called by worker thread to get item from the tail of the queue */ +struct sandbox * local_runqueue_circular_queue_get_next() { + assert(local_runqueue_circular_queue != NULL); + if (local_runqueue_circular_queue->rqueue_head > local_runqueue_circular_queue->rqueue_tail) { + struct sandbox *sandbox = + local_runqueue_circular_queue->rqueue[local_runqueue_circular_queue->rqueue_tail & (RQUEUE_QUEUE_LEN - 1)]; + assert(sandbox != NULL); + return sandbox; + } else { + return NULL; + } +} + +int +local_runqueue_circular_queue_get_length() { + assert(local_runqueue_circular_queue != NULL); + return (local_runqueue_circular_queue->rqueue_head - local_runqueue_circular_queue->rqueue_tail); +} + +int +local_runqueue_circular_queue_get_length_index(int index) { + struct request_fifo_queue * local_runqueue = worker_circular_queue[index]; + assert(local_runqueue != NULL); + return (local_runqueue->rqueue_head - local_runqueue->rqueue_tail); +} + +/** + * Registers the PS variant with the polymorphic interface + */ +void +local_runqueue_circular_queue_initialize() +{ + /* Initialize local state */ + local_runqueue_circular_queue = (struct request_fifo_queue*) malloc(sizeof(struct request_fifo_queue)); + + assert(local_runqueue_circular_queue != NULL); + + local_runqueue_circular_queue->rqueue_tail = 0; + local_runqueue_circular_queue->rqueue_head = 0; + memset(local_runqueue_circular_queue->rqueue, 0, RQUEUE_QUEUE_LEN * sizeof(struct sandbox*)); + + worker_circular_queue[global_worker_thread_idx] = local_runqueue_circular_queue; + + + /* Register Function Pointers for Abstract Scheduling API */ + struct local_runqueue_config config = { .add_fn = local_runqueue_circular_queue_add, + .add_fn_idx = local_runqueue_circular_queue_add_index, + .is_empty_fn = local_runqueue_circular_queue_is_empty, + .is_empty_fn_idx = local_runqueue_circular_queue_is_empty_index, + .delete_fn = local_runqueue_circular_queue_delete, + .get_next_fn = local_runqueue_circular_queue_get_next, + .get_length_fn = local_runqueue_circular_queue_get_length, + .get_length_fn_idx = local_runqueue_circular_queue_get_length_index + }; + + local_runqueue_initialize(&config); +} + + diff --git a/runtime/src/local_runqueue_list.c b/runtime/src/local_runqueue_list.c index 20e19c768..f7306c677 100644 --- a/runtime/src/local_runqueue_list.c +++ b/runtime/src/local_runqueue_list.c @@ -6,7 +6,24 @@ #include "local_runqueue.h" #include "sandbox_functions.h" +extern thread_local int global_worker_thread_idx; +extern _Atomic uint32_t local_queue_length[1024]; +extern uint32_t max_local_queue_length[1024]; +extern struct ps_list_head * worker_fifo_queue[1024]; thread_local static struct ps_list_head local_runqueue_list; +uint32_t runtime_fifo_queue_batch_size = 1; + +int +local_runqueue_list_get_length_index(int index) +{ + return local_queue_length[index]; +} + +int +local_runqueue_list_get_length() +{ + return local_queue_length[global_worker_thread_idx]; +} bool local_runqueue_list_is_empty() @@ -14,6 +31,14 @@ local_runqueue_list_is_empty() return ps_list_head_empty(&local_runqueue_list); } +bool +local_runqueue_list_is_empty_index(int index) +{ + struct ps_list_head *local_queue = worker_fifo_queue[index]; + assert(local_queue != NULL); + return ps_list_head_empty(local_queue); +} + /* Get the sandbox at the head of the thread local runqueue */ struct sandbox * local_runqueue_list_get_head() @@ -21,6 +46,17 @@ local_runqueue_list_get_head() return ps_list_head_first_d(&local_runqueue_list, struct sandbox); } +/** + * Removes the sandbox from the thread-local runqueue + * @param sandbox sandbox + */ +void +local_runqueue_list_remove_nolock(struct sandbox *sandbox_to_remove) +{ + ps_list_rem_d(sandbox_to_remove); + atomic_fetch_sub(&local_queue_length[global_worker_thread_idx], 1); +} + /** * Removes the sandbox from the thread-local runqueue * @param sandbox sandbox @@ -28,7 +64,11 @@ local_runqueue_list_get_head() void local_runqueue_list_remove(struct sandbox *sandbox_to_remove) { + lock_node_t node = {}; + lock_lock(&local_runqueue_list.lock, &node); ps_list_rem_d(sandbox_to_remove); + lock_unlock(&local_runqueue_list.lock, &node); + atomic_fetch_sub(&local_queue_length[global_worker_thread_idx], 1); } struct sandbox * @@ -36,11 +76,13 @@ local_runqueue_list_remove_and_return() { struct sandbox *sandbox_to_remove = ps_list_head_first_d(&local_runqueue_list, struct sandbox); ps_list_rem_d(sandbox_to_remove); + atomic_fetch_sub(&local_queue_length[global_worker_thread_idx], 1); return sandbox_to_remove; } /** - * Append a sandbox to the tail of the runqueue + * Append a sandbox to the tail of the runqueue, only called by the self thread + * Only called by the self thread and pull requests from the GQ, no need lock * @returns the appended sandbox */ void @@ -49,18 +91,59 @@ local_runqueue_list_append(struct sandbox *sandbox_to_append) assert(sandbox_to_append != NULL); assert(ps_list_singleton_d(sandbox_to_append)); ps_list_head_append_d(&local_runqueue_list, sandbox_to_append); + atomic_fetch_add(&local_queue_length[global_worker_thread_idx], 1); + if (local_queue_length[global_worker_thread_idx] > max_local_queue_length[global_worker_thread_idx]) { + max_local_queue_length[global_worker_thread_idx] = local_queue_length[global_worker_thread_idx]; + } +} + +/** + * Append a sandbox to the tail of the runqueue, will be called by the listener thread, need lock + * @returns the appended sandbox + */ +void +local_runqueue_list_append_index(int index, struct sandbox *sandbox_to_append) +{ + struct ps_list_head *local_queue = worker_fifo_queue[index]; + assert(local_queue != NULL); + assert(sandbox_to_append != NULL); + assert(ps_list_singleton_d(sandbox_to_append)); + lock_node_t node = {}; + lock_lock(&local_queue->lock, &node); + ps_list_head_append_d(local_queue, sandbox_to_append); + lock_unlock(&local_queue->lock, &node); + atomic_fetch_add(&local_queue_length[index], 1); + if (local_queue_length[index] > max_local_queue_length[index]) { + max_local_queue_length[index] = local_queue_length[index]; + } } /* Remove sandbox from head of runqueue and add it to tail */ void local_runqueue_list_rotate() { - /* If runqueue is size one, skip round robin logic since tail equals head */ - if (ps_list_head_one_node(&local_runqueue_list)) return; + if (dispatcher == DISPATCHER_LLD) { + /* need lock */ + lock_node_t node = {}; + lock_lock(&local_runqueue_list.lock, &node); + /* If runqueue is size one, skip round robin logic since tail equals head */ + if (ps_list_head_one_node(&local_runqueue_list)) { + lock_unlock(&local_runqueue_list.lock, &node); + return; + } + + struct sandbox *sandbox_at_head = local_runqueue_list_remove_and_return(); + assert(sandbox_at_head->state == SANDBOX_INTERRUPTED); + local_runqueue_list_append(sandbox_at_head); + lock_unlock(&local_runqueue_list.lock, &node); + } else { /* no need lock */ + /* If runqueue is size one, skip round robin logic since tail equals head */ + if (ps_list_head_one_node(&local_runqueue_list)) return; - struct sandbox *sandbox_at_head = local_runqueue_list_remove_and_return(); - assert(sandbox_at_head->state == SANDBOX_INTERRUPTED); - local_runqueue_list_append(sandbox_at_head); + struct sandbox *sandbox_at_head = local_runqueue_list_remove_and_return(); + assert(sandbox_at_head->state == SANDBOX_INTERRUPTED); + local_runqueue_list_append(sandbox_at_head); + } } /** @@ -68,22 +151,43 @@ local_runqueue_list_rotate() * @return the sandbox to execute or NULL if none are available */ struct sandbox * -local_runqueue_list_get_next() +local_runqueue_list_get_next_nolock() { if (local_runqueue_list_is_empty()) return NULL; return local_runqueue_list_get_head(); } +struct sandbox * +local_runqueue_list_get_next() +{ + if (local_runqueue_list_is_empty()) return NULL; + lock_node_t node = {}; + lock_lock(&local_runqueue_list.lock, &node); + struct sandbox *sandbox = local_runqueue_list_get_head(); + lock_unlock(&local_runqueue_list.lock, &node); + return sandbox; +} + void local_runqueue_list_initialize() { ps_list_head_init(&local_runqueue_list); + worker_fifo_queue[global_worker_thread_idx] = &local_runqueue_list; /* Register Function Pointers for Abstract Scheduling API */ - struct local_runqueue_config config = { .add_fn = local_runqueue_list_append, - .is_empty_fn = local_runqueue_list_is_empty, - .delete_fn = local_runqueue_list_remove, - .get_next_fn = local_runqueue_list_get_next }; + struct local_runqueue_config config = { .add_fn = local_runqueue_list_append, + .add_fn_idx = local_runqueue_list_append_index, + .get_length_fn_idx = local_runqueue_list_get_length_index, + .get_length_fn = local_runqueue_list_get_length, + .is_empty_fn = local_runqueue_list_is_empty, + .is_empty_fn_idx = local_runqueue_list_is_empty_index}; + if (dispatcher == DISPATCHER_LLD) { + config.delete_fn = local_runqueue_list_remove; + config.get_next_fn = local_runqueue_list_get_next; + } else {// must be DISPATCHER_TO_GLOBAL_QUEUE + config.delete_fn = local_runqueue_list_remove_nolock; + config.get_next_fn = local_runqueue_list_get_next_nolock; + } local_runqueue_initialize(&config); }; diff --git a/runtime/src/local_runqueue_minheap.c b/runtime/src/local_runqueue_minheap.c index d4d92a09d..cd5be2948 100644 --- a/runtime/src/local_runqueue_minheap.c +++ b/runtime/src/local_runqueue_minheap.c @@ -12,8 +12,17 @@ #include "sandbox_functions.h" #include "runtime.h" +extern struct priority_queue* worker_queues[1024]; +extern _Atomic uint32_t local_queue_length[1024]; +extern uint32_t max_local_queue_length[1024]; +extern thread_local int global_worker_thread_idx; +_Atomic uint64_t worker_queuing_cost[1024]; /* index is thread id, each queue's total execution cost of queuing requests */ +extern struct perf_window * worker_perf_windows[1024]; /* index is thread id, each queue's perf windows, each queue can + have multiple perf windows */ + thread_local static struct priority_queue *local_runqueue_minheap; + /** * Checks if the run queue is empty * @returns true if empty. false otherwise @@ -21,7 +30,20 @@ thread_local static struct priority_queue *local_runqueue_minheap; bool local_runqueue_minheap_is_empty() { - return priority_queue_length_nolock(local_runqueue_minheap) == 0; + return priority_queue_length(local_runqueue_minheap) == 0; +} + +/** + * Checks if the run queue is empty + * @returns true if empty. false otherwise + */ +bool +local_runqueue_minheap_is_empty_index(int index) +{ + struct priority_queue *local_queue = worker_queues[index]; + assert(local_queue != NULL); + + return priority_queue_length(local_queue) == 0;; } /** @@ -32,16 +54,104 @@ local_runqueue_minheap_is_empty() void local_runqueue_minheap_add(struct sandbox *sandbox) { - int return_code = priority_queue_enqueue_nolock(local_runqueue_minheap, sandbox); - if (unlikely(return_code == -ENOSPC)) { - struct priority_queue *temp = priority_queue_grow_nolock(local_runqueue_minheap); - if (unlikely(temp == NULL)) panic("Failed to grow local runqueue\n"); - local_runqueue_minheap = temp; - return_code = priority_queue_enqueue_nolock(local_runqueue_minheap, sandbox); - if (unlikely(return_code == -ENOSPC)) panic("Thread Runqueue is full!\n"); + int return_code = priority_queue_enqueue(local_runqueue_minheap, sandbox); + if (return_code != 0) { + panic("add request to local queue failed, exit\n"); } } +void +local_runqueue_minheap_add_index(int index, struct sandbox *sandbox) +{ + int return_code = priority_queue_enqueue(worker_queues[index], sandbox); + if (return_code != 0) { + panic("add request to local queue failed, exit\n"); + } + + atomic_fetch_add(&local_queue_length[index], 1); + if (local_queue_length[index] > max_local_queue_length[index]) { + max_local_queue_length[index] = local_queue_length[index]; + } + + uint32_t uid = sandbox->route->admissions_info.uid; + + /* Set estimated exeuction time for the sandbox */ + if (runtime_exponential_service_time_simulation_enabled == false) { + uint32_t uid = sandbox->route->admissions_info.uid; + uint64_t estimated_execute_cost = perf_window_get_percentile(&worker_perf_windows[index][uid], + sandbox->route->admissions_info.percentile, + sandbox->route->admissions_info.control_index); + /* Use expected execution time in the configuration file as the esitmated execution time + if estimated_execute_cost is 0 + */ + if (estimated_execute_cost == 0) { + estimated_execute_cost = sandbox->route->expected_execution_cycle; + } + sandbox->estimated_cost = estimated_execute_cost; + sandbox->relative_deadline = sandbox->route->relative_deadline; + } + + /* Record TS and calcuate RS. SRSF algo: + 1. When reqeust arrives to the queue, record TS and calcuate RS. RS = deadline - execution time + 2. When request starts running, update RS + 3. When request stops, update TS + 4. When request resumes, update RS + */ + sandbox->srsf_stop_running_ts = __getcycles(); + sandbox->srsf_remaining_slack = sandbox->relative_deadline - sandbox->estimated_cost; + worker_queuing_cost_increment(index, sandbox->estimated_cost); + +} + +uint32_t +local_runqueue_minheap_try_add_and_get_len_index(int index, struct sandbox *sandbox, bool *need_interrupt) { + struct priority_queue *local_queue = worker_queues[index]; + assert(local_queue != NULL); + + if (priority_queue_length(local_queue) == 0) { + /* The worker is idle */ + *need_interrupt = false; + return 0; + } else if (current_sandboxes[index] != NULL && + current_sandboxes[index]->srsf_remaining_slack > 0 && + sandbox_is_preemptable(current_sandboxes[index]) == true && + sandbox_get_priority(sandbox) < sandbox_get_priority(current_sandboxes[index])) { + /* The new one has a higher priority than the current one, need to interrupt the current one */ + *need_interrupt = true; + return 0; + } else { + /* Current sandbox cannot be interrupted because its priority is higher or its RS is 0, just find + a right location to add the new sandbox to the tree + */ + need_interrupt = false; + return priority_queue_length(local_queue); + } +} + +uint64_t +local_runqueue_minheap_try_add_and_get_load_index(int index, struct sandbox *sandbox, bool *need_interrupt) { + struct priority_queue *local_queue = worker_queues[index]; + assert(local_queue != NULL); + + if (priority_queue_length(local_queue) == 0) { + /* The worker is idle */ + *need_interrupt = false; + return 0; + } else if (current_sandboxes[index] != NULL && + current_sandboxes[index]->srsf_remaining_slack > 0 && + sandbox_is_preemptable(current_sandboxes[index]) == true && + sandbox_get_priority(sandbox) < sandbox_get_priority(current_sandboxes[index])) { + /* The new one has a higher priority than the current one, need to interrupt the current one */ + *need_interrupt = true; + return 0; + } else { + /* Current sandbox cannot be interrupted because its priority is higher or its RS is 0, just find + a right location to add the new sandbox to the tree + */ + need_interrupt = false; + return worker_queuing_cost[index]; + } +} /** * Deletes a sandbox from the runqueue * @param sandbox to delete @@ -51,8 +161,11 @@ local_runqueue_minheap_delete(struct sandbox *sandbox) { assert(sandbox != NULL); - int rc = priority_queue_delete_nolock(local_runqueue_minheap, sandbox); + int rc = priority_queue_delete(local_runqueue_minheap, sandbox); if (rc == -1) panic("Tried to delete sandbox %lu from runqueue, but was not present\n", sandbox->id); + + atomic_fetch_sub(&local_queue_length[global_worker_thread_idx], 1); + worker_queuing_cost_decrement(global_worker_thread_idx, sandbox->estimated_cost); } /** @@ -67,13 +180,27 @@ local_runqueue_minheap_get_next() { /* Get the deadline of the sandbox at the head of the local request queue */ struct sandbox *next = NULL; - int rc = priority_queue_top_nolock(local_runqueue_minheap, (void **)&next); + int rc = priority_queue_top(local_runqueue_minheap, (void **)&next); if (rc == -ENOENT) return NULL; return next; } +int +local_runqueue_minheap_get_len() +{ + return priority_queue_length(local_runqueue_minheap); +} + +int +local_runqueue_minheap_get_len_index(int index) +{ + struct priority_queue *local_queue = worker_queues[index]; + assert(local_queue != NULL); + return priority_queue_length(local_queue); +} + /** * Registers the PS variant with the polymorphic interface */ @@ -81,13 +208,20 @@ void local_runqueue_minheap_initialize() { /* Initialize local state */ - local_runqueue_minheap = priority_queue_initialize(RUNTIME_RUNQUEUE_SIZE, false, sandbox_get_priority); + local_runqueue_minheap = priority_queue_initialize(RUNTIME_RUNQUEUE_SIZE, true, sandbox_get_priority); + worker_queues[global_worker_thread_idx] = local_runqueue_minheap; /* Register Function Pointers for Abstract Scheduling API */ - struct local_runqueue_config config = { .add_fn = local_runqueue_minheap_add, - .is_empty_fn = local_runqueue_minheap_is_empty, - .delete_fn = local_runqueue_minheap_delete, - .get_next_fn = local_runqueue_minheap_get_next }; + struct local_runqueue_config config = { .add_fn = local_runqueue_minheap_add, + .add_fn_idx = local_runqueue_minheap_add_index, + .try_add_and_get_len_fn_t_idx = local_runqueue_minheap_try_add_and_get_len_index, + .try_add_and_get_load_fn_t_idx = local_runqueue_minheap_try_add_and_get_load_index, + .is_empty_fn = local_runqueue_minheap_is_empty, + .is_empty_fn_idx = local_runqueue_minheap_is_empty_index, + .delete_fn = local_runqueue_minheap_delete, + .get_length_fn = local_runqueue_minheap_get_len, + .get_length_fn_idx = local_runqueue_minheap_get_len_index, + .get_next_fn = local_runqueue_minheap_get_next }; local_runqueue_initialize(&config); } diff --git a/runtime/src/local_runqueue_mtds.c b/runtime/src/local_runqueue_mtds.c index 6f7c0ca5c..e4ea3c94e 100644 --- a/runtime/src/local_runqueue_mtds.c +++ b/runtime/src/local_runqueue_mtds.c @@ -53,7 +53,7 @@ local_runqueue_mtds_add(struct sandbox *sandbox) { assert(sandbox != NULL); - struct perworker_tenant_sandbox_queue *pwt = &sandbox->tenant->pwt_sandboxes[worker_thread_idx]; + struct perworker_tenant_sandbox_queue *pwt = &sandbox->tenant->pwt_sandboxes[global_worker_thread_idx]; struct priority_queue *destination_queue = pwt->mt_class == MT_GUARANTEED ? local_runqueue_mtds_guaranteed : local_runqueue_mtds_default; @@ -96,7 +96,7 @@ static void local_runqueue_mtds_delete(struct sandbox *sandbox) { assert(sandbox != NULL); - struct perworker_tenant_sandbox_queue *pwt = &sandbox->tenant->pwt_sandboxes[worker_thread_idx]; + struct perworker_tenant_sandbox_queue *pwt = &sandbox->tenant->pwt_sandboxes[global_worker_thread_idx]; /* Delete the sandbox from the corresponding Per-Worker-Tenant queue */ if (priority_queue_delete_nolock(pwt->sandboxes, sandbox) == -1) { diff --git a/runtime/src/main.c b/runtime/src/main.c index d918abeee..73a4b5e03 100644 --- a/runtime/src/main.c +++ b/runtime/src/main.c @@ -9,6 +9,11 @@ #include #include #include +#include +#include +#include +#include + #ifdef LOG_TO_FILE #include @@ -16,6 +21,7 @@ #include #endif +#include "erpc_c_interface.h" #include "json_parse.h" #include "pretty_print.h" #include "debuglog.h" @@ -25,6 +31,7 @@ #include "sandbox_perf_log.h" #include "sandbox_types.h" #include "scheduler.h" +#include "dispatcher.h" #include "software_interrupt.h" #include "tenant_functions.h" #include "worker_thread.h" @@ -35,12 +42,20 @@ uint32_t runtime_first_worker_processor = 1; uint32_t runtime_processor_speed_MHz = 0; uint32_t runtime_total_online_processors = 0; uint32_t runtime_worker_threads_count = 0; +uint32_t runtime_listener_threads_count = 0; +uint32_t max_possible_workers = 0; +bool first_request_comming = false; + +uint32_t runtime_worker_group_size = 1; enum RUNTIME_SIGALRM_HANDLER runtime_sigalrm_handler = RUNTIME_SIGALRM_HANDLER_BROADCAST; -bool runtime_preemption_enabled = true; -bool runtime_worker_spinloop_pause_enabled = false; -uint32_t runtime_quantum_us = 5000; /* 5ms */ +bool runtime_exponential_service_time_simulation_enabled = false; +bool runtime_autoscaling_enabled = false; +bool runtime_worker_busy_loop_enabled = false; +bool runtime_preemption_enabled = true; +bool runtime_worker_spinloop_pause_enabled = false; +uint32_t runtime_quantum_us = 1000; /* 1ms */ uint64_t runtime_boot_timestamp; pid_t runtime_pid = 0; @@ -60,25 +75,29 @@ runtime_usage(char *cmd) void runtime_allocate_available_cores() { - uint32_t max_possible_workers; - /* Find the number of processors currently online */ runtime_total_online_processors = sysconf(_SC_NPROCESSORS_ONLN); - pretty_print_key_value("Core Count (Online)", "%u\n", runtime_total_online_processors); - /* If more than two cores are available, leave core 0 free to run OS tasks */ - if (runtime_total_online_processors > 2) { - runtime_first_worker_processor = 2; - max_possible_workers = runtime_total_online_processors - 2; - } else if (runtime_total_online_processors == 2) { - runtime_first_worker_processor = 1; - max_possible_workers = runtime_total_online_processors - 1; + char* first_worker_core_id = getenv("SLEDGE_FIRST_WORKER_COREID"); + if (first_worker_core_id != NULL) { + runtime_first_worker_processor = atoi(first_worker_core_id); + assert(runtime_first_worker_processor > 1); + max_possible_workers = runtime_total_online_processors > 2 ? runtime_total_online_processors - 2: + runtime_total_online_processors - 1; } else { - panic("Runtime requires at least two cores!"); + /* If more than two cores are available, leave core 0 free to run OS tasks */ + if (runtime_total_online_processors > 2) { + runtime_first_worker_processor = 2; + max_possible_workers = runtime_total_online_processors - 2; + } else if (runtime_total_online_processors == 2) { + runtime_first_worker_processor = 1; + max_possible_workers = runtime_total_online_processors - 1; + } else { + panic("Runtime requires at least two cores!"); + } } - - + /* Number of Workers */ char *worker_count_raw = getenv("SLEDGE_NWORKERS"); if (worker_count_raw != NULL) { @@ -91,7 +110,7 @@ runtime_allocate_available_cores() runtime_worker_threads_count = max_possible_workers; } - pretty_print_key_value("Listener core ID", "%u\n", LISTENER_THREAD_CORE_ID); + pretty_print_key_value("Listener start core ID", "%u\n", LISTENER_THREAD_START_CORE_ID); pretty_print_key_value("First Worker core ID", "%u\n", runtime_first_worker_processor); pretty_print_key_value("Worker core count", "%u\n", runtime_worker_threads_count); } @@ -146,6 +165,38 @@ runtime_get_processor_speed_MHz(void) panic("Failed to detect processor frequency"); } +static inline void +runtime_get_listener_count() { + /* Number of Listener */ + char *listener_count_raw = getenv("SLEDGE_NLISTENERS"); + if (listener_count_raw != NULL) { + int listener_count = atoi(listener_count_raw); + int max_possible_listeners = max_possible_workers - runtime_worker_threads_count; + + if (listener_count <= 0 || listener_count > max_possible_listeners) { + panic("Invalid Lisenter Count. Was %d. Must be {1..%d}\n", listener_count, max_possible_listeners); + } + + runtime_listener_threads_count = listener_count; + } else { + runtime_listener_threads_count = 1; + } +} + +static inline void +runtime_get_worker_group_size() { + char *worker_group_size_raw = getenv("SLEDGE_WORKER_GROUP_SIZE"); + if (worker_group_size_raw != NULL) { + runtime_worker_group_size = atoi(worker_group_size_raw); + //If scheduler is SCHEDULER_FIFO, there will be one global queue, but might multiple listeners and + //multiple workers, so the listener threads count * worker group size != worker threads count + if (scheduler != SCHEDULER_FIFO) { + assert(runtime_listener_threads_count * runtime_worker_group_size == runtime_worker_threads_count); + } + } else { + printf("Not specify worker group size, default group size is 1\n"); + } +} /** * Controls the behavior of the debuglog macro defined in types.h * If LOG_TO_FILE is defined, close stdin, stdout, stderr, and debuglog writes to a logfile named awesome.log. @@ -173,7 +224,7 @@ runtime_process_debug_log_behavior() void runtime_start_runtime_worker_threads() { - printf("Starting %d worker thread(s)\n", runtime_worker_threads_count); + printf("Starting %d worker thread(s) first worker cpu is %d\n", runtime_worker_threads_count, runtime_first_worker_processor); for (int i = 0; i < runtime_worker_threads_count; i++) { /* Pass the value we want the threads to use when indexing into global arrays of per-thread values */ runtime_worker_threads_argument[i] = i; @@ -193,6 +244,38 @@ runtime_start_runtime_worker_threads() } } +void +runtime_config_validate() +{ + if (dispatcher == DISPATCHER_TO_GLOBAL_QUEUE) { + if (runtime_worker_busy_loop_enabled == false) { + panic("Must enable busy loop if dispatcher is TO_GLOBAL_QUEUE\n"); + + } + + if (disable_get_req_from_GQ == true) { + panic("Must enable get request from GQ if dispatcher is TO_GLOBAL_QUEUE\n"); + } + + if (scheduler != SCHEDULER_FIFO) { + panic("Must use FIFO scheduler if dispatcher is TO_GLOBAL_QUEUE\n"); + } + } else { + if (disable_get_req_from_GQ == false) { + panic("Must disable get requests from GQ if dispatcher is not TO_GLOBAL_QUEUE\n"); + } + } + + //if (dispatcher == DISPATCHER_RR || dispatcher == DISPATCHER_JSQ || dispatcher == DISPATCHER_EDF_INTERRUPT + // || dispatcher == DISPATCHER_SHINJUKU || dispatcher == DISPATCHER_DARC || dispatcher == DISPATCHER_LLD && scheduler == SCHEDULER_EDF) { + if (dispatcher == DISPATCHER_EDF_INTERRUPT || dispatcher == DISPATCHER_SHINJUKU || dispatcher == DISPATCHER_DARC) { + if (runtime_preemption_enabled == true) { + //panic("Must disable preemption if dispatcher is RR, JSQ, EDF_INTERRUPT, or LLD + EDF\n"); + panic("Must disable preemption if dispatcher is SHINJUKU, DARC or EDF_INTERRUPT\n"); + } + } + +} void runtime_configure() { @@ -215,6 +298,33 @@ runtime_configure() } pretty_print_key_value("Scheduler Policy", "%s\n", scheduler_print(scheduler)); + char* disable_get_req_from_global_queue = getenv("SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ"); + if (disable_get_req_from_global_queue != NULL && strcmp(disable_get_req_from_global_queue, "false") != 0) disable_get_req_from_GQ = true; + pretty_print_key_value("Enable getting req from GQ", "%s\n", + disable_get_req_from_GQ == false ? PRETTY_PRINT_GREEN_ENABLED : PRETTY_PRINT_RED_DISABLED); + + /* Dispatcher Policy */ + char *dispatcher_policy = getenv("SLEDGE_DISPATCHER"); + if (dispatcher_policy == NULL) dispatcher_policy = "EDF_INTERRUPT"; + if (strcmp(dispatcher_policy, "DARC") == 0) { + dispatcher = DISPATCHER_DARC; + } else if (strcmp(dispatcher_policy, "EDF_INTERRUPT") == 0) { + dispatcher = DISPATCHER_EDF_INTERRUPT; + } else if (strcmp(dispatcher_policy, "SHINJUKU") == 0) { + dispatcher = DISPATCHER_SHINJUKU; + } else if (strcmp(dispatcher_policy, "TO_GLOBAL_QUEUE") == 0) { + dispatcher = DISPATCHER_TO_GLOBAL_QUEUE; + } else if (strcmp(dispatcher_policy, "RR") == 0) { + dispatcher = DISPATCHER_RR; + } else if (strcmp(dispatcher_policy, "JSQ") == 0) { + dispatcher = DISPATCHER_JSQ; + } else if (strcmp(dispatcher_policy, "LLD") == 0) { + dispatcher = DISPATCHER_LLD; + } else { + panic("Invalid dispatcher policy: %s. Must be {EDF_INTERRUPT|DARC|SHINJUKU|TO_GLOBAL_QUEUE|RR|JSQ|LLD\n", dispatcher_policy); + } + pretty_print_key_value("Dispatcher Policy", "%s\n", dispatcher_print(dispatcher)); + /* Sigalrm Handler Technique */ char *sigalrm_policy = getenv("SLEDGE_SIGALRM_HANDLER"); if (sigalrm_policy == NULL) sigalrm_policy = "BROADCAST"; @@ -234,6 +344,30 @@ runtime_configure() pretty_print_key_value("Preemption", "%s\n", runtime_preemption_enabled ? PRETTY_PRINT_GREEN_ENABLED : PRETTY_PRINT_RED_DISABLED); + /* Runtime Autoscaling */ + char *autoscaling_disable = getenv("SLEDGE_DISABLE_AUTOSCALING"); + if (autoscaling_disable != NULL && strcmp(autoscaling_disable, "true") != 0) runtime_autoscaling_enabled = true; + pretty_print_key_value("Autoscaling", "%s\n", + runtime_autoscaling_enabled ? PRETTY_PRINT_GREEN_ENABLED : PRETTY_PRINT_RED_DISABLED); + + /* Runtime worker busy loop */ + char *worker_busy_loop_disable = getenv("SLEDGE_DISABLE_BUSY_LOOP"); + if (worker_busy_loop_disable != NULL && strcmp(worker_busy_loop_disable, "true") != 0) runtime_worker_busy_loop_enabled = true; + pretty_print_key_value("Worker busy loop", "%s\n", + runtime_worker_busy_loop_enabled ? PRETTY_PRINT_GREEN_ENABLED : PRETTY_PRINT_RED_DISABLED); + + + /* Runtime local FIFO queue batch size */ + char *batch_size = getenv("SLEDGE_FIFO_QUEUE_BATCH_SIZE"); + if (batch_size != NULL) { + runtime_fifo_queue_batch_size = atoi(batch_size); + if (runtime_fifo_queue_batch_size == 0) { + panic("Fifo queue batch size must be larger than 0\n"); + } + } + + /* Check validation */ + runtime_config_validate(); /* Runtime Quantum */ char *quantum_raw = getenv("SLEDGE_QUANTUM_US"); if (quantum_raw != NULL) { @@ -245,21 +379,21 @@ runtime_configure() } pretty_print_key_value("Quantum", "%u us\n", runtime_quantum_us); + /* Runtime exponential service time simulation */ + char *exponential_service_time_simulation_disable = getenv("SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION"); + if (exponential_service_time_simulation_disable != NULL && + strcmp(exponential_service_time_simulation_disable, "false") == 0) { + runtime_exponential_service_time_simulation_enabled = true; + } + + pretty_print_key_value("Exponential_service_time_simulation", " %s\n", + runtime_exponential_service_time_simulation_enabled ? + PRETTY_PRINT_GREEN_ENABLED : PRETTY_PRINT_RED_DISABLED); + sandbox_perf_log_init(); http_session_perf_log_init(); } -void -runtime_configure_worker_spinloop_pause() -{ - /* Runtime Worker-Spinloop-Pause Toggle */ - char *pause_enable = getenv("SLEDGE_SPINLOOP_PAUSE_ENABLED"); - if (pause_enable != NULL && strcmp(pause_enable, "true") == 0) runtime_worker_spinloop_pause_enabled = true; - pretty_print_key_value("Worker-Spinloop-Pause", "%s\n", - runtime_worker_spinloop_pause_enabled ? PRETTY_PRINT_GREEN_ENABLED - : PRETTY_PRINT_RED_DISABLED); -} - void log_compiletime_config() { @@ -459,6 +593,48 @@ load_file_into_buffer(const char *file_name, char **file_buffer) return 0; } +void listener_threads_initialize() { + printf("Starting %d listener thread(s)\n", runtime_listener_threads_count); + for (int i = 0; i < runtime_listener_threads_count; i++) { + listener_thread_initialize(i); + } +} + +int getUri(const char *interface, int port, char *uri) { + struct ifreq ifr; + + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd == -1) { + perror("socket"); + return -1; + } + + // Set the name of the interface + strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1); + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; + + // Get the IP address + if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1) { + perror("ioctl"); + close(sockfd); + return -1; + } + + close(sockfd); + + // Convert the IP address to a string + if (inet_ntop(AF_INET, &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, uri, INET_ADDRSTRLEN) == NULL) { + perror("inet_ntop"); + return -1; + } + + // Append the port to the IP address in the same buffer + int offset = strlen(uri); + snprintf(uri + offset, INET_ADDRSTRLEN - offset + 6, ":%d", port); + + return 0; +} + int main(int argc, char **argv) { @@ -471,6 +647,19 @@ main(int argc, char **argv) printf("Starting the Sledge runtime\n"); + software_interrupt_initialize(); + + /* eRPC init */ + char server_uri[INET_ADDRSTRLEN + 6]; + + if (getUri("ens1f0np0", 31850, server_uri) == 0) { + printf("URI: %s\n", server_uri); + } else { + printf("Failed to get URI.\n"); + } + + erpc_init(server_uri, 0, 0); + log_compiletime_config(); runtime_process_debug_log_behavior(); @@ -480,14 +669,9 @@ main(int argc, char **argv) runtime_allocate_available_cores(); runtime_configure(); runtime_initialize(); - software_interrupt_initialize(); - - listener_thread_initialize(); - runtime_start_runtime_worker_threads(); runtime_get_processor_speed_MHz(); - runtime_configure_worker_spinloop_pause(); - software_interrupt_arm_timer(); + #ifdef LOG_TENANT_LOADING debuglog("Parsing file [%s]\n", argv[1]); #endif @@ -509,27 +693,46 @@ main(int argc, char **argv) panic("Tenant database full!\n"); exit(-1); } - - /* Start listening for requests */ - rc = tenant_listen(tenant); - if (rc < 0) exit(-1); } - runtime_boot_timestamp = __getcycles(); + runtime_get_listener_count(); + runtime_get_worker_group_size(); + /* Set main thread cpu affinity to core #1 */ + cpu_set_t cs; + CPU_ZERO(&cs); + CPU_SET(1, &cs); + int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs); + assert(ret == 0); - for (int tenant_idx = 0; tenant_idx < tenant_config_vec_len; tenant_idx++) { - tenant_config_deinit(&tenant_config_vec[tenant_idx]); - } - free(tenant_config_vec); + runtime_start_runtime_worker_threads(); + software_interrupt_arm_timer(); + + listener_threads_initialize(); + + runtime_boot_timestamp = __getcycles(); for (int i = 0; i < runtime_worker_threads_count; i++) { int ret = pthread_join(runtime_worker_threads[i], NULL); if (ret) { errno = ret; - perror("pthread_join"); + perror("worker pthread_join"); exit(-1); } } + for (int i = 0; i < runtime_listener_threads_count; i++) { + int ret = pthread_join(runtime_listener_threads[i], NULL); + if (ret) { + errno = ret; + perror("listener pthread_join"); + exit(-1); + } + } + + for (int tenant_idx = 0; tenant_idx < tenant_config_vec_len; tenant_idx++) { + tenant_config_deinit(&tenant_config_vec[tenant_idx]); + } + free(tenant_config_vec); + exit(-1); } diff --git a/runtime/src/memlogging.c b/runtime/src/memlogging.c new file mode 100644 index 000000000..2c3e7b703 --- /dev/null +++ b/runtime/src/memlogging.c @@ -0,0 +1,122 @@ +#include +#include "memlogging.h" +#include "panic.h" + +__thread struct mem_logging_obj log_obj; + +/** + * Initialize memory log. This should be called in each worker thread. + * Every worker thread writes log to its own memory. + * @param log_size the number of bytes of memory to allocate for logging + * @param file the FILE pointer to an opened file, each worker thread will + * dump the memory log to this file when sledge exits. The file is + * defined by an environment variable + */ +void +mem_log_init2(uint32_t log_size, FILE* file) +{ +#ifndef LOG_RUNTIME_MEM_LOG + return; +#endif + log_obj.log_size = log_size; + log_obj.logger = (char *)malloc(log_size); + if (!log_obj.logger) { + panic("failed to allocate memory for logging\n"); + } + + log_obj.offset = 0; + log_obj.fout = file; +} + +/** + * Initialize memory log. This should be called in each worker thread. + * Every worker thread writes log to its own memory. + * @param log_size the number of bytes of memory to allocate for logging + * @param file the file to dump the memory log when sledge exits + */ +void +mem_log_init(uint32_t log_size, char const* file) +{ +#ifndef LOG_RUNTIME_MEM_LOG + return; +#endif + log_obj.log_size = log_size; + log_obj.logger = (char *)malloc(log_size); + if (!log_obj.logger) { + panic("failed to allocate memory for logging\n"); + } + + log_obj.offset = 0; + log_obj.fout = fopen(file, "w"); +} + +/** + * Prints log to memory + */ +void +mem_log(char const * fmt, ...) +{ +#ifndef LOG_RUNTIME_MEM_LOG + return; +#endif + assert(log_obj.logger); + if (!log_obj.fout) { + return; + } + + va_list va; + va_start(va, fmt); + int n = vsnprintf(log_obj.logger + log_obj.offset, log_obj.log_size - log_obj.offset, fmt, va); + va_end(va); + + if (n < 0) { + /* Based on the doc of vsnprintf, the write is failed if n is negative */ + panic("failed to write data to memory, return value:%d\n", n); + } else if (n >= log_obj.log_size - log_obj.offset) { + /* Memory is full, realloc memory */ + char* old_logger = log_obj.logger; + + while (n >=log_obj.log_size - log_obj.offset) { + log_obj.logger = (char *)realloc(log_obj.logger, log_obj.log_size * 2); + if (!log_obj.logger) { + log_obj.logger = old_logger; + dump_log_to_file(log_obj); + panic("failed to realloc memory for logging\n"); + } + + log_obj.log_size = log_obj.log_size * 2; + va_start(va, fmt); + n = vsnprintf(log_obj.logger + log_obj.offset, log_obj.log_size - log_obj.offset, fmt, va); + va_end(va); + } + log_obj.offset += n; + } else { + /* Write Success */ + log_obj.offset += n; + } +} + +/** + * Dump log from memory to file. This should be called when a worker thread receives SIGINT signal + */ + +void +dump_log_to_file() +{ +#ifndef LOG_RUNTIME_MEM_LOG + return; +#endif + if (!log_obj.fout) { + return; + } + + assert(log_obj.logger); + + uint32_t write_bytes = 0; + while(write_bytes != log_obj.offset) { + int return_bytes = fprintf(log_obj.fout, "%s", log_obj.logger + write_bytes); + write_bytes += return_bytes; + } + fflush(log_obj.fout); +} + diff --git a/runtime/src/module.c b/runtime/src/module.c index 093106f65..602ba75a6 100644 --- a/runtime/src/module.c +++ b/runtime/src/module.c @@ -44,14 +44,11 @@ module_init(struct module *module, char *path) rc = sledge_abi_symbols_init(&module->abi, path); if (rc != 0) goto err; - module->pools = calloc(runtime_worker_threads_count, sizeof(struct module_pool)); - module->path = path; module->stack_size = ((uint32_t)(round_up_to_page(stack_size == 0 ? WASM_STACK_SIZE : stack_size))); module_alloc_table(module); - module_initialize_pools(module); done: return rc; err: @@ -68,8 +65,6 @@ module_deinit(struct module *module) free(module->path); sledge_abi_symbols_deinit(&module->abi); /* TODO: Free indirect_table */ - module_deinitialize_pools(module); - free(module->pools); } /*************************************** diff --git a/runtime/src/request_typed_deque.c b/runtime/src/request_typed_deque.c new file mode 100644 index 000000000..a8e576ab9 --- /dev/null +++ b/runtime/src/request_typed_deque.c @@ -0,0 +1,188 @@ +#include +#include + +#include "panic.h" +#include "likely.h" +#include "request_typed_deque.h" + +struct request_typed_deque * +request_typed_deque_init(uint8_t type, int size) { + struct request_typed_deque *deque = malloc(sizeof(struct request_typed_deque)); + assert(deque != NULL); + assert(size <= RDEQUE_LEN); + + deque->type = type; + deque->front = -1; + deque->rear = 0; + deque->size = size; + deque->length = 0; + deque->deadline = 0; + + memset(deque->rqueue, 0, RDEQUE_LEN * sizeof(struct sandbox*)); + + return deque; + +} + +// Checks whether request_typed_deque is full or not. +bool isFull(struct request_typed_deque * queue) +{ + assert(queue != NULL); + return ((queue->front == 0 && queue->rear == queue->size - 1) + || queue->front == queue->rear + 1); +} + +// Checks whether request_typed_deque is empty or not. +bool isEmpty(struct request_typed_deque * queue) { + + assert(queue != NULL); + return (queue->front == -1); +} + +// Inserts an element at front +void insertfront(struct request_typed_deque * queue, struct sandbox * sandbox, uint64_t ts) +{ + assert(queue != NULL); + // check whether request_typed_deque if full or not + if (isFull(queue)) { + panic("Request typed deque overflow\n"); + } + + // If queue is initially empty + if (queue->front == -1) { + queue->front = 0; + queue->rear = 0; + } + + // front is at first position of queue + else if (queue->front == 0) + queue->front = queue->size - 1; + + else // decrement front end by '1' + queue->front = queue->front - 1; + + // insert current element into request_typed_deque + queue->rqueue[queue->front] = sandbox; + queue->tsqueue[queue->front] = ts; + queue->length++; +} + +// function to inset element at rear end +// of request_typed_deque. +void insertrear(struct request_typed_deque * queue, struct sandbox * sandbox, uint64_t ts) +{ + assert(queue != NULL); + + if (isFull(queue)) { + panic("Request typed deque overflow\n"); + return; + } + + // If queue is initially empty + if (queue->front == -1) { + queue->front = 0; + queue->rear = 0; + } + + // rear is at last position of queue + else if (queue->rear == queue->size - 1) + queue->rear = 0; + + // increment rear end by '1' + else + queue->rear = queue->rear + 1; + + // insert current element into request_typed_deque + queue->rqueue[queue->rear] = sandbox; + queue->tsqueue[queue->rear] = ts; + queue->length++; +} + +// Deletes element at front end of request_typed_deque +void deletefront(struct request_typed_deque * queue) +{ + assert(queue != NULL); + + // check whether request_typed_deque is empty or not + if (isEmpty(queue)) { + printf("Queue Underflow\n"); + return; + } + + /* reset the front item to NULL */ + queue->rqueue[queue->front] = NULL; + + // request_typed_deque has only one element + if (queue->front == queue->rear) { + queue->front = -1; + queue->rear = -1; + } else { + // back to initial position + if (queue->front == queue->size - 1) { + queue->front = 0; + } else {// increment front by '1' to remove current + // front value from request_typed_deque + queue->front = queue->front + 1; + } + } + queue->length--; +} + + +// Delete element at rear end of request_typed_deque +void deleterear(struct request_typed_deque * queue) +{ + assert(queue != NULL); + if (isEmpty(queue)) { + printf(" Underflow\n"); + return; + } + + /* reset the front item to NULL */ + queue->rqueue[queue->rear] = NULL; + + // request_typed_deque has only one element + if (queue->front == queue->rear) { + queue->front = -1; + queue->rear = -1; + } + else if (queue->rear == 0) + queue->rear = queue->size - 1; + else + queue->rear = queue->rear - 1; + queue->length--; +} + +int getLength(struct request_typed_deque * queue) { + assert(queue != NULL); + return queue->length; +} + +// Returns front element of request_typed_deque +struct sandbox * getFront(struct request_typed_deque * queue, uint64_t * ts) +{ + assert(queue != NULL); + *ts = 0; + // check whether request_typed_deque is empty or not + if (isEmpty(queue)) { + printf(" Underflow\n"); + return NULL; + } + *ts = queue->tsqueue[queue->front]; + return queue->rqueue[queue->front]; +} + +// function return rear element of request_typed_deque +struct sandbox * getRear(struct request_typed_deque * queue, uint64_t * ts) +{ + assert(queue != NULL); + *ts = 0; + // check whether request_typed_deque is empty or not + if (isEmpty(queue) || queue->rear < 0) { + printf(" Underflow\n"); + return NULL; + } + *ts = queue->tsqueue[queue->rear]; + return queue->rqueue[queue->rear]; +} + diff --git a/runtime/src/request_typed_queue.c b/runtime/src/request_typed_queue.c new file mode 100644 index 000000000..52801e7a2 --- /dev/null +++ b/runtime/src/request_typed_queue.c @@ -0,0 +1,53 @@ +#include +#include +#include + +#include "panic.h" +#include "likely.h" +#include "request_typed_queue.h" + +extern thread_local uint32_t current_reserved; +extern thread_local uint8_t dispatcher_thread_idx; + +struct request_typed_queue * +request_typed_queue_init(uint8_t type, uint32_t n_resas) { + struct request_typed_queue *queue = malloc(sizeof(struct request_typed_queue)); + queue->type_group = type; + queue->rqueue_tail = 0; + queue->rqueue_head = 0; + queue->n_resas = n_resas; + for (unsigned int i = 0; i < n_resas; ++i) { + queue->res_workers[i] = current_reserved++; + } + + for (unsigned int i = current_reserved; i < runtime_worker_group_size; i++) { + queue->stealable_workers[queue->n_stealable++] = i; + } + + if (queue->n_stealable == 0) { + printf("Listener %u reserve %u cores (from %u to %u) to group id %u, can steal 0 core\n", + dispatcher_thread_idx, n_resas, queue->res_workers[0], queue->res_workers[n_resas-1], type); + } else { + printf("Listener %u reserve %u cores (from %u to %u) to group id %u, can steal cores %u(from %u to %u)\n", + dispatcher_thread_idx, n_resas, queue->res_workers[0], queue->res_workers[n_resas-1], type, + queue->n_stealable, queue->stealable_workers[0], queue->stealable_workers[queue->n_stealable-1]); + } + memset(queue->rqueue, 0, RQUEUE_LEN * sizeof(struct sandbox*)); + + return queue; + +} + +int push_to_rqueue(struct sandbox *sandbox, struct request_typed_queue *rtype, uint64_t tsc) { + assert(sandbox != NULL); + + if (unlikely(rtype->rqueue_head - rtype->rqueue_tail == RQUEUE_LEN)) { + panic("Dispatcher dropped request as group type %d because queue is full\n", rtype->type_group); + return -1; + } else { + rtype->tsqueue[rtype->rqueue_head & (RQUEUE_LEN - 1)] = tsc; + rtype->rqueue[rtype->rqueue_head++ & (RQUEUE_LEN - 1)] = sandbox; + return 0; + } +} + diff --git a/runtime/src/route_config.c b/runtime/src/route_config.c new file mode 100644 index 000000000..b09b23b5c --- /dev/null +++ b/runtime/src/route_config.c @@ -0,0 +1,3 @@ +#include "route_config.h" + +int groups[route_config_member_len] = { 0 }; // record reserved cpu cores per group, key is group id diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index e2c7918e2..05941ead6 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -24,14 +24,23 @@ #include "software_interrupt.h" #include "sandbox_perf_log.h" +#define WAKEUP_THREAD_OVERHEAD 5 /* 5us */ + /*************************** * Shared Process State * **************************/ +/* Count of the total number of requests we've ever received. Never decrements as it is used to dispatch requests to workers with RR */ +_Atomic uint64_t request_index; pthread_t *runtime_worker_threads; +pthread_t *runtime_listener_threads; int *runtime_worker_threads_argument; +int *runtime_listener_threads_argument; /* The active deadline of the sandbox running on each worker thread */ uint64_t *runtime_worker_threads_deadline; +uint64_t wakeup_thread_cycles; + +struct memory_pool *memory_pools = NULL; /****************************************** * Shared Process / Listener Thread Logic * @@ -45,9 +54,18 @@ runtime_cleanup() if (runtime_worker_threads_deadline) free(runtime_worker_threads_deadline); if (runtime_worker_threads_argument) free(runtime_worker_threads_argument); - if (runtime_worker_threads) free(runtime_worker_threads); + if (runtime_worker_threads) { + free(runtime_worker_threads); + runtime_worker_threads = NULL; + } + if (runtime_listener_threads) { + free(runtime_listener_threads); + runtime_listener_threads = NULL; + } software_interrupt_cleanup(); + runtime_deinitialize_pools(); + free(memory_pools); exit(EXIT_SUCCESS); } @@ -104,9 +122,25 @@ runtime_initialize(void) assert(runtime_worker_threads_deadline != NULL); memset(runtime_worker_threads_deadline, UINT8_MAX, runtime_worker_threads_count * sizeof(uint64_t)); + runtime_listener_threads = calloc(runtime_listener_threads_count, sizeof(pthread_t)); + assert(runtime_listener_threads != NULL); + runtime_listener_threads_argument = calloc(runtime_listener_threads_count, sizeof(int)); + assert(runtime_listener_threads_argument != NULL); + http_total_init(); sandbox_total_initialize(); + request_index_initialize(); sandbox_state_totals_initialize(); + worker_queuing_cost_initialize(); + + memory_pools = aligned_alloc(128, runtime_worker_threads_count * sizeof(struct memory_pool)); + if (!memory_pools) { + perror("aligned_alloc failed"); + exit(1); + } + memset(memory_pools, 0, runtime_worker_threads_count * sizeof(struct memory_pool)); + //memory_pools = calloc(runtime_worker_threads_count, sizeof(struct memory_pool)); + runtime_initialize_pools(); /* Setup Scheduler */ scheduler_initialize(); @@ -114,11 +148,11 @@ runtime_initialize(void) /* Configure Signals */ signal(SIGPIPE, SIG_IGN); signal(SIGTERM, runtime_cleanup); - signal(SIGINT, runtime_cleanup); signal(SIGQUIT, runtime_cleanup); http_parser_settings_initialize(); admissions_control_initialize(); + wakeup_thread_cycles = WAKEUP_THREAD_OVERHEAD * runtime_processor_speed_MHz; } static void diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index 48b74fca1..769675049 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -15,6 +15,9 @@ #include "wasm_memory.h" #include "wasm_stack.h" +extern bool runtime_exponential_service_time_simulation_enabled; +extern thread_local int group_worker_thread_idx; + _Atomic uint64_t sandbox_total = 0; static inline void @@ -72,12 +75,32 @@ sandbox_free_globals(struct sandbox *sandbox) wasm_globals_deinit(&sandbox->globals); } -static inline void +static inline uint64_t sandbox_free_stack(struct sandbox *sandbox) { assert(sandbox); - return module_free_stack(sandbox->module, sandbox->stack); + //return module_free_stack(sandbox->module, sandbox->stack); + //set SP to sandbox->stack + sandbox->stack->sp = (uint8_t*)sandbox->ctxt.regs[UREG_SP]; + uint64_t t = module_free_stack(sandbox->module, sandbox->stack); + return t; +} + + +static inline int +sandbox_init_response_body(struct sandbox *sandbox) +{ + assert(sandbox != NULL); + assert(sandbox->response_body.data == NULL); + assert(sandbox->response_body.size == 0); + + int rc = auto_buf_init(&sandbox->response_body); + if (rc < 0) { + return -1; + } + + return 0; } /** @@ -87,14 +110,17 @@ sandbox_free_stack(struct sandbox *sandbox) */ int sandbox_prepare_execution_environment(struct sandbox *sandbox) -{ +{ assert(sandbox != NULL); + sandbox->global_worker_thread_idx = global_worker_thread_idx; + sandbox->group_worker_thread_idx = group_worker_thread_idx; + char *error_message = ""; int rc; - rc = http_session_init_response_body(sandbox->http); + rc = sandbox_init_response_body(sandbox); if (rc < 0) { error_message = "failed to allocate response body"; goto err_globals_allocation_failed; @@ -137,7 +163,7 @@ sandbox_prepare_execution_environment(struct sandbox *sandbox) void sandbox_init(struct sandbox *sandbox, struct module *module, struct http_session *session, struct route *route, - struct tenant *tenant, uint64_t admissions_estimate) + struct tenant *tenant, uint64_t admissions_estimate, void *rpc_handler, uint8_t rpc_id) { /* Sets the ID to the value before the increment */ sandbox->id = sandbox_total_postfix_increment(); @@ -148,12 +174,19 @@ sandbox_init(struct sandbox *sandbox, struct module *module, struct http_session ps_list_init_d(sandbox); /* Allocate HTTP session structure */ - assert(session); sandbox->http = session; sandbox->tenant = tenant; sandbox->route = route; + sandbox->rpc_handler = rpc_handler; + sandbox->rpc_id = rpc_id; + sandbox->rpc_request_body = NULL; + sandbox->rpc_request_body_size = 0; + sandbox->cursor = 0; sandbox->absolute_deadline = sandbox->timestamp_of.allocation + sandbox->route->relative_deadline; + sandbox->relative_deadline = sandbox->route->relative_deadline; + sandbox->global_worker_thread_idx = -1; + sandbox->group_worker_thread_idx = -1; /* * Admissions Control State @@ -176,7 +209,7 @@ sandbox_init(struct sandbox *sandbox, struct module *module, struct http_session */ struct sandbox * sandbox_alloc(struct module *module, struct http_session *session, struct route *route, struct tenant *tenant, - uint64_t admissions_estimate) + uint64_t admissions_estimate, void *req_handle, uint8_t rpc_id) { size_t alignment = (size_t)PAGE_SIZE; size_t size_to_alloc = (size_t)round_up_to_page(sizeof(struct sandbox)); @@ -190,19 +223,29 @@ sandbox_alloc(struct module *module, struct http_session *session, struct route memset(sandbox, 0, size_to_alloc); sandbox_set_as_allocated(sandbox); - sandbox_init(sandbox, module, session, route, tenant, admissions_estimate); + sandbox_init(sandbox, module, session, route, tenant, admissions_estimate, req_handle, rpc_id); return sandbox; } void -sandbox_deinit(struct sandbox *sandbox) +sandbox_deinit(struct sandbox *sandbox, uint64_t *ret) { assert(sandbox != NULL); assert(sandbox != current_sandbox_get()); assert(sandbox->state == SANDBOX_ERROR || sandbox->state == SANDBOX_COMPLETE); + uint64_t now = __getcycles(); + auto_buf_deinit(&sandbox->response_body); + uint64_t d1 = __getcycles(); + ret[0] = d1 - now; + if (sandbox->rpc_request_body) { + free(sandbox->rpc_request_body); + sandbox->rpc_request_body = NULL; + } + uint64_t d2 = __getcycles(); + ret[1] = d2 - d1; /* Assumption: HTTP session was migrated to listener core */ assert(sandbox->http == NULL); @@ -211,22 +254,45 @@ sandbox_deinit(struct sandbox *sandbox) /* Linear Memory and Guard Page should already have been munmaped and set to NULL */ assert(sandbox->memory == NULL); - if (likely(sandbox->stack != NULL)) sandbox_free_stack(sandbox); + if (likely(sandbox->stack != NULL)) ret[2] = sandbox_free_stack(sandbox); + uint64_t d3 = __getcycles(); + //ret[2] = d3 - d2; if (likely(sandbox->globals.buffer != NULL)) sandbox_free_globals(sandbox); + uint64_t d4 = __getcycles(); + ret[3] = d4 - d3; if (likely(sandbox->wasi_context != NULL)) wasi_context_destroy(sandbox->wasi_context); + uint64_t d5 = __getcycles(); + ret[4] = d5 - d4; } /** * Free stack and heap resources.. also any I/O handles. * @param sandbox */ -void -sandbox_free(struct sandbox *sandbox) +uint64_t +sandbox_free(struct sandbox *sandbox, uint64_t *ret) { assert(sandbox != NULL); assert(sandbox != current_sandbox_get()); assert(sandbox->state == SANDBOX_ERROR || sandbox->state == SANDBOX_COMPLETE); - - sandbox_deinit(sandbox); + + uint64_t now = __getcycles(); + sandbox_deinit(sandbox, ret); + uint64_t d = __getcycles() - now; free(sandbox); + return d; +} + +void sandbox_send_response(struct sandbox *sandbox, uint8_t response_code) { + if (response_code == 0) { + uint64_t pure_cpu_time = (sandbox->duration_of_state[SANDBOX_RUNNING_SYS] + sandbox->duration_of_state[SANDBOX_RUNNING_USER]) + / runtime_processor_speed_MHz; + char tmp_buf[20] = {0}; + sprintf(tmp_buf, "%lu", pure_cpu_time); + erpc_req_response_enqueue(sandbox->rpc_id, sandbox->rpc_handler, tmp_buf, strlen(tmp_buf), response_code); + } else { + auto_buf_flush(&sandbox->response_body); + erpc_req_response_enqueue(sandbox->rpc_id, sandbox->rpc_handler, + sandbox->response_body.data, sandbox->response_body.size, response_code); + } } diff --git a/runtime/src/scheduler.c b/runtime/src/scheduler.c index e6c67b135..6f6b67b7b 100644 --- a/runtime/src/scheduler.c +++ b/runtime/src/scheduler.c @@ -1,3 +1,4 @@ #include "scheduler.h" enum SCHEDULER scheduler = SCHEDULER_EDF; +bool disable_get_req_from_GQ = false; diff --git a/runtime/src/sledge_abi.c b/runtime/src/sledge_abi.c index 6d1424ad5..86daa4fa4 100644 --- a/runtime/src/sledge_abi.c +++ b/runtime/src/sledge_abi.c @@ -1163,3 +1163,8 @@ sledge_abi__scratch_storage_upsert(uint32_t key_offset, uint32_t key_len, uint32 sandbox_return(sandbox); } + +EXPORT uint64_t +sledge_abi__env_getcycles() { + return __getcycles(); +} diff --git a/runtime/src/software_interrupt.c b/runtime/src/software_interrupt.c index e263900ad..00ef2a6c6 100644 --- a/runtime/src/software_interrupt.c +++ b/runtime/src/software_interrupt.c @@ -24,7 +24,29 @@ #include "scheduler.h" #include "software_interrupt.h" #include "software_interrupt_counts.h" - +#include "memlogging.h" +#include "tenant_functions.h" + +extern sem_t semlock[1024]; +extern uint32_t local_runqueue_count[1024]; +extern thread_local uint8_t dispatcher_thread_idx; +extern uint32_t worker_old_sandbox[1024]; +extern uint32_t worker_new_sandbox[1024]; +extern thread_local uint64_t total_requests; +extern uint32_t max_local_queue_length[1024]; +extern uint32_t max_local_queue_height[1024]; +extern thread_local uint32_t max_queue_length; +extern thread_local uint32_t dispatcher_try_interrupts; +thread_local uint32_t interrupts = 0; +thread_local uint32_t preemptable_interrupts = 0; + +extern struct sandbox* current_sandboxes[1024]; +extern time_t t_start; +extern thread_local int global_worker_thread_idx; +extern thread_local bool pthread_stop; +extern thread_local bool is_listener; +extern thread_local uint32_t total_complete_requests; +extern uint64_t requests_counter[MAX_DISPATCHER][MAX_REQUEST_TYPE]; thread_local _Atomic volatile sig_atomic_t handler_depth = 0; thread_local _Atomic volatile sig_atomic_t deferred_sigalrm = 0; @@ -33,10 +55,18 @@ thread_local _Atomic volatile sig_atomic_t deferred_sigalrm = 0; **************************************/ extern pthread_t *runtime_worker_threads; +extern pthread_t *runtime_listener_threads; + +#ifdef SANDBOX_STATE_TOTALS +extern _Atomic uint32_t sandbox_state_totals[SANDBOX_STATE_COUNT]; +#endif /************************** * Private Static Inlines * *************************/ +void perf_window_print_mean() { + tenant_database_foreach(tenat_perf_window_print_mean, NULL, NULL); +} /** * A POSIX signal is delivered to only one thread. @@ -75,6 +105,34 @@ propagate_sigalrm(siginfo_t *signal_info) } } +/** + * A POSIX signal is delivered to only one thread. + * This function broadcasts the sigint signal to all other worker threads + */ +static inline void +sigint_propagate_workers_listener(siginfo_t *signal_info) +{ + /* Signal was sent directly by the kernel user space, so forward to other threads */ + if (signal_info->si_code == SI_KERNEL || signal_info->si_code == SI_USER) { + for (int i = 0; i < runtime_worker_threads_count; i++) { + if (pthread_self() == runtime_worker_threads[i]) continue; + + /* All threads should have been initialized */ + assert(runtime_worker_threads[i] != 0); + pthread_kill(runtime_worker_threads[i], SIGINT); + } + for (int i = 0; i < runtime_listener_threads_count;i++) { + if (pthread_self() == runtime_listener_threads[i]) continue; + + assert(runtime_listener_threads[i] != 0); + pthread_kill(runtime_listener_threads[i], SIGINT); + } + } else { + /* Signal forwarded from another thread. Just confirm it resulted from pthread_kill */ + assert(signal_info->si_code == SI_TKILL); + } +} + static inline bool worker_thread_is_running_cooperative_scheduler(void) { @@ -82,13 +140,22 @@ worker_thread_is_running_cooperative_scheduler(void) } -static inline bool +bool current_sandbox_is_preemptable() { struct sandbox *sandbox = current_sandbox_get(); return sandbox != NULL && sandbox->state == SANDBOX_RUNNING_USER; } +bool +sandbox_is_preemptable(void *sandbox) { + return sandbox != NULL && ((struct sandbox *)sandbox)->state == SANDBOX_RUNNING_USER; +} + +void preempt_worker(int thread_id) { + pthread_kill(runtime_worker_threads[thread_id], SIGALRM); +} + /** * The handler function for Software Interrupts (signals) * SIGALRM is executed periodically by an interval timer, causing preemption of the current sandbox @@ -112,7 +179,7 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void switch (signal_type) { case SIGALRM: { - assert(runtime_preemption_enabled); + //assert(runtime_preemption_enabled); if (worker_thread_is_running_cooperative_scheduler()) { /* There is no benefit to deferring SIGALRMs that occur when we are already in the cooperative @@ -121,15 +188,23 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void /* Global tenant promotions */ global_timeout_queue_process_promotions(); } - propagate_sigalrm(signal_info); + if (runtime_preemption_enabled) { + propagate_sigalrm(signal_info); + } } else if (current_sandbox_is_preemptable()) { + preemptable_interrupts++; /* Preemptable, so run scheduler. The scheduler handles outgoing state changes */ + /* sandbox_interrupt means the sandbox stopped, but might be resume very soon. If + deciding preempt the sandbox for a while, then will call sandbox_preempt */ sandbox_interrupt(current_sandbox); if (scheduler == SCHEDULER_MTDS && signal_info->si_code == SI_KERNEL) { /* Global tenant promotions */ global_timeout_queue_process_promotions(); } - propagate_sigalrm(signal_info); + + if (runtime_preemption_enabled) { + propagate_sigalrm(signal_info); + } scheduler_preemptive_sched(interrupted_context); } else { /* We transition the sandbox to an interrupted state to exclude time propagating signals and @@ -138,14 +213,17 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void /* Global tenant promotions */ global_timeout_queue_process_promotions(); } - propagate_sigalrm(signal_info); + + if (runtime_preemption_enabled) { + propagate_sigalrm(signal_info); + } atomic_fetch_add(&deferred_sigalrm, 1); } break; } case SIGUSR1: { - assert(runtime_preemption_enabled); + //assert(runtime_preemption_enabled); assert(current_sandbox); assert(current_sandbox->state == SANDBOX_PREEMPTED); assert(current_sandbox->ctxt.variant == ARCH_CONTEXT_VARIANT_SLOW); @@ -184,6 +262,55 @@ software_interrupt_handle_signals(int signal_type, siginfo_t *signal_info, void break; } + case SIGINT: { /* Stop the alarm timer first */ software_interrupt_disarm_timer(); + sigint_propagate_workers_listener(signal_info); + time_t t_end = time(NULL); + double seconds = difftime(t_end, t_start); + + if (is_listener) { + pthread_stop = true; + double arriving_rate = total_requests / seconds; + printf("%d try preempts:%u max global queue %u arriving rate %f total requests %lu\n", dispatcher_thread_idx, + dispatcher_try_interrupts, max_queue_length, arriving_rate, total_requests); + for (int i = 0; i < MAX_REQUEST_TYPE; i++) { + if (requests_counter[dispatcher_thread_idx][i] != 0) { + mem_log("type %d total requests %lu\n", i, requests_counter[dispatcher_thread_idx][i]); + } + } + //mem_log("%d try preempts:%u max global queue %u arriving rate %f total requests %lu\n", dispatcher_thread_idx, + // dispatcher_try_interrupts, max_queue_length, arriving_rate, total_requests); + dump_log_to_file(); + break; + } + + /* calculate the throughput */ + double throughput = atomic_load(&sandbox_state_totals[SANDBOX_COMPLETE]) / seconds; + uint32_t total_sandboxes_error = atomic_load(&sandbox_state_totals[SANDBOX_ERROR]); + mem_log("throughput %f tid(%d) error request %u complete requests %u total request %u total_complete_requests %u interrupts %u p-interrupts %u max local queue %u\n", + throughput, global_worker_thread_idx, total_sandboxes_error, atomic_load(&sandbox_state_totals[SANDBOX_COMPLETE]), + atomic_load(&sandbox_state_totals[SANDBOX_ALLOCATED]), total_complete_requests, interrupts, preemptable_interrupts, + max_local_queue_length[global_worker_thread_idx]); + dump_log_to_file(); + //printf("id %d max local queue %u height %u new %u old %u current length %u real length %d total complete request %u\n", + // global_worker_thread_idx, max_local_queue_length[global_worker_thread_idx],max_local_queue_height[global_worker_thread_idx], + // worker_new_sandbox[global_worker_thread_idx], worker_old_sandbox[global_worker_thread_idx], + // local_runqueue_count[global_worker_thread_idx], + // local_runqueue_get_length(), total_complete_requests); + printf("id %d max local queue %u new %u old %u current length %u real length %d total complete request %u\n", + global_worker_thread_idx, max_local_queue_length[global_worker_thread_idx], + worker_new_sandbox[global_worker_thread_idx], worker_old_sandbox[global_worker_thread_idx], + local_runqueue_count[global_worker_thread_idx], + local_runqueue_get_length(), total_complete_requests); + pthread_stop = true; + if (!runtime_worker_busy_loop_enabled) { + /* Wake up worker so it can check if pthread_stop is true, othewise, it will block at condition wait */ + wakeup_worker(global_worker_thread_idx); + sem_post(&semlock[global_worker_thread_idx]); + } + /* Use pthread_exit in case some function code is an infinite loop and cannot finish forever */ + //pthread_exit(NULL); + break; + } default: { const char *signal_name = strsignal(signal_type); switch (signal_info->si_code) { @@ -268,9 +395,10 @@ software_interrupt_initialize(void) sigaddset(&signal_action.sa_mask, SIGUSR1); sigaddset(&signal_action.sa_mask, SIGFPE); sigaddset(&signal_action.sa_mask, SIGSEGV); + sigaddset(&signal_action.sa_mask, SIGINT); - const int supported_signals[] = { SIGALRM, SIGUSR1, SIGFPE, SIGSEGV }; - const size_t supported_signals_len = 4; + const int supported_signals[] = { SIGALRM, SIGUSR1, SIGFPE, SIGSEGV, SIGINT }; + const size_t supported_signals_len = 5; for (int i = 0; i < supported_signals_len; i++) { int signal = supported_signals[i]; diff --git a/runtime/src/tenant_database.c b/runtime/src/tenant_database.c index db9098023..b4e6eb188 100644 --- a/runtime/src/tenant_database.c +++ b/runtime/src/tenant_database.c @@ -8,7 +8,8 @@ * Tenant Database * ******************/ -struct tenant *tenant_database[RUNTIME_MAX_TENANT_COUNT] = { NULL }; +struct tenant *tenant_database[RUNTIME_MAX_TENANT_COUNT] = { NULL }; // the index is UDP port +struct tenant *tenant_index_database[RUNTIME_MAX_TENANT_COUNT] = { NULL }; // the index is index size_t tenant_database_count = 0; /** @@ -24,7 +25,9 @@ tenant_database_add(struct tenant *tenant) int rc; if (tenant_database_count == RUNTIME_MAX_TENANT_COUNT) goto err_no_space; - tenant_database[tenant_database_count++] = tenant; + tenant_database[tenant->port] = tenant; + tenant_index_database[tenant_database_count] = tenant; + tenant_database_count++; rc = 0; done: @@ -74,11 +77,7 @@ tenant_database_find_by_socket_descriptor(int socket_descriptor) struct tenant * tenant_database_find_by_port(uint16_t port) { - for (size_t i = 0; i < tenant_database_count; i++) { - assert(tenant_database[i]); - if (tenant_database[i]->tcp_server.port == port) return tenant_database[i]; - } - return NULL; + return tenant_database[port]; } /** @@ -95,10 +94,10 @@ tenant_database_find_by_ptr(void *ptr) } void -tenant_database_foreach(void (*cb)(struct tenant *, void *), void *arg) +tenant_database_foreach(void (*cb)(struct tenant *, void *), void *arg, void *arg2) { for (size_t i = 0; i < tenant_database_count; i++) { - assert(tenant_database[i]); - cb(tenant_database[i], arg); + assert(tenant_index_database[i]); + cb(tenant_index_database[i], arg); } } diff --git a/runtime/src/worker_thread.c b/runtime/src/worker_thread.c index 0b8d86f31..70a919ee5 100644 --- a/runtime/src/worker_thread.c +++ b/runtime/src/worker_thread.c @@ -7,6 +7,7 @@ #include #include "current_sandbox.h" +#include "memlogging.h" #include "local_cleanup_queue.h" #include "local_runqueue.h" #include "local_runqueue_list.h" @@ -17,16 +18,35 @@ #include "worker_thread.h" #include "tenant_functions.h" #include "priority_queue.h" +#include "local_preempted_fifo_queue.h" /*************************** * Worker Thread State * **************************/ +extern pthread_mutex_t mutexs[1024]; +extern pthread_cond_t conds[1024]; +extern sem_t semlock[1024]; + +_Atomic uint32_t local_queue_length[1024] = {0}; +uint32_t max_local_queue_length[1024] = {0}; +uint32_t max_local_queue_height[1024] = {0}; +extern struct perf_window * worker_perf_windows[1024]; // index is worker id +thread_local struct perf_window perf_window_per_thread[1024]; // index is route unique id +struct sandbox* current_sandboxes[1024] = { NULL }; +extern uint32_t runtime_worker_group_size; + +extern FILE *sandbox_perf_log; +thread_local bool pthread_stop = false; +thread_local int dispatcher_id; /* context of the runtime thread before running sandboxes or to resume its "main". */ thread_local struct arch_context worker_thread_base_context; /* Used to index into global arguments and deadlines arrays */ -thread_local int worker_thread_idx; +thread_local int global_worker_thread_idx; + +/* Mark the worker index within the group */ +thread_local int group_worker_thread_idx; /* Used to track tenants' timeouts */ thread_local struct priority_queue *worker_thread_timeout_queue; @@ -34,6 +54,27 @@ thread_local struct priority_queue *worker_thread_timeout_queue; * Worker Thread Logic * **********************/ +/** + */ + +void preallocate_memory() { + tenant_database_foreach(tenant_preallocate_memory, NULL, NULL); +} + +void perf_window_init() { + worker_perf_windows[global_worker_thread_idx] = perf_window_per_thread; + tenant_database_foreach(tenant_perf_window_init, NULL, NULL); +} + +void condition_variable_init() { + pthread_mutex_init(&mutexs[global_worker_thread_idx], NULL); + pthread_cond_init(&conds[global_worker_thread_idx], NULL); +} + +void semaphore_init(){ + sem_init(&semlock[global_worker_thread_idx], 0, 0); +} + /** * The entry function for sandbox worker threads * Initializes thread-local state, unmasks signals, sets up epoll loop and @@ -42,17 +83,35 @@ thread_local struct priority_queue *worker_thread_timeout_queue; void * worker_thread_main(void *argument) { + pthread_setname_np(pthread_self(), "worker"); /* Set base context as running */ worker_thread_base_context.variant = ARCH_CONTEXT_VARIANT_RUNNING; /* Index was passed via argument */ - worker_thread_idx = *(int *)argument; + global_worker_thread_idx = *(int *)argument; + + atomic_init(&local_queue_length[global_worker_thread_idx], 0); + /* Set dispatcher id for this worker */ + dispatcher_id = global_worker_thread_idx / runtime_worker_group_size; + group_worker_thread_idx = global_worker_thread_idx - dispatcher_id * runtime_worker_group_size; + + printf("global thread %d's dispatcher id is %d group size is %d group id is %d\n", global_worker_thread_idx, + dispatcher_id, runtime_worker_group_size, group_worker_thread_idx); /* Set my priority */ // runtime_set_pthread_prio(pthread_self(), 2); pthread_setschedprio(pthread_self(), -20); + preallocate_memory(); + perf_window_init(); + condition_variable_init(); + semaphore_init(); + scheduler_runqueue_initialize(); + local_preempted_fifo_queue_init(); + + /* Initialize memory logging, set 100M memory for logging */ + mem_log_init2(1024*1024*1024, sandbox_perf_log); /* Initialize Cleanup Queue */ local_cleanup_queue_initialize(); @@ -65,13 +124,15 @@ worker_thread_main(void *argument) software_interrupt_unmask_signal(SIGFPE); software_interrupt_unmask_signal(SIGSEGV); + /* Unmask SIGINT signals */ + software_interrupt_unmask_signal(SIGINT); + /* Unmask signals, unless the runtime has disabled preemption */ - if (runtime_preemption_enabled) { - software_interrupt_unmask_signal(SIGALRM); - software_interrupt_unmask_signal(SIGUSR1); - } + software_interrupt_unmask_signal(SIGALRM); + software_interrupt_unmask_signal(SIGUSR1); scheduler_idle_loop(); - panic("Worker Thread unexpectedly completed idle loop."); + //panic("Worker Thread unexpectedly completed idle loop."); + return NULL; } diff --git a/runtime/tests/add_partition.sh b/runtime/tests/add_partition.sh new file mode 100755 index 000000000..f150eb684 --- /dev/null +++ b/runtime/tests/add_partition.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +setVim(){ +echo "hi comment ctermfg=3 +set hlsearch +if has(\"autocmd\") + au BufReadPost * if line(\"'\\\"\") > 1 && line(\"'\\\"\") <= line(\"$\") | exe \"normal! g'\\\"\" | endif +endif +" > ~/.vimrc +} + +setVim + +#remember github username and password +git config --global credential.helper store + +do_partition() { +echo "doing partition" +echo "n +p +1 ++400G +w +" | sudo fdisk /dev/sdb && sleep 4 && sudo mkfs.ext4 /dev/sdb1 +} + +add_partition() { + has_sdb=`sudo fdisk -l /dev/sdb|grep "/dev/sdb: 1.9 TiB"` + no_partition=`sudo fdisk -l /dev/sdb|grep "Device"` + #echo $has_sdb + if [[ $has_sdb == *"Disk /dev/sdb: 1.9 TiB"* ]]; then + if [ -z "$no_partition" ]; + then + do_partition + else + echo "/dev/sdb already has paritions" + exit + fi + else + echo "no /dev/sdb or its capacity is not 1.1 TiB" + exit + fi +} + +mount_partition() { + sudo mkdir /my_mount + sudo mount /dev/sda4 /my_mount + df -h +} +#add_partition +sudo mkfs.ext4 /dev/sda4 +mount_partition +sudo chmod 777 /my_mount diff --git a/runtime/tests/applications_Makefile_patch b/runtime/tests/applications_Makefile_patch new file mode 100644 index 000000000..efe3f7952 --- /dev/null +++ b/runtime/tests/applications_Makefile_patch @@ -0,0 +1,30 @@ +diff --git a/applications/Makefile b/applications/Makefile +index 1ec969c..08cd49c 100644 +--- a/applications/Makefile ++++ b/applications/Makefile +@@ -21,6 +21,9 @@ dist: + all: \ + cifar10.install \ + empty.install \ ++ sift.install \ ++ hash.install \ ++ multi_ncut.install \ + fibonacci.install \ + gocr.install \ + gps_ekf.install \ +@@ -90,6 +93,15 @@ exit.install: ../runtime/bin/exit.wasm.so + .PHONY: fibonacci.install + fibonacci.install: ../runtime/bin/fibonacci.wasm.so + ++.PHONY: multi_ncut.install ++fibonacci.install: ../runtime/bin/multi_ncut.wasm.so ++ ++.PHONY: sift.install ++fibonacci.install: ../runtime/bin/sift.wasm.so ++ ++.PHONY: hash.install ++hash.install: ../runtime/bin/hash.wasm.so ++ + .PHONY: asc-fib.install + asc-fib.install: ../runtime/bin/asc-fib.wasm.so + diff --git a/runtime/tests/apps_Makefile b/runtime/tests/apps_Makefile new file mode 100644 index 000000000..4bf96144c --- /dev/null +++ b/runtime/tests/apps_Makefile @@ -0,0 +1,159 @@ +AWSMCC=../awsm/target/release/awsm +CC=clang + +# Used by aWsm when compiling the *.wasm to *.bc +AWSMFLAGS= --inline-constant-globals --runtime-globals + +# Used by clang when compiling the *.so module +# --whole-archive causes the symbols in the listed static archive to be exported from the resulting *.so +# https://stackoverflow.com/questions/805555/ld-linker-question-the-whole-archive-option +CFLAGS=-O3 -flto +LDFLAGS=-shared -fPIC -Wl,--export-dynamic,--whole-archive -L../libsledge/dist/ -lsledge -Wl,--no-whole-archive +# LDFLAGS=-flto -fvisibility=hidden + +# Strips out calls to assert() and disables debuglog +CFLAGS+=-DNDEBUG + +dist: + mkdir -p dist + +.PHONY: all +all: \ + cifar10.install \ + resize_image.install \ + dummy_tpcc.install \ + hash.install \ + sift.install \ + mser.install \ + multi_ncut.install \ + disparity.install \ + tracking.install \ + empty.install \ + fibonacci.install \ + #gocr.install \ + #gps_ekf.install \ + #license_plate_detection.install \ + #resize_image.install \ + #scratch_storage_get.install \ + #scratch_storage_set.install \ + #scratch_storage_delete.install \ + #scratch_storage_upsert.install \ + +.PHONY: clean +clean: + @make -C wasm_apps clean + @make -C scratch_storage clean + @rm -rf dist + @rm -rf ../runtime/bin/*.so + +wasm_apps/dist/%.wasm: + make -C wasm_apps $(addprefix dist/,$(notdir $@)) + +../libsledge/dist/: + mkdir ../libsledge/dist + +../libsledge/dist/libsledge.a: ../libsledge/dist/ + make -C .. libsledge + +PHONY: scratch_storage +scratch_storage: + make -C scratch_storage all + +PHONY: scratch_storage.install +scratch_storage.install: \ + scratch_storage_get.install \ + scratch_storage_set.install \ + scratch_storage_delete.install \ + scratch_storage_upsert.install + +scratch_storage/scratch_storage_%.wasm: + make -C scratch_storage all + +dist/scratch_storage_%.bc: scratch_storage/scratch_storage_%.wasm dist + ${AWSMCC} ${AWSMFLAGS} $< -o $@ + +dist/%.bc: ./wasm_apps/dist/%.wasm dist + ${AWSMCC} ${AWSMFLAGS} $< -o $@ + +dist/%.ll: dist/%.bc + llvm-dis-12 $< -o $@ + +dist/%.wasm.so: dist/%.bc + ${CC} ${CFLAGS} ${LDFLAGS} $^ -o $@ + +../runtime/bin/%.wasm.so: dist/%.wasm.so + cp $^ $@ + +.PHONY: cifar10.install +cifar10.install: ../runtime/bin/cifar10.wasm.so + +# Echo? + +.PHONY: empty.install +empty.install: ../runtime/bin/empty.wasm.so + +.PHONY: exit.install +exit.install: ../runtime/bin/exit.wasm.so + +.PHONY: fibonacci.install +fibonacci.install: ../runtime/bin/fibonacci.wasm.so + +.PHONY: sift.install +sift.install: ../runtime/bin/sift.wasm.so + +.PHONY: mser.install +mser.install: ../runtime/bin/mser.wasm.so + +.PHONY: multi_ncut.install +multi_ncut.install: ../runtime/bin/multi_ncut.wasm.so + +.PHONY: hash.install +hash.install: ../runtime/bin/hash.wasm.so + +.PHONY: dummy_tpcc.install +dummy_tpcc.install: ../runtime/bin/dummy_tpcc.wasm.so + +.PHONY: disparity.install +disparity.install: ../runtime/bin/disparity.wasm.so + +.PHONY: tracking.install +tracking.install: ../runtime/bin/tracking.wasm.so + +.PHONY: asc-fib.install +asc-fib.install: ../runtime/bin/asc-fib.wasm.so + +.PHONY: gocr.install +gocr.install: ../runtime/bin/gocr.wasm.so + +.PHONY: resize_image.install +resize_image.install: ../runtime/bin/resize_image.wasm.so + +.PHONY: gps_ekf.install +gps_ekf.install: ../runtime/bin/gps_ekf.wasm.so + +.PHONY: license_plate_detection.install +license_plate_detection.install: ../runtime/bin/license_plate_detection.wasm.so + +.PHONY: trap_divzero.install +trap_divzero.install: ../runtime/bin/trap_divzero.wasm.so + +.PHONY: stack_overflow.install +stack_overflow.install: ../runtime/bin/stack_overflow.wasm.so + +.PHONY: html.install +html.install: ../runtime/bin/html.wasm.so + +.PHONY: scratch_storage_get.install +scratch_storage_get.install: ../runtime/bin/scratch_storage_get.wasm.so + +.PHONY: scratch_storage_set.install +scratch_storage_set.install: ../runtime/bin/scratch_storage_set.wasm.so + +.PHONY: scratch_storage_delete.install +scratch_storage_delete.install: ../runtime/bin/scratch_storage_delete.wasm.so + +.PHONY: scratch_storage_upsert.install +scratch_storage_upsert.install: ../runtime/bin/scratch_storage_upsert.wasm.so + +.PHONY: depth_to_xyz.install +depth_to_xyz.install: ../runtime/bin/depth_to_xyz.wasm.so diff --git a/runtime/tests/binary_search_tree.c b/runtime/tests/binary_search_tree.c new file mode 100644 index 000000000..3e7b53b74 --- /dev/null +++ b/runtime/tests/binary_search_tree.c @@ -0,0 +1,717 @@ +#include +#include +#include +#include +#include + +#define MAX_NODES 4096 // Maximum number of nodes in the pool + +// Define the colors for Red-Black Tree +typedef enum { RED, BLACK } Color; + +// Definition of a binary search tree node +struct TreeNode { + struct TreeNode *left; + struct TreeNode *right; + struct TreeNode *next; // pointing to the next node, this is used for nodePool + // to find next available node + struct TreeNode *dup_next; // pointing to next duplicate key item + struct TreeNode *parent; + Color color; + uint64_t left_subtree_sum; + uint64_t deadline; + uint64_t exe_time; +}; + +// Definition of TreeNode memory pool +struct TreeNodePool { + struct TreeNode* head; +}; + +struct binary_tree { + struct TreeNode *root; + struct TreeNodePool nodePool; + int id; + int queue_length; +}; + +// Initialize the node pool +void initNodePool(struct TreeNodePool *nodePool, int pool_size) { + + assert(nodePool != NULL); + + struct TreeNode *nodes = (struct TreeNode*)malloc(pool_size * sizeof(struct TreeNode)); + nodePool->head = nodes; // Initialize head to the beginning of the node array + + for (int i = 0; i < MAX_NODES - 1; ++i) { + nodes[i].next = &nodes[i + 1]; // Set the next pointer of each node to the next node + nodes[i].left = NULL; + nodes[i].right = NULL; + nodes[i].dup_next = NULL; + nodes[i].parent = NULL; + nodes[i].color = RED; + nodes[i].left_subtree_sum = 0; + nodes[i].deadline = 0; + nodes[i].exe_time = 0; + } + nodes[MAX_NODES - 1].next = NULL; +} + +struct binary_tree * init_binary_tree(int id, int queue_size) { + + struct binary_tree *binary_tree = (struct binary_tree *)calloc(1, sizeof(struct binary_tree)); + initNodePool(&binary_tree->nodePool, queue_size); + binary_tree->root = NULL; + binary_tree->id = id; + binary_tree->queue_length = 0; + + + return binary_tree; +} + +// Function to get the total number of non-deleted nodes in the binary tree +int getNonDeletedNodeCount(struct binary_tree *binary_tree) { + assert(binary_tree != NULL); + return binary_tree->queue_length; +} + +// Get a new node from the pool +struct TreeNode* newNode(struct binary_tree *binary_tree, Color color, struct TreeNode* left, + struct TreeNode* right, struct TreeNode* parent, uint64_t deadline, uint64_t exe_time) { + + assert(binary_tree != NULL); + + if (binary_tree->nodePool.head == NULL) { + printf("Binary search tree queue %d is full\n", binary_tree->id); + return NULL; + } else { + // Remove a node from the head of the memory pool + struct TreeNode* new_node_t = binary_tree->nodePool.head; + binary_tree->nodePool.head = new_node_t->next; + new_node_t->next = NULL; // Reset the next pointer of the new node + new_node_t->deadline = deadline; + new_node_t->exe_time = exe_time; + new_node_t->left = left; + new_node_t->right = right; + new_node_t->parent = parent; + new_node_t->color = color; + new_node_t->dup_next = NULL; + new_node_t->left_subtree_sum = 0; + return new_node_t; + } +} + +void getAvailableCapacity(struct binary_tree *binary_tree) { + + assert(binary_tree != NULL); + + int size = 0; + struct TreeNode* start = binary_tree->nodePool.head; + while(start) { + size++; + start = start->next; + } + + printf("available capacity of the queue is %d\n", size); +} + +void print_in_order(struct TreeNode* node) { + if (node != NULL) { + // Recursively traverse the left subtree + print_in_order(node->left); + + // Print the data in the current node + printf("%lu(%lu) ", node->deadline, node->exe_time); + //Print data in the dup_next list + struct TreeNode* cur = node->dup_next; + while(cur) { + printf("%lu(%lu) ", cur->deadline, cur->exe_time); + cur = cur->dup_next; + } + + // Recursively traverse the right subtree + print_in_order(node->right); + } +} + +// Function to print the items in the binary search tree in order +void print_tree_in_order(struct binary_tree* bst) { + if (bst != NULL) { + print_in_order(bst->root); + printf("\n"); + } +} + +//get the total execute time of a node. +uint64_t get_sum_exe_time_of_node(struct TreeNode* node){ + uint64_t total = 0; + struct TreeNode* curNode = node; + while (curNode!=NULL) { + total += curNode->exe_time; + curNode = curNode->dup_next; + } + + return total; +} + +//get the total execute time of a node's subtree, including left and right subtree +uint64_t get_sum_exe_time_of_subtree(struct TreeNode* root) { + if (root == NULL) { + return 0; + } + + return get_sum_exe_time_of_node(root) + root->left_subtree_sum + get_sum_exe_time_of_subtree(root->right); +} + +// Return a node to the pool +void deleteNode(struct binary_tree *binary_tree, struct TreeNode* node) { + + assert(binary_tree != NULL); + assert(node != NULL); + + // Insert the node back to the head of the memory pool + node->left = NULL; + node->right = NULL; + node->parent = NULL; + node->color = RED; + node->deadline = 0; + node->exe_time = 0; + node->left_subtree_sum = 0; + node->dup_next = NULL; + node->next = binary_tree->nodePool.head; + binary_tree->nodePool.head = node; +} + +int findHeight(struct TreeNode *root) +{ + int lefth, righth; + if(root == NULL) + return 0; + lefth = findHeight(root->left); + righth = findHeight(root->right); + return (lefth > righth ? lefth : righth)+1; +} +// Update root if rotating. +void leftRotate(struct binary_tree *binary_tree, struct TreeNode *x) { + struct TreeNode *y = x->right; + x->right = y->left; + if (y->left != NULL) + y->left->parent = x; + + y->parent = x->parent; + + if (x->parent == NULL) + binary_tree->root = y; + else if (x == x->parent->left) + x->parent->left = y; + else + x->parent->right = y; + y->left = x; + x->parent = y; + y->left_subtree_sum += x->exe_time + x->left_subtree_sum; +} + +// Update root if rotatiing +void rightRotate(struct binary_tree *binary_tree, struct TreeNode *x) { + struct TreeNode *y = x->left; + x->left = y->right; + + int new_sum_left = 0; + if (y->right != NULL) { + y->right->parent = x; + new_sum_left = get_sum_exe_time_of_subtree(y->right); //y->right->exe_time + y->right->sum_left; + } + + x->left_subtree_sum = new_sum_left; + y->parent = x->parent; + if (x->parent == NULL) + binary_tree->root = y; + else if (x == x->parent->right) + x->parent->right = y; + else + x->parent->left = y; + y->right = x; + x->parent = y; +} + +void insertFixup(struct binary_tree *binary_tree, struct TreeNode *z) { + while (z->parent != NULL && z->parent->color == RED) { + if (z->parent == z->parent->parent->left) { + struct TreeNode *y = z->parent->parent->right; + if (y != NULL && y->color == RED) { + z->parent->color = BLACK; + y->color = BLACK; + z->parent->parent->color = RED; + z = z->parent->parent; + } else { + if (z == z->parent->right) { + z = z->parent; + leftRotate(binary_tree, z); + } + z->parent->color = BLACK; + z->parent->parent->color = RED; + rightRotate(binary_tree, z->parent->parent); + } + } else { + struct TreeNode *y = z->parent->parent->left; + if (y != NULL && y->color == RED) { + z->parent->color = BLACK; + y->color = BLACK; + z->parent->parent->color = RED; + z = z->parent->parent; + } else { + if (z == z->parent->left) { + z = z->parent; + rightRotate(binary_tree, z); + } + z->parent->color = BLACK; + z->parent->parent->color = RED; + leftRotate(binary_tree, z->parent->parent); + } + } + } + binary_tree->root->color = BLACK; +} + +// Function to insert a value into a binary search tree. Tree root might be changed after inserting a new node +void insert(struct binary_tree *binary_tree, uint64_t deadline, uint64_t exe_time) { + struct TreeNode *z = newNode(binary_tree, RED, NULL, NULL, NULL, deadline, exe_time); + binary_tree->queue_length++; + struct TreeNode *y = NULL; + struct TreeNode *x = binary_tree->root; + while (x != NULL) { + y = x; + + //if found a dup_next deadline, inserted the node + //at the end of the linklist + if (z->deadline == x->deadline) { + //find the tail the link list; + struct TreeNode* tail = x; + while (tail->dup_next != NULL) { + tail = tail->dup_next; + } + //append the new node at the end of the list; + tail->dup_next = z; + z->color = x->color; + z->left_subtree_sum = x->left_subtree_sum; + return; + } else if (z->deadline < x->deadline) { + x->left_subtree_sum += z->exe_time; + x = x->left; + } else { + x = x->right; + } + } + + z->parent = y; + if (y == NULL) { + binary_tree->root = z; + } else if (z->deadline < y->deadline) { + y->left = z; + } else { + y->right = z; + } + + insertFixup(binary_tree, z); +} + +// Helper function to find the minimum value in a binary search tree +struct TreeNode* findMin(struct binary_tree *binary_tree) { + + assert(binary_tree != NULL); + + struct TreeNode *curNode = binary_tree->root; + if (curNode == NULL) { + return NULL; + } + + while (curNode->left != NULL) { + curNode = curNode->left; // Keep traversing to the left until the leftmost node is reached + } + return curNode; +} + +// Helper function to find the maximum value in a binary search tree +struct TreeNode* findMax(struct binary_tree *binary_tree) { + + assert(binary_tree != NULL); + + struct TreeNode *curNode = binary_tree->root; + if (curNode == NULL) { + return NULL; + } + + while (curNode->right != NULL) { + curNode = curNode->right; // Keep traversing to the right until the rightmost node is reached + } + return curNode; +} + +struct TreeNode* searchByKey(struct binary_tree *binary_tree, uint64_t deadline) { + struct TreeNode* current = binary_tree->root; + while (current != NULL && current->deadline != deadline) { + if (deadline < current->deadline) { + current = current->left; + } + else { + current = current->right; + } + } + + return current; +} + +void transplant(struct binary_tree *binary_tree, struct TreeNode *u, struct TreeNode *v) { + if (u->parent == NULL) + binary_tree->root = v; + else if (u == u->parent->left) + u->parent->left = v; + else + u->parent->right = v; + if (v != NULL) + v->parent = u->parent; +} + +struct TreeNode* minimum(struct TreeNode *node) { + while (node->left != NULL) { + node = node->left; + } + return node; +} + +void deleteFixup(struct binary_tree *binary_tree, struct TreeNode *x) { + while (x != binary_tree->root && (x == NULL || x->color == BLACK)) { + + if (x == NULL){ + break; + } + + if ( x == x->parent->left) { + struct TreeNode *w = x->parent->right; + if (w->color == RED) { + w->color = BLACK; + x->parent->color = RED; + leftRotate(binary_tree, x->parent); + w = x->parent->right; + } + if ((w->left == NULL || w->left->color == BLACK) && (w->right == NULL || w->right->color == BLACK)) { + w->color = RED; + x = x->parent; + } else { + if (w->right == NULL || w->right->color == BLACK) { + if (w->left != NULL) w->left->color = BLACK; + w->color = RED; + rightRotate(binary_tree, w); + w = x->parent->right; + } + w->color = x->parent->color; + x->parent->color = BLACK; + if (w->right != NULL) w->right->color = BLACK; + leftRotate(binary_tree, x->parent); + x = binary_tree->root; + } + } else { + struct TreeNode *w = x->parent->left; + if (w->color == RED) { + w->color = BLACK; + x->parent->color = RED; + rightRotate(binary_tree, x->parent); + w = x->parent->left; + } + if ((w->right == NULL || w->right->color == BLACK) && (w->left == NULL || w->left->color == BLACK)) { + w->color = RED; + x = x->parent; + } else { + if (w->left == NULL || w->left->color == BLACK) { + if (w->right != NULL) w->right->color = BLACK; + w->color = RED; + leftRotate(binary_tree, w); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = BLACK; + if (w->left != NULL) w->left->color = BLACK; + rightRotate(binary_tree, x->parent); + x = binary_tree->root; + } + } + } + if (x != NULL) x->color = BLACK; +} + +void removeNode(struct binary_tree *binary_tree, struct TreeNode *z) { + struct TreeNode *y = z; + struct TreeNode *x = NULL; + Color y_original_color = y->color; + + if ( z->left != NULL && z->right != NULL ) { + y = minimum(z->right); + + int diff = get_sum_exe_time_of_node(y) - get_sum_exe_time_of_node(z); + struct TreeNode* cur = z->right; + while (cur != y) { + cur->left_subtree_sum -= diff; + cur = cur->left; + } + + uint64_t remove_deadline = z->deadline; + uint64_t remove_exe_time = z->exe_time; + z->deadline = y->deadline; + z->exe_time = y->exe_time; + z->dup_next = y->dup_next; + y->deadline = remove_deadline; + y->exe_time = remove_exe_time; + y->dup_next = NULL; + + removeNode(binary_tree, y); + return; + } + + //now the node to be removed has only no children + //or only one child, update the sum_left value of + //all the node along the path from root to z. + struct TreeNode* current = z; + struct TreeNode* p = current->parent; + while (p != NULL) { + if (p->left == current) + { + p->left_subtree_sum -= z->exe_time; + } + current = p; + p = current->parent; + } + + if (z->left == NULL) { + x = z->right; + transplant(binary_tree, z, z->right); + + } else if (z->right == NULL) { + x = z->left; + transplant(binary_tree, z, z->left); + + } + + deleteNode(binary_tree, z); + if (y_original_color == BLACK) { + deleteFixup(binary_tree, x); + } +} + +// Function to delete a value from a binary search tree +void delete_i(struct binary_tree *binary_tree, uint64_t deadline, uint64_t exe_time, bool *deleted) { + + assert(binary_tree != NULL); + + struct TreeNode *z = searchByKey(binary_tree, deadline); + if (z != NULL) { + //if there are duplicated nodes in Z, + //we just need to remove duplicated one. + if (z->dup_next != NULL) { + struct TreeNode* cur = z; + struct TreeNode* prev = NULL; + while (cur && cur->exe_time != exe_time) { + prev = cur; + cur = cur->dup_next; + } + + //if the target node has been found + if (cur != NULL) { + //update the sumLeft in all of its parent node. + struct TreeNode* current = z; + struct TreeNode* p = current->parent; + while (p != NULL) { + if (p->left == current) + { + p->left_subtree_sum -= exe_time; + } + current = p; + p = current->parent; + } + } else { + *deleted = false; + printf("not found the node (%d %d)\n", deadline, exe_time); + return; + } + + //if the removed node is the head of the linkedlist; + if (cur == z) { + //copy the data from the removed node. + struct TreeNode* newroot = z->dup_next; + newroot->color = z->color; + newroot->left_subtree_sum = z->left_subtree_sum; + newroot->left = z->left; + if (newroot->left != NULL) { + newroot->left->parent = newroot; + } + newroot->right = z->right; + if (newroot->right != NULL) { + newroot->right->parent = newroot; + } + + newroot->parent = z->parent; + + if (z->parent) { + if (z->parent->left == z) { + z->parent->left = newroot; + } else { + z->parent->right = newroot; + } + } + //clean up the remove node z; + //memset( z, 0, sizeof(Node)); + binary_tree->queue_length--; + *deleted = true; + deleteNode(binary_tree, z); + } + else { //remove the node from the link list; + prev->dup_next = cur->dup_next; + } + } else{ + if (z->exe_time == exe_time) { + removeNode(binary_tree, z); + binary_tree->queue_length--; + *deleted = true; + } else { + *deleted = false; + printf("not found the node (%d %d)\n", deadline, exe_time); + } + } + } else { + *deleted = false; + printf("not found the node (%d %d)\n", deadline, exe_time); + } +} + +// Function to find a value in a binary search tree (non-recursive) +/*struct TreeNode* find(struct TreeNode* root, int val) { + while (root != NULL) { + if (val == root->val) { + return root; // Return the node if value is found + } else if (val < root->val) { + root = root->left; // Move to left subtree if value is less + } else { + root = root->right; // Move to right subtree if value is greater + } + } + return NULL; // Return NULL if value is not found or if the tree is empty +}*/ + +bool is_empty(struct binary_tree *binary_tree) { + assert(binary_tree != NULL); + + return binary_tree->root == NULL; +} + +void inorder(struct binary_tree *binary_tree, struct TreeNode* root) +{ + assert(binary_tree != NULL); + + if(root == NULL) + return; + inorder(binary_tree, root->left); + printf("%lu(%lu) ", root->deadline, root->exe_time); + inorder(binary_tree, root->right); +} +// return the sum of nodes' execution time that less than the target priority +uint64_t findMaxValueLessThan(struct binary_tree *binary_tree, struct TreeNode* root, uint64_t deadline) { + assert(binary_tree != NULL); + + if (root == NULL) { + return 0; + } + if (deadline == root->deadline) { + return root->left_subtree_sum; + } else if (deadline < root->deadline) { + return findMaxValueLessThan(binary_tree, root->left, deadline); + } else { + return get_sum_exe_time_of_node(root) + root->left_subtree_sum + findMaxValueLessThan(binary_tree, root->right, deadline); + } + +} + +struct TreeNode* makeEmpty(struct binary_tree *binary_tree, struct TreeNode* root) +{ + assert(binary_tree != NULL); + + if(root != NULL) { + makeEmpty(binary_tree, root->left); + makeEmpty(binary_tree, root->right); + deleteNode(binary_tree, root); + } + return NULL; +} + +int main() { + struct binary_tree * bst = init_binary_tree(12, MAX_NODES); + for(int i = 0; i < 64; i++) { + insert(bst, i+1, i+1); + } + print_tree_in_order(bst); + printf("\n"); + + printf("=============================================\n"); + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + + printf("\n============insert the duplicated value: 8===============" ); + insert(bst, 8, 8); + + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + + printf("\n============insert the duplicated value: 10===============" ); + insert(bst, 10, 10); + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + + printf("\n============remove the duplicated value: 8===============" ); + bool deleted = false; + delete_i(bst, 8, 8, &deleted); + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + + printf("\n============remove the duplicated value: 10===============" ); + delete_i(bst, 10, 10, &deleted); + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + + printf("\n============remove the single value: 20===============" ); + delete_i(bst, 20, 20, &deleted); + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + printf("\n============insert the duplicated value: 41===============" ); + insert(bst, 41, 35); + print_tree_in_order(bst); + printf("\n"); + + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + + printf("\n============remove the single value: 40===============" ); + + delete_i(bst, 40, 40, &deleted); + print_tree_in_order(bst); + printf("\n"); + for (int i = 0; i < 64; i++) { + printf("Sum of values less than %d: %lu\n", i+1, findMaxValueLessThan(bst, bst->root, i+1)); + } + + struct TreeNode *min = findMin(bst); + printf("min is (%u %u)\n", min->deadline, min->exe_time); + struct TreeNode *max = findMax(bst); + printf("max is (%u %u)\n", max->deadline, max->exe_time); + + printf("delete 45, 30\n"); + delete_i(bst, 45, 30, &deleted); + printf("delete 41, 35\n"); + delete_i(bst, 41, 35, &deleted); + print_tree_in_order(bst); + printf("\n"); +} diff --git a/runtime/tests/change_vision_apps_json.sh b/runtime/tests/change_vision_apps_json.sh new file mode 100755 index 000000000..f526bc761 --- /dev/null +++ b/runtime/tests/change_vision_apps_json.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# 检查参数数量 +if [ $# -ne 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +# 参数赋值 +multiplier=$1 +file_path=$2 +request_type=$3 + +# 创建临时文件 +temp_file=$(mktemp) + +# 读取文件并修改内容 +while IFS= read -r line; do + if echo "$line" | grep -q "\"request-type\": $request_type,"; then + found_request_type=1 + fi + + if [ "$found_request_type" == "1" ] && echo "$line" | grep -q "\"expected-execution-us\": "; then + expected_exec_us=$(echo "$line" | grep -o "[0-9]\+") + fi + + if [ "$found_request_type" == "1" ] && echo "$line" | grep -q "\"relative-deadline-us\": "; then + new_deadline=$((expected_exec_us * multiplier)) + line=$(echo "$line" | sed -E "s/[0-9]+/$new_deadline/") + found_request_type=0 + fi + + echo "$line" >> "$temp_file" +done < "$file_path" + +# 替换原文件 +mv "$temp_file" "$file_path" + +echo "Updated 'relative-deadline-us' for request_type $request_type in $file_path." + diff --git a/runtime/tests/compare_dispatchers.sh b/runtime/tests/compare_dispatchers.sh new file mode 100755 index 000000000..77bf031fd --- /dev/null +++ b/runtime/tests/compare_dispatchers.sh @@ -0,0 +1,72 @@ +#!/bin/bash +ulimit -n 655350 + +function usage { + echo "$0 [worker num] [listener num] [first worker core id] [dispatcher policy, EDF_INTERRUPT, RR, JSQ, LLD] [scheduler policy] [server log file] [disable busy loop] [disable service time simulation] [disable preempt] [disable get req from GQ] [json config]" + exit 1 +} + +if [ $# != 11 ] ; then + usage + exit 1; +fi + +worker_num=$1 +listener_num=$2 +first_worker_core_id=$3 +dispatcher_policy=$4 +scheduler_policy=$5 +server_log=$6 +disable_busy_loop=$7 +disable_service_ts_simulation=$8 +disable_preempt=$9 +disable_get_req_from_GQ=${10} +disable_autoscaling="true" +json_config=${11} + +if [ "$scheduler_policy" = "FIFO" ]; then + worker_group_size=$worker_num +else + worker_group_size=$((worker_num / listener_num)) +fi + +declare project_path="$( + cd "$(dirname "$0")/../.." + pwd +)" +echo $project_path +path=`pwd` +export SLEDGE_DISABLE_PREEMPTION=$disable_preempt +export SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ=$disable_get_req_from_GQ +export SLEDGE_DISABLE_BUSY_LOOP=$disable_busy_loop +export SLEDGE_DISABLE_AUTOSCALING=$disable_autoscaling +#export SLEDGE_SIGALRM_HANDLER=TRIAGED +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=$disable_service_ts_simulation +export SLEDGE_FIRST_WORKER_COREID=$first_worker_core_id +export SLEDGE_NWORKERS=$worker_num +export SLEDGE_NLISTENERS=$listener_num +export SLEDGE_WORKER_GROUP_SIZE=$worker_group_size +export SLEDGE_SCHEDULER=$scheduler_policy +#export SLEDGE_DISPATCHER=DARC +export SLEDGE_DISPATCHER=$dispatcher_policy +export SLEDGE_SCHEDULER=$scheduler_policy +#export SLEDGE_DISPATCHER=EDF_INTERRUPT +export SLEDGE_SANDBOX_PERF_LOG=$path/$server_log +#echo $SLEDGE_SANDBOX_PERF_LOG +cd $project_path/runtime/bin +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_big_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_armcifar10.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_png2bmp.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/mulitple_linear_chain.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing3.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/hash.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/empty.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/fib.json +LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/$json_config +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_sodresize.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_sodresize.json + diff --git a/runtime/tests/config.json b/runtime/tests/config.json new file mode 100644 index 000000000..3605cfb76 --- /dev/null +++ b/runtime/tests/config.json @@ -0,0 +1,330 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/fib1", + "request-type": 1, + "n-resas": 2, + "path": "fibonacci1.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib2", + "request-type": 2, + "n-resas": 2, + "path": "fibonacci2.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib3", + "request-type": 3, + "n-resas": 2, + "path": "fibonacci3.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib4", + "request-type": 4, + "n-resas": 2, + "path": "fibonacci4.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib5", + "request-type": 5, + "n-resas": 2, + "path": "fibonacci5.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib6", + "request-type": 6, + "n-resas": 2, + "path": "fibonacci6.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib7", + "request-type": 7, + "n-resas": 2, + "path": "fibonacci7.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib8", + "request-type": 8, + "n-resas": 2, + "path": "fibonacci8.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib9", + "request-type": 9, + "n-resas": 2, + "path": "fibonacci9.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib10", + "request-type": 10, + "n-resas": 2, + "path": "fibonacci10.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib11", + "request-type": 11, + "n-resas": 2, + "path": "fibonacci11.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib12", + "request-type": 12, + "n-resas": 2, + "path": "fibonacci12.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib13", + "request-type": 13, + "n-resas": 2, + "path": "fibonacci13.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib14", + "request-type": 14, + "n-resas": 2, + "path": "fibonacci14.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib15", + "request-type": 15, + "n-resas": 2, + "path": "fibonacci15.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib16", + "request-type": 16, + "n-resas": 2, + "path": "fibonacci16.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib17", + "request-type": 17, + "n-resas": 2, + "path": "fibonacci17.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib18", + "request-type": 18, + "n-resas": 2, + "path": "fibonacci18.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib19", + "request-type": 19, + "n-resas": 2, + "path": "fibonacci19.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib20", + "request-type": 20, + "n-resas": 2, + "path": "fibonacci20.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib21", + "request-type": 21, + "n-resas": 2, + "path": "fibonacci21.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib22", + "request-type": 22, + "n-resas": 2, + "path": "fibonacci22.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib23", + "request-type": 23, + "n-resas": 2, + "path": "fibonacci23.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib24", + "request-type": 24, + "n-resas": 2, + "path": "fibonacci24.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib25", + "request-type": 25, + "n-resas": 2, + "path": "fibonacci25.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib26", + "request-type": 26, + "n-resas": 2, + "path": "fibonacci26.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib27", + "request-type": 27, + "n-resas": 2, + "path": "fibonacci27.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib28", + "request-type": 28, + "n-resas": 2, + "path": "fibonacci28.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib29", + "request-type": 29, + "n-resas": 2, + "path": "fibonacci29.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib30", + "request-type": 30, + "n-resas": 2, + "path": "fibonacci30.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib31", + "request-type": 31, + "n-resas": 2, + "path": "fibonacci31.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib32", + "request-type": 32, + "n-resas": 2, + "path": "fibonacci32.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + } + ] + } +] \ No newline at end of file diff --git a/runtime/tests/copy_func_so.sh b/runtime/tests/copy_func_so.sh new file mode 100644 index 000000000..d7a5164ad --- /dev/null +++ b/runtime/tests/copy_func_so.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +num_copies=$1 +func_name="fibonacci" + +for i in $(seq $num_copies); do + cp $func_name".wasm.so" $func_name$i.wasm.so +done + +echo "Files copied successfully." + diff --git a/runtime/tests/debug.sh b/runtime/tests/debug.sh new file mode 100755 index 000000000..9123eaa5a --- /dev/null +++ b/runtime/tests/debug.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Executes the runtime in GDB +# Substitutes the absolute path from the container with a path relatively derived from the location of this script +# This allows debugging outside of the Docker container +# Also disables pagination and stopping on SIGUSR1 + +declare project_path="$( + cd "$(dirname "$1")/../.." + pwd +)" +path=`pwd` +echo $project_path +cd $project_path/runtime/bin +export SLEDGE_DISABLE_BUSY_LOOP=true +export SLEDGE_DISABLE_AUTOSCALING=true +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=false +export SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ=true +export SLEDGE_DISABLE_PREEMPTION=false +export SLEDGE_SANDBOX_PERF_LOG=$path/server.log +export SLEDGE_NWORKERS=6 +export SLEDGE_FIRST_WORKER_COREID=4 +export SLEDGE_NLISTENERS=1 +export SLEDGE_WORKER_GROUP_SIZE=6 +export SLEDGE_SCHEDULER=FIFO +#export SLEDGE_DISPATCHER=DARC +#export SLEDGE_DISPATCHER=EDF_INTERRUPT +#export SLEDGE_DISPATCHER=SHINJUKU +export SLEDGE_DISPATCHER=LLD +export LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" +#gdb --eval-command="handle SIGUSR1 nostop" \ +# --eval-command="set pagination off" \ +# --eval-command="set substitute-path /sledge/runtime $project_path/runtime" \ +# --eval-command="run ../tests/fib.json" +# ./sledgert + +gdb --eval-command="handle SIGUSR1 nostop" \ + --eval-command="handle SIGUSR1 noprint" \ + ./sledgert diff --git a/runtime/tests/dummy_tpcc.json b/runtime/tests/dummy_tpcc.json new file mode 100644 index 000000000..de44828e4 --- /dev/null +++ b/runtime/tests/dummy_tpcc.json @@ -0,0 +1,69 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/Payment", + "request-type": 1, + "n-resas": 1, + "group-id": 1, + "path": "dummy_tpcc.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 6, + "relative-deadline-us": 60, + "http-resp-content-type": "text/plain" + }, + { + "route": "/OrderStatus", + "request-type": 2, + "n-resas": 1, + "group-id": 1, + "path": "dummy_tpcc.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 6, + "relative-deadline-us": 60, + "http-resp-content-type": "text/plain" + }, + { + "route": "/NewOrder", + "request-type": 3, + "n-resas": 2, + "group-id": 2, + "path": "dummy_tpcc.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 20, + "relative-deadline-us": 200, + "http-resp-content-type": "text/plain" + }, + + { + "route": "/Delivery", + "request-type": 4, + "n-resas": 2, + "group-id": 3, + "path": "dummy_tpcc.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 88, + "relative-deadline-us": 880, + "http-resp-content-type": "text/plain" + }, + { + "route": "/StockLevel", + "request-type": 5, + "n-resas": 2, + "group-id": 3, + "path": "dummy_tpcc.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 100, + "relative-deadline-us": 1000, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/empty.json b/runtime/tests/empty.json new file mode 100644 index 000000000..dfa91cef5 --- /dev/null +++ b/runtime/tests/empty.json @@ -0,0 +1,24 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/empty", + "request-type": 1, + "n-resas": 2, + "group-id": 1, + "path": "empty.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/fib.json b/runtime/tests/fib.json new file mode 100644 index 000000000..3666ec8bc --- /dev/null +++ b/runtime/tests/fib.json @@ -0,0 +1,35 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/fib", + "request-type": 1, + "n-resas": 1, + "group-id": 1, + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib2", + "request-type": 2, + "n-resas": 2, + "group-id": 2, + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 6480, + "relative-deadline-us": 64800, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/generate_json.py b/runtime/tests/generate_json.py new file mode 100644 index 000000000..0836e8410 --- /dev/null +++ b/runtime/tests/generate_json.py @@ -0,0 +1,42 @@ +import sys +import json + +def generate_config(num_routes): + config = [] + for i in range(1, num_routes + 1): + route = { + "route": f"/fib{i}", + "request-type": i, + "n-resas": 2, + "path": f"fibonacci{i}.wasm.so", + #"path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + } + config.append(route) + return config + +def main(): + if len(sys.argv) != 2: + print("Usage: python script.py ") + return + + num_routes = int(sys.argv[1]) + routes = generate_config(num_routes) + + config = [{ + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": routes + }] + + with open('config.json', 'w') as f: + json.dump(config, f, indent=4) + +if __name__ == "__main__": + main() + diff --git a/runtime/tests/generate_json_with_replica_field.py b/runtime/tests/generate_json_with_replica_field.py new file mode 100644 index 000000000..b2c68fa7b --- /dev/null +++ b/runtime/tests/generate_json_with_replica_field.py @@ -0,0 +1,42 @@ +import json +import argparse + +def generate_json(route_replicas): + data = [ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "route-replicas": route_replicas, + "routes": [ + { + "route": "/fib", + "request-type": 1, + "n-resas": 1, + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + } + ] + } + ] + return data + +def main(): + parser = argparse.ArgumentParser(description='Generate JSON file.') + parser.add_argument('route_replicas', type=int, help='Value for route-replicas') + parser.add_argument('output_file', type=str, help='Output JSON file') + + args = parser.parse_args() + + json_data = generate_json(args.route_replicas) + + with open(args.output_file, 'w') as f: + json.dump(json_data, f, indent=4) + +if __name__ == "__main__": + main() + diff --git a/runtime/tests/get_ctx.sh b/runtime/tests/get_ctx.sh new file mode 100755 index 000000000..4a4a3fedc --- /dev/null +++ b/runtime/tests/get_ctx.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +function usage { + echo "$0 [concurrency]" + exit 1 +} + +if [ $# != 1 ] ; then + usage + exit 1; +fi + +concurrency=$1 +output="${concurrency}_ctx.txt" +pid=`ps -ef|grep "sledgert"|grep -v grep |awk '{print $2}'` +total_switches=0 + +if [ -z "$pid" ] || [ ! -d "/proc/$pid" ]; then + echo "PID $pid not found or no longer exists, skipping" | tee -a $output + exit 1 +fi + +echo "PID $pid:" | tee -a $output + +for tid in $(ls /proc/$pid/task); do + thread_name=$(cat /proc/$pid/task/$tid/comm 2>/dev/null) + + if [ "$thread_name" = "worker_thread" ]; then + if [ -f "/proc/$pid/task/$tid/sched" ]; then + tid_switches=$(grep nr_switches /proc/$pid/task/$tid/sched | awk '{print $3}') + echo " TID $tid (name=$thread_name): nr_switches = $tid_switches" | tee -a $output + total_switches=$((total_switches + tid_switches)) + fi + fi +done + +echo "----------------------------------------" | tee -a $output +echo "Total nr_switches for all 'worker_thread' threads: $total_switches" | tee -a $output +echo "Results saved to $output" diff --git a/runtime/tests/hash.json b/runtime/tests/hash.json new file mode 100644 index 000000000..2c3f1e84c --- /dev/null +++ b/runtime/tests/hash.json @@ -0,0 +1,24 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/hash", + "request-type": 1, + "n-resas": 3, + "group-id": 1, + "path": "hash.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/hash_darc.json b/runtime/tests/hash_darc.json new file mode 100644 index 000000000..9bfb2b6d4 --- /dev/null +++ b/runtime/tests/hash_darc.json @@ -0,0 +1,35 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/hash1", + "request-type": 1, + "n-resas": 3, + "group-id": 1, + "path": "hash.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + }, + { + "route": "/hash2", + "request-type": 2, + "n-resas": 3, + "group-id": 2, + "path": "hash.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/hash_high_bimodal.json b/runtime/tests/hash_high_bimodal.json new file mode 100644 index 000000000..ecdcb18f2 --- /dev/null +++ b/runtime/tests/hash_high_bimodal.json @@ -0,0 +1,35 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/hash", + "request-type": 1, + "n-resas": 1, + "group-id": 1, + "path": "hash.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + }, + { + "route": "/hash2", + "request-type": 2, + "n-resas": 5, + "group-id": 2, + "path": "hash.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/high_bimodal_realapps.json b/runtime/tests/high_bimodal_realapps.json new file mode 100644 index 000000000..68cd18c5d --- /dev/null +++ b/runtime/tests/high_bimodal_realapps.json @@ -0,0 +1,35 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/resize", + "request-type": 1, + "n-resas": 1, + "group-id": 1, + "path": "resize_image.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 26, + "relative-deadline-us": 260, + "http-resp-content-type": "text/plain" + }, + { + "route": "/classifier", + "request-type": 2, + "n-resas": 5, + "group-id": 2, + "path": "cifar10.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 2070, + "relative-deadline-us": 20700, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/id_rsa b/runtime/tests/id_rsa new file mode 100644 index 000000000..832cdbcd4 --- /dev/null +++ b/runtime/tests/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEA2bxGJUQa7ywL7I6prE75oA2fE/AfTHTi5KzUEh+LezA/KObP +Aa7FgcWUu/QiwCz/aVMvIeVms7CZuu/QJJ9TEinSdxjHixoNhZvbBgLhweCje2YR +MUl9ynPsaVZwb/DebloVzlweXRkXKLCfx3s+HXDSHMpDgMhbF5AiehxQCXCUrNzE +/e6d217PmX02+VRQPwVWycu5/Mi+mMvlgN/e1XggHDYhvX6t4U0Am5KZ4kqdxXYP +T0x6gXxcLi6Ulb15LNCqbbdbFhfYg+tWlqxqIyTHJO2kSw4VNINxIR0dT5FYbxFV +4DL9msYQ+ePXUUym3LnPix0Bt9EQLPMeewYhYwIBIwKCAQAlU3m9PuALvmhxsg51 +T13+LjiHEziQw5ScOuKGw5RBAPTicLXFxjB8pYc2KdoSUNtT4l/Z7M/HCFTeN71I +G1ARvgbSloibgM8eN/mpQlKWQ8RBCi7VP8xr2Vu6SVUagRAwLLNH8ojcwnj/qT/2 +T6Q/j679nwRB9nYEChSJ9jmbN437tvrLSE1ZxvUIzV96Sd9aGaOjg4Ezyb7KqGSR +0pFKiufTAoAtUcpQ/hYrQ88KFgbFFNm4yRz0dfNpOekdio/IXs2WZ8688k8aZ1OS +DDmFiMLyHxZmp6s5d2pJsLQBIGGWMqxm7a2xeJHY6oLc3DVIRfoJqkFDNWXLlPyR +eBAbAoGBAPPvG4koPGbJ7MoJF6DXTg8bjdXDCPhKL5JoU7y/OX7vKdYLX69RYxs+ +ubILTM/7TTC33nEhqty+GWVVOkwOlap99vmWp2hhrjelLYPJmUsQ7aMWUM2Fsxcw +S/NH8RUeaM0F1QqwJEDbdvKjOrgteVQmtPaFo8LFHKmyHF1PsrQ/AoGBAOSBay+J +Nx6FvsWHt5PYrKpAjJvM7lAEgFM89zNvBLsIzqxsnTFjvnZ0ySKT5OeBfPA6nf13 +tx+GsurzIniwKjKeBgGUXRIQ5CjEuGtOdnox7rLfkiluiOyKVDyApUN/66d9FLLX +pEeJZSDlUSPGJBvlwfvNGBeVUj9XRv/awbndAoGAfXOnwuF+Jjv2HsLY86HtoV6g +txPYujTIAicGflO3K1ZtSYIxNZeSDgMAajGyeZcvAxy7iqOZs1pzdfFRLm3mkjIn +Pof+UvBoOd/rhZrhH0qI14fRyMhqu3fsi76ZPg+jnKPp6D1UeSBpDxIeMtWO2tIT +7H8+RukHbTcHRe55KX8CgYEAlikpLd3Tw5m34OQoLfTJPK4TQc/Pzi7X/C9gniRi +McP1h0APhtslY8kWdc7m4UZ2rH5KkJ8gkQ9fobW3kSNO7hASk1LeEy+r4EbCVSTu +xVQDQlhnXQ4fdt6PIHHLsAOa28c5fNbZq1pJxSj6zl2isz82VQMedeXIVYJ/HSla +u/cCgYAMNf9At68UjxBECLQOxfLU2GtJBQMP1r3QJzz6/PJqctgJZWzDL/SIg24F +hp6cEaaCiUYV158IShdeTUZpai7UNooQ9wijDYYcodXE/9HnNGgVjP+YqvpnizGx +yFiYiEcowoetWDVOkDzGHDVAtHdwlWBc4D4frJ5kNipMlEEw5Q== +-----END RSA PRIVATE KEY----- diff --git a/runtime/tests/kill_perf.sh b/runtime/tests/kill_perf.sh new file mode 100755 index 000000000..6af2b12df --- /dev/null +++ b/runtime/tests/kill_perf.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +pid=`ps -ef | grep "perf" | grep -v grep | awk 'NR==1 {print $2}'` +sudo kill -SIGINT $pid +sleep 2 + +pid=`ps -ef|grep "perf" |grep -v grep |awk '{print $2}'` +echo $pid +sudo kill -9 $pid diff --git a/runtime/tests/kill_sledge.sh b/runtime/tests/kill_sledge.sh new file mode 100755 index 000000000..8069a8593 --- /dev/null +++ b/runtime/tests/kill_sledge.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +pid=`ps -ef|grep "sledgert"|grep -v grep |awk '{print $2}'` +echo $pid +sudo kill -2 $pid +sleep 2 +sudo kill -9 $pid diff --git a/runtime/tests/meet_deadline_percentage.py b/runtime/tests/meet_deadline_percentage.py new file mode 100644 index 000000000..ea2b175fb --- /dev/null +++ b/runtime/tests/meet_deadline_percentage.py @@ -0,0 +1,225 @@ +import sys +import json +from collections import defaultdict +import numpy as np + +weights_dict = { + '1': 0.99, + '2': 0.09, + '3': 0.01 +} + +def def_list_value(): + return [0, 0, 0, 0, 0] +def def_value(): + return 0 + +def count_miss_or_meet_deadline_requests(file_dir, percentage): + throughput = 0; + max_exe_time = 0 + ### each time for a request + request_times_dict = defaultdict(def_list_value) + #### get execution time + running_time_dict = defaultdict(def_value) + queuing_times_dict = defaultdict(def_value) + total_times_dict = defaultdict(def_value) + runnable_times_dict = defaultdict(def_value) + blocked_times_dict = defaultdict(def_value) + initializing_times_dict = defaultdict(def_value) + execution_times_dict = defaultdict(def_value) + + ### init overhead + ### queuelength + queuelength_dict = defaultdict(list) + ### + running_times = defaultdict(list) + queuing_times = defaultdict(list) + thread_running_times = defaultdict(list) + total_times = defaultdict(list) + thread_times = defaultdict(list) + thread_throughput = defaultdict(list) + t2_cleanup = defaultdict(list) + runnable_times = defaultdict(list) + blocked_times = defaultdict(list) + initializing_times = defaultdict(list) + execution_times = defaultdict(list) + #### + request_counter = defaultdict(def_value) + total_time_dist = defaultdict(list) + total_time_list = [] + total_slow_down = [] + total_workload_dist = defaultdict(def_value) + total_real_time_workload_dist = defaultdict(def_value) + real_time_workload_times_dist = defaultdict(list) + real_time_workload_workloads_dist = defaultdict(list) + real_time_workload_requests_dist = defaultdict(list) + min_time = sys.maxsize + # list[0] is meet deadline number, list[1] is miss deadline number + delays_dict = defaultdict(list) + max_latency_dist = defaultdict(def_value) + total_deadline = 0 + miss_deadline_dist = defaultdict(def_value) + meet_deadline_dist = defaultdict(def_value) + meet_deadline = 0 + miss_deadline = 0 + max_sc = 0 + total_interrupts = 0; + fo = open(file_dir, "r+") + for line in fo: + line = line.strip() + if "meet" in line: + meet_deadline += 1 + name = line.split(" ")[6] + cleanup = line.split(" ")[8] + deadline = int(line.split(" ")[9]) + tid = line.split(" ")[0] + request_counter[name] += 1 + total_time = int(line.split(" ")[3]) + total_time_dist[name].append(total_time) + if name == "fib": + total_slow_down.append(round((float(total_time) / deadline), 2)) + else: + total_slow_down.append(round((float(total_time) / deadline), 2)) + total_time_list.append(total_time) + thread_times[tid].append(total_time) + if total_time > max_latency_dist[name]: + max_latency_dist[name] = total_time + meet_deadline_dist[name] += 1 + exe_time = int(line.split(" ")[4]) + if exe_time > max_exe_time: + max_exe_time = exe_time + running_times[name].append(exe_time); + queue_time = int(line.split(" ")[5]) + queuing_times[name].append(queue_time); + thread_running_times[tid].append(exe_time); + t2_cleanup[tid].append(cleanup) + if "miss" in line: + miss_deadline += 1 + name = line.split(" ")[6] + cleanup = line.split(" ")[8] + deadline = int(line.split(" ")[9]) + tid = line.split(" ")[0] + total_time = int(line.split(" ")[3]) + if total_time > max_latency_dist[name]: + max_latency_dist[name] = total_time + request_counter[name] += 1 + total_time_dist[name].append(total_time) + total_time_list.append(total_time) + if name == "fib": + total_slow_down.append(round((float(total_time) / deadline), 2)) + else: + total_slow_down.append(round((float(total_time) / deadline), 2)) + thread_times[tid].append(total_time) + miss_deadline_dist[name] += 1 + exe_time = int(line.split(" ")[4]) + if exe_time > max_exe_time: + max_exe_time = exe_time + running_times[name].append(exe_time); + queue_time = int(line.split(" ")[5]) + queuing_times[name].append(queue_time); + thread_running_times[tid].append(exe_time); + t2_cleanup[tid].append(cleanup) + #print("name:", name) + if "throughput" in line: + throughput = line.split(" ")[1] + interrupt = line.split(" ")[15] + total_interrupts += int(interrupt); + ### calculate the execution time + #if "memory" in line or "total_time" in line or "min" in line or "miss" in line or "meet" in line or "time " in line or "scheduling count" in line or "thread id" in line: + # continue + if "pthroughput" in line: + tid = line.split(" ")[2] + throughput = line.split(" ")[3] + thread_throughput[tid].append(throughput) + + miss_deadline_percentage = (miss_deadline * 100) / (miss_deadline + meet_deadline) + print("meet deadline num:", meet_deadline) + print("miss deadline num:", miss_deadline) + print("total requests:", meet_deadline + miss_deadline) + print("miss deadline percentage:", miss_deadline_percentage) + print("throughput:", throughput) + print("total interrupts:", total_interrupts) + + for key,value in request_counter.items(): + print(key, ":", str(value), "proportion:", (100*value)/(meet_deadline + miss_deadline)) + for key,value in total_time_dist.items(): + a = np.array(value) + p = np.percentile(a, int(percentage)) + print(key + " " + percentage + " percentile is:" + str(p) + " mean is:" + str(np.mean(value)) + " max latency is:" + str(max_latency_dist[key])) + #total_cpu_times = 0 + weighted_miss_rate = 0 + for key,value in meet_deadline_dist.items(): + # total_cpu_times += value * fun_execution_time[key] + miss_value = miss_deadline_dist[key] + total_request = miss_value + value + miss_rate = (miss_value * 100) / total_request + weighted_miss_rate = weighted_miss_rate + miss_rate * weights_dict[key] + + print("type ", key, " miss deadline rate:", str(miss_rate)); + print("type ", key, " total meet requests:", str(value), " total miss requests:", str(miss_value)) + # print(func_name_dict[key] + " miss deadline rate:" + str(miss_rate) + " miss count is:" + str(miss_value) + " total request:" + str(total_request)) + #print("effective total cpu times:", total_cpu_times) + #for key,value in real_time_workload_times_dist.items(): + # real_time_workload_times_dist[key] = [x - min_time for x in value] + print("weighted miss rate:", weighted_miss_rate) + for key,value in running_times.items(): + #print("function times:", func_name_with_id[key], np.median(total_times[key]), np.median(running_times[key]), np.median(queuing_times[key]), np.median(runnable_times[key]), np.median(blocked_times[key]), np.median(initializing_times[key])) + print("function :", key, "median total:", np.median(total_time_dist[key]), "exec:", np.median(running_times[key]), "queue:", np.median(queuing_times[key])) + print(len(value)) + print(len(queuing_times[key])) + print(len(total_time_dist[key])) + total_time_array = np.array(total_time_list) + p_99 = np.percentile(total_time_array, 99) + p_99_9 = np.percentile(total_time_array, 99.9) + p_99_99 = np.percentile(total_time_array, 99.99) + print("99 percentile latency is ", p_99) + print("99.9 percentile latency is ", p_99_9) + print("99.99 percentile latency is ", p_99_99) + total_time_slow_down = np.array(total_slow_down) + p_99_slow_down = np.percentile(total_time_slow_down, 99) + p_99_9_slow_down = np.percentile(total_time_slow_down, 99.9) + p_99_99_slow_down = np.percentile(total_time_slow_down, 99.99) + print("99 percentile slow down is ", p_99_slow_down) + print("99.9 percentile slow down is ", p_99_9_slow_down) + print("99.99 percentile slow down is ", p_99_99_slow_down) + print("max exe time:", max_exe_time) + js_latency = json.dumps(total_time_list) + f_latency = open("total_time_list.txt", 'w') + f_latency.write(js_latency) + f_latency.close() + js = json.dumps(total_time_dist) + f = open("total_time.txt", 'w') + f.write(js) + f.close() + + js1 = json.dumps(queuing_times) + f1 = open("queuing_time.txt", 'w') + f1.write(js1) + f1.close() + + js5 = json.dumps(thread_running_times) + f5 = open("running_time.txt", 'w') + f5.write(js5) + f5.close() + + js2 = json.dumps(thread_times) + f2 = open("thread_time.txt", 'w') + f2.write(js2) + f2.close() + + js3 = json.dumps(thread_throughput) + f3 = open("thread_throughput.txt", 'w') + f3.write(js3) + f3.close() + + js4 = json.dumps(t2_cleanup) + f4 = open("cleanup.txt", 'w') + f4.write(js4) + f4.close() + +if __name__ == "__main__": + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], " " " ") + sys.exit() + count_miss_or_meet_deadline_requests(argv[0], argv[1]) diff --git a/runtime/tests/monitor_mem.sh b/runtime/tests/monitor_mem.sh new file mode 100755 index 000000000..74092809c --- /dev/null +++ b/runtime/tests/monitor_mem.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +function usage { + echo "$0 [output file]" + exit 1 +} + +if [ $# != 1 ] ; then + usage + exit 1; +fi + +output=$1 + +pid=`ps -ef|grep "sledgert"|grep -v grep |awk '{print $2}'` + +if [ -z "$pid" ]; then + echo "No process found. Exiting." + exit 1 +fi + +interval=1 # 采样间隔时间,单位为秒 +samples=20 # 采样次数 + +total_rss=0 +count=0 + +while [ $count -lt $samples ]; do + # 使用ps命令获取RSS (单位为KB) + rss=$(ps -o rss= -p $pid) + + if [ -n "$rss" ]; then + total_rss=$((total_rss + rss)) + count=$((count + 1)) + else + echo "process $pid not exists or exits" + exit 1 + fi + + sleep $interval # 采样间隔 +done + +# 计算平均RSS +average_rss=$((total_rss / samples)) + +echo "avg RSS (KB): $average_rss" > $output + diff --git a/runtime/tests/no_hyperthreads.sh b/runtime/tests/no_hyperthreads.sh new file mode 100755 index 000000000..5c949bdfc --- /dev/null +++ b/runtime/tests/no_hyperthreads.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# DISCLAIMER: +# Script taken from http://askubuntu.com/questions/201937/ubuntu-detect-4-cpus-and-i-have-only-2-cpus + + +echo "Disabling hyperthreading..." + +CPUS_TO_SKIP=" $(cat /sys/devices/system/cpu/cpu*/topology/thread_siblings_list | sed 's/[^0-9].*//' | sort | uniq | tr "\r\n" " ") " + +# Disable Hyperthreading +for CPU_PATH in /sys/devices/system/cpu/cpu[0-9]*; do + CPU="$(echo $CPU_PATH | tr -cd "0-9")" + echo "$CPUS_TO_SKIP" | grep " $CPU " > /dev/null + if [ $? -ne 0 ]; then + echo 0 > $CPU_PATH/online + fi +done + +lscpu | grep -i -E "^CPU\(s\):|core|socket" + diff --git a/runtime/tests/ori_sledge_tests/.gitignore b/runtime/tests/ori_sledge_tests/.gitignore new file mode 100644 index 000000000..d786eca17 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/.gitignore @@ -0,0 +1,11 @@ +res +perf.data +perf.data.old +samples +perf.log +http_perf.log +log.csv +*res.dat +result.dat +result.jpg +rt.log diff --git a/runtime/tests/ori_sledge_tests/curl.sh b/runtime/tests/ori_sledge_tests/curl.sh new file mode 100755 index 000000000..70a2cc6d2 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/curl.sh @@ -0,0 +1,6 @@ +#curl -I -w "Total time: %{time_total}s\n" http://10.10.1.1:31850/empty +#curl -w "Total time: %{time_total}s\n" http://10.10.1.1:31850/empty http://10.10.1.1:31850/empty http://10.10.1.1:31850/empty http://10.10.1.1:31850/empty +#curl -I -H "Connection: keep-alive" -w "Total time: %{time_total}s TCP Connect time: %{time_connect}s\n" -o /dev/null -s http://10.10.1.1:31850/empty http://10.10.1.1:31850/empty + + +curl -H 'Expect:' -H "Content-Type: image/bmp" --data-binary "@frog5.bmp" "10.10.1.1:31850/classify" diff --git a/runtime/tests/ori_sledge_tests/debug.sh b/runtime/tests/ori_sledge_tests/debug.sh new file mode 100755 index 000000000..3e87c2bbd --- /dev/null +++ b/runtime/tests/ori_sledge_tests/debug.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Executes the runtime in GDB +# Substitutes the absolute path from the container with a path relatively derived from the location of this script +# This allows debugging outside of the Docker container +# Also disables pagination and stopping on SIGUSR1 + +declare project_path="$( + cd "$(dirname "$1")/../.." + pwd +)" +path=`pwd` +echo $project_path +cd $project_path/runtime/bin +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=true +export SLEDGE_DISABLE_PREEMPTION=true +export SLEDGE_SANDBOX_PERF_LOG=$path/srsf.log +export SLEDGE_NWORKERS=9 +export SLEDGE_FIRST_WORKER_COREID=4 +export SLEDGE_NLISTENERS=3 +export SLEDGE_WORKER_GROUP_SIZE=3 +#export SLEDGE_DISPATCHER=DARC +export SLEDGE_DISPATCHER=EDF_INTERRUPT +#export SLEDGE_DISPATCHER=SHINJUKU +export LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" +#gdb --eval-command="handle SIGUSR1 nostop" \ +# --eval-command="set pagination off" \ +# --eval-command="set substitute-path /sledge/runtime $project_path/runtime" \ +# --eval-command="run ../tests/fib.json" +# ./sledgert + +gdb --eval-command="handle SIGUSR1 nostop" \ + --eval-command="handle SIGUSR1 noprint" \ + ./sledgert diff --git a/runtime/tests/ori_sledge_tests/empty.json b/runtime/tests/ori_sledge_tests/empty.json new file mode 100644 index 000000000..d449eb563 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/empty.json @@ -0,0 +1,21 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/empty", + "path": "empty.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/ori_sledge_tests/fib.json b/runtime/tests/ori_sledge_tests/fib.json new file mode 100644 index 000000000..a5840cc95 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/fib.json @@ -0,0 +1,29 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/fib", + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1, + "relative-deadline-us": 10, + "http-resp-content-type": "text/plain" + }, + { + "route": "/fib2", + "path": "fibonacci.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 6480, + "relative-deadline-us": 64800, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/ori_sledge_tests/hash.json b/runtime/tests/ori_sledge_tests/hash.json new file mode 100644 index 000000000..8c6212132 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/hash.json @@ -0,0 +1,33 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/hash", + "request-type": 1, + "n-resas": 2, + "path": "hash.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 5, + "relative-deadline-us": 50, + "http-resp-content-type": "text/plain" + }, + { + "route": "/hash2", + "request-type": 2, + "n-resas": 1, + "path": "hash.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 6480, + "relative-deadline-us": 64800, + "http-resp-content-type": "text/plain" + } + ] + + } + +] + diff --git a/runtime/tests/ori_sledge_tests/id_rsa b/runtime/tests/ori_sledge_tests/id_rsa new file mode 100644 index 000000000..832cdbcd4 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEA2bxGJUQa7ywL7I6prE75oA2fE/AfTHTi5KzUEh+LezA/KObP +Aa7FgcWUu/QiwCz/aVMvIeVms7CZuu/QJJ9TEinSdxjHixoNhZvbBgLhweCje2YR +MUl9ynPsaVZwb/DebloVzlweXRkXKLCfx3s+HXDSHMpDgMhbF5AiehxQCXCUrNzE +/e6d217PmX02+VRQPwVWycu5/Mi+mMvlgN/e1XggHDYhvX6t4U0Am5KZ4kqdxXYP +T0x6gXxcLi6Ulb15LNCqbbdbFhfYg+tWlqxqIyTHJO2kSw4VNINxIR0dT5FYbxFV +4DL9msYQ+ePXUUym3LnPix0Bt9EQLPMeewYhYwIBIwKCAQAlU3m9PuALvmhxsg51 +T13+LjiHEziQw5ScOuKGw5RBAPTicLXFxjB8pYc2KdoSUNtT4l/Z7M/HCFTeN71I +G1ARvgbSloibgM8eN/mpQlKWQ8RBCi7VP8xr2Vu6SVUagRAwLLNH8ojcwnj/qT/2 +T6Q/j679nwRB9nYEChSJ9jmbN437tvrLSE1ZxvUIzV96Sd9aGaOjg4Ezyb7KqGSR +0pFKiufTAoAtUcpQ/hYrQ88KFgbFFNm4yRz0dfNpOekdio/IXs2WZ8688k8aZ1OS +DDmFiMLyHxZmp6s5d2pJsLQBIGGWMqxm7a2xeJHY6oLc3DVIRfoJqkFDNWXLlPyR +eBAbAoGBAPPvG4koPGbJ7MoJF6DXTg8bjdXDCPhKL5JoU7y/OX7vKdYLX69RYxs+ +ubILTM/7TTC33nEhqty+GWVVOkwOlap99vmWp2hhrjelLYPJmUsQ7aMWUM2Fsxcw +S/NH8RUeaM0F1QqwJEDbdvKjOrgteVQmtPaFo8LFHKmyHF1PsrQ/AoGBAOSBay+J +Nx6FvsWHt5PYrKpAjJvM7lAEgFM89zNvBLsIzqxsnTFjvnZ0ySKT5OeBfPA6nf13 +tx+GsurzIniwKjKeBgGUXRIQ5CjEuGtOdnox7rLfkiluiOyKVDyApUN/66d9FLLX +pEeJZSDlUSPGJBvlwfvNGBeVUj9XRv/awbndAoGAfXOnwuF+Jjv2HsLY86HtoV6g +txPYujTIAicGflO3K1ZtSYIxNZeSDgMAajGyeZcvAxy7iqOZs1pzdfFRLm3mkjIn +Pof+UvBoOd/rhZrhH0qI14fRyMhqu3fsi76ZPg+jnKPp6D1UeSBpDxIeMtWO2tIT +7H8+RukHbTcHRe55KX8CgYEAlikpLd3Tw5m34OQoLfTJPK4TQc/Pzi7X/C9gniRi +McP1h0APhtslY8kWdc7m4UZ2rH5KkJ8gkQ9fobW3kSNO7hASk1LeEy+r4EbCVSTu +xVQDQlhnXQ4fdt6PIHHLsAOa28c5fNbZq1pJxSj6zl2isz82VQMedeXIVYJ/HSla +u/cCgYAMNf9At68UjxBECLQOxfLU2GtJBQMP1r3QJzz6/PJqctgJZWzDL/SIg24F +hp6cEaaCiUYV158IShdeTUZpai7UNooQ9wijDYYcodXE/9HnNGgVjP+YqvpnizGx +yFiYiEcowoetWDVOkDzGHDVAtHdwlWBc4D4frJ5kNipMlEEw5Q== +-----END RSA PRIVATE KEY----- diff --git a/runtime/tests/ori_sledge_tests/kill_sledge.sh b/runtime/tests/ori_sledge_tests/kill_sledge.sh new file mode 100755 index 000000000..8069a8593 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/kill_sledge.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +pid=`ps -ef|grep "sledgert"|grep -v grep |awk '{print $2}'` +echo $pid +sudo kill -2 $pid +sleep 2 +sudo kill -9 $pid diff --git a/runtime/tests/ori_sledge_tests/measure_old_sledge_cost.sh b/runtime/tests/ori_sledge_tests/measure_old_sledge_cost.sh new file mode 100755 index 000000000..d84a3ac2a --- /dev/null +++ b/runtime/tests/ori_sledge_tests/measure_old_sledge_cost.sh @@ -0,0 +1,28 @@ +#!/bin/bash +function usage { + echo "$0 [repeat count]" + exit 1 +} + +if [ $# != 1 ] ; then + usage + exit 1; +fi + +chmod 400 ./id_rsa +remote_ip="10.10.1.1" + +repeat_count=$1 +> old_sledge.log + +path="/my_mount/ori_sledge/sledge-serverless-framework/runtime/tests" +for(( i=0;i<$repeat_count;i++ )) do + #echo "i is $i" + #echo "start server..." + ssh -o stricthostkeychecking=no -i ./id_rsa xiaosuGW@$remote_ip "sudo $path/start.sh > 1.txt 2>&1 &" + sleep 1 + #echo "start client..." + #./curl.sh >> old_sledge.log + ./curl.sh + ssh -o stricthostkeychecking=no -i ./id_rsa xiaosuGW@$remote_ip "sudo $path/kill_sledge.sh" > /dev/null 2>&1 +done diff --git a/runtime/tests/ori_sledge_tests/measure_throughput.sh b/runtime/tests/ori_sledge_tests/measure_throughput.sh new file mode 100755 index 000000000..19582973b --- /dev/null +++ b/runtime/tests/ori_sledge_tests/measure_throughput.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +num_runs=$1 +chmod 400 ./id_rsa +remote_ip="128.110.218.253" + +ulimit -n 655350 +path="/my_mount/old_sledge/sledge-serverless-framework/runtime/tests" + +for i in $(seq 1 $num_runs); do + ssh -o stricthostkeychecking=no -i ./id_rsa xiaosuGW@$remote_ip "$path/start.sh > 1.txt 2>&1 &" + sleep 5 + echo "Running test $i..." + hey -disable-compression -disable-redirects -cpus 20 -z "20"s -c "300" -m POST "http://10.10.1.1:31850/empty" > "output_$i.txt" + ssh -o stricthostkeychecking=no -i ./id_rsa xiaosuGW@$remote_ip "sudo $path/kill_sledge.sh" +done + +echo "All tests completed." diff --git a/runtime/tests/ori_sledge_tests/old_sledge.log b/runtime/tests/ori_sledge_tests/old_sledge.log new file mode 100644 index 000000000..c09f84f29 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/old_sledge.log @@ -0,0 +1 @@ +Total time: 0.000415s diff --git a/runtime/tests/ori_sledge_tests/parse_avg_throughput.sh b/runtime/tests/ori_sledge_tests/parse_avg_throughput.sh new file mode 100755 index 000000000..8857324ca --- /dev/null +++ b/runtime/tests/ori_sledge_tests/parse_avg_throughput.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# 文件名匹配模式,例如 output_*.txt +files="output_*.txt" + +# 初始化总和和计数 +total=0 +count=0 + +# 遍历每个文件 +for file in $files; do + if [ -f "$file" ]; then + # 提取 Requests/sec 后的数字 + value=$(grep "Requests/sec:" "$file" | awk '{print $2}') + if [[ $value =~ ^[0-9.]+$ ]]; then + total=$(echo "$total + $value" | bc) + count=$((count + 1)) + fi + fi +done + +# 计算平均值 +if [ $count -gt 0 ]; then + average=$(echo "scale=4; $total / $count" | bc) + echo "Average Requests/sec: $average" +else + echo "No valid data found." +fi diff --git a/runtime/tests/ori_sledge_tests/parse_batch.py b/runtime/tests/ori_sledge_tests/parse_batch.py new file mode 100644 index 000000000..8fbca84db --- /dev/null +++ b/runtime/tests/ori_sledge_tests/parse_batch.py @@ -0,0 +1,153 @@ +import re +import os +import sys +from collections import defaultdict + +#get all file names which contain key_str +def file_name(file_dir, key_str): + print(file_dir, key_str) + file_list = [] + rps_list = [] + + for root, dirs, files in os.walk(file_dir): + print(files, root, dirs) + for file_i in files: + if file_i.find(key_str) >= 0: + full_path = os.path.join(os.getcwd() + "/" + root, file_i) + #print(full_path) + segs = file_i.split('-') + if len(segs) < 2: + continue + rps=segs[1] + rps=rps.split(".")[0] + file_list.append(full_path) + rps_list.append(rps) + + file_list = sorted(file_list, key = lambda x: int(x.split('-')[-1].split(".")[0])) + rps_list = sorted(rps_list) + print(file_list) + print(rps_list) + return file_list, rps_list + +def get_values(key, files_list, latency_dict, slow_down_dict, slow_down_99_9_dict, latency_99_9_dict, slow_down_99_99_dict, latency_99_99_dict): + for file_i in files_list: + cmd='sudo python3 ./meet_deadline_percentage.py %s 99' % file_i + rt=os.popen(cmd).read().strip() + print(rt) + # Define regular expressions to match the desired values + latency_rule = r'99 percentile latency is\s*([\d.]+)' + slow_down_rule = r'99 percentile slow down is\s*([\d.]+)' + latency_99_9_rule = r'99.9 percentile latency is\s*([\d.]+)' + slow_down_99_9_rule = r'99.9 percentile slow down is\s*([\d.]+)' + latency_99_99_rule = r'99.99 percentile latency is\s*([\d.]+)' + slow_down_99_99_rule = r'99.99 percentile slow down is\s*([\d.]+)' + + # Use the regular expressions to find the values + latency_match = re.search(latency_rule, rt) + slow_down_match = re.search(slow_down_rule, rt) + latency_99_9_match = re.search(latency_99_9_rule, rt) + slow_down_99_9_match = re.search(slow_down_99_9_rule, rt) + latency_99_99_match = re.search(latency_99_99_rule, rt) + slow_down_99_99_match = re.search(slow_down_99_99_rule, rt) + + # Check if matches were found and extract the values + if latency_match: + latency_value = 0 + latency_value = latency_match.group(1) + print("99th latency is:", latency_value) + latency_dict[key].append(latency_value) + + if slow_down_match: + slow_down_value = 0 + slow_down_value = slow_down_match.group(1) + print("99th slow down is:", slow_down_value) + slow_down_dict[key].append(slow_down_value) + + if latency_99_9_match: + latency_value = 0 + latency_value = latency_99_9_match.group(1) + print("99.9th latency is:", latency_value) + latency_99_9_dict[key].append(latency_value) + + if slow_down_99_9_match: + slow_down_value = 0 + slow_down_value = slow_down_99_9_match.group(1) + print("99.9th slow down is:", slow_down_value) + slow_down_99_9_dict[key].append(slow_down_value) + + if latency_99_99_match: + latency_value = 0 + latency_value = latency_99_99_match.group(1) + print("99.99th latency is:", latency_value) + latency_99_99_dict[key].append(latency_value) + + if slow_down_99_99_match: + slow_down_value = 0 + slow_down_value = slow_down_99_99_match.group(1) + print("99.99th slow down is:", slow_down_value) + slow_down_99_99_dict[key].append(slow_down_value) + +if __name__ == "__main__": + import json + #file_folders = ['SHINJUKU', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + file_folders = ['SHINJUKU_5', 'SHINJUKU_100', 'SHINJUKU_200', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU'] + latency = defaultdict(list) + slow_down = defaultdict(list) + slow_down_99_9 = defaultdict(list) + latency_99_9 = defaultdict(list) + slow_down_99_99 = defaultdict(list) + latency_99_99 = defaultdict(list) + + rps_list = [] + + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], "[file key]") + sys.exit() + + for key in file_folders: + files_list, rps_list = file_name(key, argv[0]) + get_values(key, files_list, latency, slow_down, slow_down_99_9, latency_99_9, slow_down_99_99, latency_99_99) + + print("99 latency:") + for key, value in latency.items(): + print(key, ":", value) + print("99 slow down:") + for key, value in slow_down.items(): + print(key, ":", value) + + js1 = json.dumps(latency) + f1 = open("99_latency.txt", 'w') + f1.write(js1) + f1.close() + + js2 = json.dumps(slow_down) + f2 = open("99_slow_down.txt", 'w') + f2.write(js2) + f2.close() + + js4 = json.dumps(latency_99_9) + f4 = open("99_9_latency.txt", 'w') + f4.write(js4) + f4.close() + + js5 = json.dumps(slow_down_99_9) + f5 = open("99_9_slow_down.txt", 'w') + f5.write(js5) + f5.close() + + js6 = json.dumps(latency_99_99) + f6 = open("99_99_latency.txt", 'w') + f6.write(js6) + f6.close() + + js7 = json.dumps(slow_down_99_99) + f7 = open("99_99_slow_down.txt", 'w') + f7.write(js7) + f7.close() + + js3 = json.dumps(rps_list) + f3 = open("rps.txt", 'w') + f3.write(js3) + f3.close() diff --git a/runtime/tests/ori_sledge_tests/parse_cost.py b/runtime/tests/ori_sledge_tests/parse_cost.py new file mode 100644 index 000000000..17e3b3b30 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/parse_cost.py @@ -0,0 +1,47 @@ +import sys + +if len(sys.argv) != 2: + print("Usage: python script.py ") + sys.exit(1) + +filename = sys.argv[1] + +try: + with open(filename, "r") as file: + lines = file.readlines() +except FileNotFoundError: + print("File not found:", filename) + sys.exit(1) + +total_cold_time = 0 +total_tcp_conn = 0 +total_tcp_conn2 = 0 +total_warm_time = 0 +count = 0 + +for i in range(0, len(lines), 2): + time_str_1 = lines[i].split(" ")[2].strip() + tcp_cost_str = lines[i].split(" ")[6].strip() + time_str_2 = lines[i+1].split(" ")[2].strip() + tcp_cost_str2 = lines[i+1].split(" ")[6].strip() + time_value_1 = float(time_str_1[:-1]) + time_value_2 = float(time_str_2[:-1]) + tcp_cost = float(tcp_cost_str[:-1]) + tcp_cost2 = float(tcp_cost_str2[:-1]) + + print("cold:", time_value_1, " warm:", time_value_2) + total_cold_time += time_value_1 + total_warm_time += time_value_2 + total_tcp_conn += tcp_cost + total_tcp_conn2 += tcp_cost2 + count += 1 + +average_cold_time = total_cold_time / count +average_warm_time = total_warm_time / count +average_tcp_conn = total_tcp_conn / count +average_tcp_conn2 = total_tcp_conn2 / count + +print("Average cold time:", average_cold_time, "s") +print("Average warm time:", average_warm_time, "s") +print("Average tcp conn:", average_tcp_conn, "s") +print("Average tcp conn2:", average_tcp_conn2, "s") diff --git a/runtime/tests/ori_sledge_tests/parse_cpu_batch.py b/runtime/tests/ori_sledge_tests/parse_cpu_batch.py new file mode 100644 index 000000000..1be6c1212 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/parse_cpu_batch.py @@ -0,0 +1,78 @@ +import re +import os +import sys +from collections import defaultdict + +sending_rate_dict = defaultdict(list) +service_rate_dict = defaultdict(list) + +#get all file names which contain key_str +def file_name(file_dir, key_str): + print(file_dir, key_str) + file_list = [] + rps_list = [] + + for root, dirs, files in os.walk(file_dir): + print("file:", files) + print("root:", root) + print("dirs:", dirs) + for file_i in files: + if file_i.find(key_str) >= 0: + full_path = os.path.join(os.getcwd() + "/" + root, file_i) + #print(full_path) + segs = file_i.split('-') + if len(segs) < 2: + continue + rps=segs[1] + print("rps---------", rps) + rps=rps.split(".")[0] + file_list.append(full_path) + rps_list.append(rps) + + file_list = sorted(file_list, key = lambda x: int(x.split('-')[-1].split(".")[0])) + rps_list = sorted(rps_list) + print(file_list) + print(rps_list) + return file_list, rps_list + +def get_values(key, files_list, cpu_usages_dict): + for file_i in files_list: + cmd="awk '/Average:/ {print $8}' %s" % file_i + rt=os.popen(cmd).read().strip() + cpu_count=float(rt)/100 + cpu_count=round(cpu_count, 2) + print(cpu_count) + cpu_usages_dict[key].append(cpu_count) + + +if __name__ == "__main__": + import json + #file_folders = ['SHINJUKU', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU_7', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU', 'DARC', 'EDF_SRSF_INTERRUPT'] + file_folders = ['EDF_INTERRUPT-disable-busy-loop-false-disable-autoscaling-true-9','EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-false-9', 'EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-true-27'] + #file_folders = ['EDF_INTERRUPT','EDF_SRSF_INTERRUPT_1'] + #file_folders = ['DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU'] + cpu_usages_dict = defaultdict(list) + rps_list = [] + + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], "[file key]") + sys.exit() + + for key in file_folders: + files_list, rps_list = file_name(key, argv[0]) + get_values(key, files_list, cpu_usages_dict) + + print("cpu usage:") + for key, value in cpu_usages_dict.items(): + print(key, ":", value) + + js1 = json.dumps(cpu_usages_dict) + f1 = open("cpu.txt", 'w') + f1.write(js1) + f1.close() + + diff --git a/runtime/tests/ori_sledge_tests/parse_power_consumption.py b/runtime/tests/ori_sledge_tests/parse_power_consumption.py new file mode 100644 index 000000000..55e0a208d --- /dev/null +++ b/runtime/tests/ori_sledge_tests/parse_power_consumption.py @@ -0,0 +1,94 @@ +import re +import os +import sys +from collections import defaultdict + +sending_rate_dict = defaultdict(list) +service_rate_dict = defaultdict(list) + +#get all file names which contain key_str +def file_name(file_dir, key_str): + print(file_dir, key_str) + file_list = [] + rps_list = [] + + for root, dirs, files in os.walk(file_dir): + print("file:", files) + print("root:", root) + print("dirs:", dirs) + for file_i in files: + if file_i.find(key_str) >= 0: + full_path = os.path.join(os.getcwd() + "/" + root, file_i) + #print(full_path) + segs = file_i.split('-') + if len(segs) < 2: + continue + rps=segs[1] + print("rps---------", rps) + rps=rps.split(".")[0] + file_list.append(full_path) + rps_list.append(rps) + + file_list = sorted(file_list, key = lambda x: int(x.split('-')[-1].split(".")[0])) + rps_list = sorted(rps_list) + print(file_list) + print(rps_list) + return file_list, rps_list + +def get_values(key, files_list, core_energy_consum_dict, pkg_energy_consum_dict): + for file_i in files_list: + with open(file_i, 'r') as file: + print(file_i) + lines = file.readlines() + + core_line_index = next(i for i, line in enumerate(lines) if "Domain CORE" in line) + pkg_line_index = next(i for i, line in enumerate(lines) if "Domain PKG" in line) + + core_value = float(lines[core_line_index+1].split()[-2]) + pkg_value = float(lines[pkg_line_index+1].split()[-2]) + + #print("Domain CORE 的值:", core_value) + #print("Domain PKG 的值:", pkg_value) + + core_energy_consum_dict[key].append(core_value) + pkg_energy_consum_dict[key].append(pkg_value) + + +if __name__ == "__main__": + import json + #file_folders = ['SHINJUKU', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU_7', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU', 'DARC', 'EDF_SRSF_INTERRUPT'] + file_folders = ['EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-true-27', 'EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-false-27'] + #file_folders = ['EDF_INTERRUPT','EDF_SRSF_INTERRUPT_1'] + #file_folders = ['DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU'] + core_energy_consum_dict = defaultdict(list) + pkg_energy_consum_dict = defaultdict(list) + rps_list = [] + + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], "[file key]") + sys.exit() + + for key in file_folders: + files_list, rps_list = file_name(key, argv[0]) + get_values(key, files_list, core_energy_consum_dict, pkg_energy_consum_dict) + + print("core consume:") + for key, value in core_energy_consum_dict.items(): + print(key, ":", value) + for key, value in pkg_energy_consum_dict.items(): + print(key, ":", value) + + js1 = json.dumps(core_energy_consum_dict) + f1 = open("core_consume.txt", 'w') + f1.write(js1) + f1.close() + + js2 = json.dumps(pkg_energy_consum_dict) + f2 = open("pkg_consume.txt", 'w') + f2.write(js2) + f2.close() + diff --git a/runtime/tests/ori_sledge_tests/server.log b/runtime/tests/ori_sledge_tests/server.log new file mode 100644 index 000000000..a749ba460 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/server.log @@ -0,0 +1,8 @@ +id,tenant,route,state,deadline,actual,queued,uninitialized,allocated,initialized,runnable,interrupted,preempted,running_sys,running_user,asleep,returned,complete,error,proc_MHz,memory +1,gwu,/empty,Complete,32400,153009,56541,0,447,56094,846,0,0,23899,71746,0,149836,0,0,3240 +2,gwu,/empty,Complete,32400,112424,46060,0,728,45332,587,0,0,14593,51231,0,128333,0,0,3240 +3,gwu,/empty,Complete,32400,141963,58233,0,728,57505,329,0,0,17530,65918,0,105633,0,0,3240 +4,gwu,/empty,Complete,32400,149483,74730,0,493,74237,305,0,0,19106,55389,0,102531,0,0,3240 +5,gwu,/empty,Complete,32400,28717,12925,0,494,12431,564,0,0,9283,5969,0,66951,0,0,3240 +6,gwu,/empty,Complete,32400,24111,7919,0,423,7496,1058,0,0,9447,5710,0,62064,0,0,3240 +7,gwu,/empty,Complete,32400,30527,15581,0,470,15111,329,0,0,8577,6063,0,65988,0,0,3240 diff --git a/runtime/tests/ori_sledge_tests/start.sh b/runtime/tests/ori_sledge_tests/start.sh new file mode 100755 index 000000000..ed37c86c3 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/start.sh @@ -0,0 +1,41 @@ +#!/bin/bash +ulimit -n 655350 + +function usage { + echo "$0" + exit 1 +} + +if [ $# != 0 ] ; then + usage + exit 1; +fi + +declare project_path="$( + cd "$(dirname "$0")/../.." + pwd +)" +echo $project_path +path=`pwd` +export SLEDGE_DISABLE_PREEMPTION=true +#export SLEDGE_SIGALRM_HANDLER=TRIAGED +export SLEDGE_SCHEDULER=EDF +export SLEDGE_NWORKERS=1 +export SLEDGE_SANDBOX_PERF_LOG=$path/server.log +#echo $SLEDGE_SANDBOX_PERF_LOG +cd $project_path/runtime/bin +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_big_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_armcifar10.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_png2bmp.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/mulitple_linear_chain.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing3.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/fib.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/hash.json +LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/empty.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_sodresize.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_sodresize.json + diff --git a/runtime/tests/ori_sledge_tests/start_monitor.sh b/runtime/tests/ori_sledge_tests/start_monitor.sh new file mode 100755 index 000000000..19c14b87c --- /dev/null +++ b/runtime/tests/ori_sledge_tests/start_monitor.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +function usage { + echo "$0 [pidstat file]" + exit 1 +} + +if [ $# != 1 ] ; then + usage + exit 1; +fi + +pidstat_file=$1 +sledge_pid=`ps -ef|grep "sledgert"|grep -v grep |awk '{print $2}'` +sleep 6 && pidstat -u -p $sledge_pid 1 1800 > $pidstat_file 2>&1 & diff --git a/runtime/tests/ori_sledge_tests/stop_monitor.sh b/runtime/tests/ori_sledge_tests/stop_monitor.sh new file mode 100755 index 000000000..fa67a47b5 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/stop_monitor.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +pid=`ps -ef|grep "pidstat"|grep -v grep |awk '{print $2}'` +echo $pid +kill -2 $pid diff --git a/runtime/tests/ori_sledge_tests/two_curl_requests.log b/runtime/tests/ori_sledge_tests/two_curl_requests.log new file mode 100644 index 000000000..fdcbc0fb3 --- /dev/null +++ b/runtime/tests/ori_sledge_tests/two_curl_requests.log @@ -0,0 +1,200 @@ +Total time: 0.000256s TCP Connect time: 0.000119s +Total time: 0.000155s TCP Connect time: 0.000077s +Total time: 0.000231s TCP Connect time: 0.000100s +Total time: 0.000141s TCP Connect time: 0.000078s +Total time: 0.000275s TCP Connect time: 0.000126s +Total time: 0.000158s TCP Connect time: 0.000080s +Total time: 0.000265s TCP Connect time: 0.000121s +Total time: 0.000228s TCP Connect time: 0.000126s +Total time: 0.000216s TCP Connect time: 0.000091s +Total time: 0.000170s TCP Connect time: 0.000097s +Total time: 0.000214s TCP Connect time: 0.000088s +Total time: 0.000204s TCP Connect time: 0.000112s +Total time: 0.000337s TCP Connect time: 0.000111s +Total time: 0.000174s TCP Connect time: 0.000095s +Total time: 0.000251s TCP Connect time: 0.000115s +Total time: 0.000180s TCP Connect time: 0.000096s +Total time: 0.000210s TCP Connect time: 0.000084s +Total time: 0.000159s TCP Connect time: 0.000091s +Total time: 0.000243s TCP Connect time: 0.000111s +Total time: 0.000154s TCP Connect time: 0.000083s +Total time: 0.000248s TCP Connect time: 0.000100s +Total time: 0.000149s TCP Connect time: 0.000081s +Total time: 0.000237s TCP Connect time: 0.000106s +Total time: 0.000165s TCP Connect time: 0.000084s +Total time: 0.000232s TCP Connect time: 0.000101s +Total time: 0.000167s TCP Connect time: 0.000090s +Total time: 0.000264s TCP Connect time: 0.000114s +Total time: 0.000174s TCP Connect time: 0.000090s +Total time: 0.000222s TCP Connect time: 0.000090s +Total time: 0.000153s TCP Connect time: 0.000076s +Total time: 0.000235s TCP Connect time: 0.000103s +Total time: 0.000168s TCP Connect time: 0.000098s +Total time: 0.000223s TCP Connect time: 0.000100s +Total time: 0.000136s TCP Connect time: 0.000071s +Total time: 0.000226s TCP Connect time: 0.000103s +Total time: 0.000177s TCP Connect time: 0.000095s +Total time: 0.000254s TCP Connect time: 0.000101s +Total time: 0.000167s TCP Connect time: 0.000099s +Total time: 0.000255s TCP Connect time: 0.000108s +Total time: 0.000142s TCP Connect time: 0.000074s +Total time: 0.000223s TCP Connect time: 0.000101s +Total time: 0.000154s TCP Connect time: 0.000076s +Total time: 0.000264s TCP Connect time: 0.000108s +Total time: 0.000150s TCP Connect time: 0.000085s +Total time: 0.000243s TCP Connect time: 0.000107s +Total time: 0.000135s TCP Connect time: 0.000071s +Total time: 0.000237s TCP Connect time: 0.000096s +Total time: 0.000201s TCP Connect time: 0.000093s +Total time: 0.000229s TCP Connect time: 0.000093s +Total time: 0.000167s TCP Connect time: 0.000088s +Total time: 0.000223s TCP Connect time: 0.000090s +Total time: 0.000187s TCP Connect time: 0.000098s +Total time: 0.000222s TCP Connect time: 0.000107s +Total time: 0.000148s TCP Connect time: 0.000073s +Total time: 0.000228s TCP Connect time: 0.000100s +Total time: 0.000173s TCP Connect time: 0.000075s +Total time: 0.000235s TCP Connect time: 0.000100s +Total time: 0.000147s TCP Connect time: 0.000080s +Total time: 0.000258s TCP Connect time: 0.000104s +Total time: 0.000213s TCP Connect time: 0.000121s +Total time: 0.000210s TCP Connect time: 0.000093s +Total time: 0.000174s TCP Connect time: 0.000100s +Total time: 0.000229s TCP Connect time: 0.000100s +Total time: 0.000167s TCP Connect time: 0.000074s +Total time: 0.000218s TCP Connect time: 0.000088s +Total time: 0.000155s TCP Connect time: 0.000077s +Total time: 0.000248s TCP Connect time: 0.000099s +Total time: 0.000141s TCP Connect time: 0.000075s +Total time: 0.000210s TCP Connect time: 0.000089s +Total time: 0.000150s TCP Connect time: 0.000069s +Total time: 0.000246s TCP Connect time: 0.000115s +Total time: 0.000144s TCP Connect time: 0.000079s +Total time: 0.000239s TCP Connect time: 0.000102s +Total time: 0.000174s TCP Connect time: 0.000091s +Total time: 0.000260s TCP Connect time: 0.000101s +Total time: 0.000197s TCP Connect time: 0.000118s +Total time: 0.000232s TCP Connect time: 0.000093s +Total time: 0.000143s TCP Connect time: 0.000078s +Total time: 0.000249s TCP Connect time: 0.000094s +Total time: 0.000139s TCP Connect time: 0.000072s +Total time: 0.000252s TCP Connect time: 0.000114s +Total time: 0.000164s TCP Connect time: 0.000088s +Total time: 0.000214s TCP Connect time: 0.000091s +Total time: 0.000146s TCP Connect time: 0.000079s +Total time: 0.000230s TCP Connect time: 0.000089s +Total time: 0.000147s TCP Connect time: 0.000076s +Total time: 0.000238s TCP Connect time: 0.000108s +Total time: 0.000148s TCP Connect time: 0.000080s +Total time: 0.000269s TCP Connect time: 0.000097s +Total time: 0.000167s TCP Connect time: 0.000088s +Total time: 0.000257s TCP Connect time: 0.000123s +Total time: 0.000221s TCP Connect time: 0.000070s +Total time: 0.000246s TCP Connect time: 0.000098s +Total time: 0.000150s TCP Connect time: 0.000073s +Total time: 0.000244s TCP Connect time: 0.000108s +Total time: 0.000161s TCP Connect time: 0.000082s +Total time: 0.000225s TCP Connect time: 0.000103s +Total time: 0.000158s TCP Connect time: 0.000079s +Total time: 0.000241s TCP Connect time: 0.000099s +Total time: 0.000153s TCP Connect time: 0.000081s +Total time: 0.000229s TCP Connect time: 0.000096s +Total time: 0.000184s TCP Connect time: 0.000110s +Total time: 0.000230s TCP Connect time: 0.000103s +Total time: 0.000150s TCP Connect time: 0.000075s +Total time: 0.000210s TCP Connect time: 0.000103s +Total time: 0.000152s TCP Connect time: 0.000065s +Total time: 0.000257s TCP Connect time: 0.000123s +Total time: 0.000143s TCP Connect time: 0.000075s +Total time: 0.000218s TCP Connect time: 0.000088s +Total time: 0.000155s TCP Connect time: 0.000079s +Total time: 0.000257s TCP Connect time: 0.000117s +Total time: 0.000188s TCP Connect time: 0.000111s +Total time: 0.000240s TCP Connect time: 0.000102s +Total time: 0.000150s TCP Connect time: 0.000080s +Total time: 0.000237s TCP Connect time: 0.000104s +Total time: 0.000157s TCP Connect time: 0.000075s +Total time: 0.000268s TCP Connect time: 0.000132s +Total time: 0.000135s TCP Connect time: 0.000079s +Total time: 0.000228s TCP Connect time: 0.000095s +Total time: 0.000151s TCP Connect time: 0.000081s +Total time: 0.000226s TCP Connect time: 0.000103s +Total time: 0.000139s TCP Connect time: 0.000075s +Total time: 0.000235s TCP Connect time: 0.000105s +Total time: 0.000175s TCP Connect time: 0.000104s +Total time: 0.000224s TCP Connect time: 0.000103s +Total time: 0.000160s TCP Connect time: 0.000090s +Total time: 0.000217s TCP Connect time: 0.000095s +Total time: 0.000167s TCP Connect time: 0.000094s +Total time: 0.000246s TCP Connect time: 0.000109s +Total time: 0.000159s TCP Connect time: 0.000081s +Total time: 0.000300s TCP Connect time: 0.000141s +Total time: 0.000151s TCP Connect time: 0.000073s +Total time: 0.000268s TCP Connect time: 0.000105s +Total time: 0.000146s TCP Connect time: 0.000084s +Total time: 0.000240s TCP Connect time: 0.000100s +Total time: 0.000131s TCP Connect time: 0.000067s +Total time: 0.000231s TCP Connect time: 0.000108s +Total time: 0.000159s TCP Connect time: 0.000092s +Total time: 0.000233s TCP Connect time: 0.000109s +Total time: 0.000146s TCP Connect time: 0.000078s +Total time: 0.000247s TCP Connect time: 0.000111s +Total time: 0.000144s TCP Connect time: 0.000076s +Total time: 0.000224s TCP Connect time: 0.000092s +Total time: 0.000197s TCP Connect time: 0.000105s +Total time: 0.000240s TCP Connect time: 0.000102s +Total time: 0.000161s TCP Connect time: 0.000078s +Total time: 0.000245s TCP Connect time: 0.000106s +Total time: 0.000163s TCP Connect time: 0.000093s +Total time: 0.000298s TCP Connect time: 0.000121s +Total time: 0.000170s TCP Connect time: 0.000097s +Total time: 0.000227s TCP Connect time: 0.000102s +Total time: 0.000188s TCP Connect time: 0.000080s +Total time: 0.000245s TCP Connect time: 0.000103s +Total time: 0.000164s TCP Connect time: 0.000085s +Total time: 0.000219s TCP Connect time: 0.000101s +Total time: 0.000163s TCP Connect time: 0.000090s +Total time: 0.000251s TCP Connect time: 0.000111s +Total time: 0.000204s TCP Connect time: 0.000124s +Total time: 0.000246s TCP Connect time: 0.000111s +Total time: 0.000165s TCP Connect time: 0.000084s +Total time: 0.000230s TCP Connect time: 0.000092s +Total time: 0.000167s TCP Connect time: 0.000089s +Total time: 0.000241s TCP Connect time: 0.000090s +Total time: 0.000164s TCP Connect time: 0.000096s +Total time: 0.000234s TCP Connect time: 0.000100s +Total time: 0.000150s TCP Connect time: 0.000081s +Total time: 0.000233s TCP Connect time: 0.000085s +Total time: 0.000135s TCP Connect time: 0.000069s +Total time: 0.000242s TCP Connect time: 0.000109s +Total time: 0.000222s TCP Connect time: 0.000091s +Total time: 0.000280s TCP Connect time: 0.000112s +Total time: 0.000181s TCP Connect time: 0.000099s +Total time: 0.000241s TCP Connect time: 0.000110s +Total time: 0.000133s TCP Connect time: 0.000069s +Total time: 0.000235s TCP Connect time: 0.000105s +Total time: 0.000164s TCP Connect time: 0.000092s +Total time: 0.000250s TCP Connect time: 0.000111s +Total time: 0.000159s TCP Connect time: 0.000088s +Total time: 0.000309s TCP Connect time: 0.000124s +Total time: 0.000181s TCP Connect time: 0.000100s +Total time: 0.000224s TCP Connect time: 0.000092s +Total time: 0.000144s TCP Connect time: 0.000075s +Total time: 0.000243s TCP Connect time: 0.000100s +Total time: 0.000154s TCP Connect time: 0.000076s +Total time: 0.000225s TCP Connect time: 0.000091s +Total time: 0.000171s TCP Connect time: 0.000089s +Total time: 0.000224s TCP Connect time: 0.000092s +Total time: 0.000160s TCP Connect time: 0.000080s +Total time: 0.000238s TCP Connect time: 0.000106s +Total time: 0.000153s TCP Connect time: 0.000083s +Total time: 0.000243s TCP Connect time: 0.000111s +Total time: 0.000182s TCP Connect time: 0.000109s +Total time: 0.000221s TCP Connect time: 0.000096s +Total time: 0.000186s TCP Connect time: 0.000111s +Total time: 0.000231s TCP Connect time: 0.000101s +Total time: 0.000138s TCP Connect time: 0.000074s +Total time: 0.000223s TCP Connect time: 0.000106s +Total time: 0.000162s TCP Connect time: 0.000093s +Total time: 0.000217s TCP Connect time: 0.000086s +Total time: 0.000150s TCP Connect time: 0.000080s diff --git a/runtime/tests/parse_batch.py b/runtime/tests/parse_batch.py new file mode 100644 index 000000000..0c218a2da --- /dev/null +++ b/runtime/tests/parse_batch.py @@ -0,0 +1,268 @@ +import re +import os +import sys +from collections import defaultdict + +miss_rate_dict = defaultdict(lambda: defaultdict(list)) +seperate_meet_dict = defaultdict(lambda: defaultdict(list)) +total_meet_dict = defaultdict(list) +total_miss_rate_dict = defaultdict(list) +weighted_miss_rate_dict = defaultdict(list) +load_dict = defaultdict(int) +total_requests_dict = defaultdict(list) + + +#get all file names which contain key_str +def file_name(file_dir, key_str): + print(file_dir, key_str) + file_list = [] + rps_list = [] + last_num_list = [] + + for root, dirs, files in os.walk(file_dir): + print("file:", files) + print("root:", root) + print("dirs:", dirs) + for file_i in files: + if file_i.find(key_str) >= 0: + full_path = os.path.join(os.getcwd() + "/" + root, file_i) + print(full_path) + segs = file_i.split('-') + if len(segs) < 2: + continue + rps=segs[1] + print("rps---------", rps) + rps=rps.split(".")[0] + file_list.append(full_path) + rps_list.append(int(rps)) + last_num=segs[2] + last_num=last_num.split(".")[0] + last_num_list.append(int(last_num)) + + pattern = r"server-(\d+)-\d+(\.\d+)?\.log" + + for file in file_list: + match = re.search(pattern, file) + if match: + print(f"Matched: {file}, RPS: {match.group(1)}") + else: + print(f"Not Matched: {file}", file) + + file_list_based_throughput = sorted(file_list, key=lambda x: float(re.search(pattern, x).group(1))) + rps_list = sorted(rps_list) + last_num_list = sorted(last_num_list) + # Sort the list based on the last number in the filename + file_list_based_last_num = sorted(file_list, key=lambda x: int(x.split('-')[-1].split('.')[0])) + + #print(file_list_based_last_num) + print("--------------------------------", rps_list) + return file_list_based_throughput, file_list_based_last_num, rps_list, last_num_list + +def get_values(key, files_list, latency_dict, slow_down_dict, slow_down_99_9_dict, latency_99_9_dict, slow_down_99_99_dict, latency_99_99_dict): + for file_i in files_list: + cmd='sudo python3 ./meet_deadline_percentage.py %s 99' % file_i + rt=os.popen(cmd).read().strip() + print(rt) + # Define regular expressions to match the desired values + latency_rule = r'99 percentile latency is\s*([\d.]+)' + slow_down_rule = r'99 percentile slow down is\s*([\d.]+)' + latency_99_9_rule = r'99.9 percentile latency is\s*([\d.]+)' + slow_down_99_9_rule = r'99.9 percentile slow down is\s*([\d.]+)' + latency_99_99_rule = r'99.99 percentile latency is\s*([\d.]+)' + slow_down_99_99_rule = r'99.99 percentile slow down is\s*([\d.]+)' + + miss_rate_rule = r"type\s+(\d+)\s+miss\s+deadline\s+rate:\s+([\d.]+)" + total_meet_requests_rule = r"type\s+(\d+)\s+total\s+meet\s+requests:\s+([\d.]+)" + total_requests_rule = r"type\s+(\d+)\s+meet\s+requests:\s+([\d.]+)" + total_miss_rate_rule = r"miss\s+deadline\s+percentage:\s+([\d.]+)" + weighted_miss_rate_rule = r"weighted\s+miss\s+rate:\s+([\d.]+)" + #total_requests_rule = r"total\s+requests:\s+([\d.]+)" + + # Use the regular expressions to find the values + latency_match = re.search(latency_rule, rt) + slow_down_match = re.search(slow_down_rule, rt) + latency_99_9_match = re.search(latency_99_9_rule, rt) + slow_down_99_9_match = re.search(slow_down_99_9_rule, rt) + latency_99_99_match = re.search(latency_99_99_rule, rt) + slow_down_99_99_match = re.search(slow_down_99_99_rule, rt) + total_miss_rate_match = re.search(total_miss_rate_rule, rt) + weighted_miss_rate_match = re.search(weighted_miss_rate_rule, rt) + total_requests_match = re.search(total_requests_rule, rt) + + # Check if matches were found and extract the values + if latency_match: + latency_value = 0 + latency_value = latency_match.group(1) + print("99th latency is:", latency_value) + latency_dict[key].append(latency_value) + + if slow_down_match: + slow_down_value = 0 + slow_down_value = slow_down_match.group(1) + print("99th slow down is:", slow_down_value) + slow_down_dict[key].append(slow_down_value) + + if latency_99_9_match: + latency_value = 0 + latency_value = latency_99_9_match.group(1) + print("99.9th latency is:", latency_value) + latency_99_9_dict[key].append(latency_value) + + if slow_down_99_9_match: + slow_down_value = 0 + slow_down_value = slow_down_99_9_match.group(1) + print("99.9th slow down is:", slow_down_value) + slow_down_99_9_dict[key].append(slow_down_value) + + if latency_99_99_match: + latency_value = 0 + latency_value = latency_99_99_match.group(1) + print("99.99th latency is:", latency_value) + latency_99_99_dict[key].append(latency_value) + + if slow_down_99_99_match: + slow_down_value = 0 + slow_down_value = slow_down_99_99_match.group(1) + print("99.99th slow down is:", slow_down_value) + slow_down_99_99_dict[key].append(slow_down_value) + + if total_miss_rate_match: + total_miss_rate = 0 + total_miss_rate = total_miss_rate_match.group(1) + print("total miss rate for ", key, " is:", total_miss_rate) + total_miss_rate_dict[key].append(total_miss_rate) + + if weighted_miss_rate_match: + weighted_miss_rate = 0 + weighted_miss_rate = weighted_miss_rate_match.group(1) + print("weighted miss rate for ", key, " is:", weighted_miss_rate) + weighted_miss_rate_dict[key].append(weighted_miss_rate) + + total_meet = 0 + for match in re.finditer(total_meet_requests_rule, rt): + r_type, meet_requests = match.groups() + print("type:", r_type, "meet requests:", meet_requests) + total_meet = total_meet + int(meet_requests) + seperate_meet_dict[key][int(r_type)].append(meet_requests) + total_meet_dict[key].append(total_meet) + if total_requests_match: + total_requests = 0 + total_requests = total_requests_match.group(1) + print("total request for ", key, " is:", total_requests) + total_requests_dict[key].append(total_requests) + + for match in re.finditer(miss_rate_rule, rt): + r_type, miss_rate = match.groups() + print("type:", r_type, "miss rate:", miss_rate) + miss_rate_dict[key][int(r_type)].append(float(miss_rate)) + + +if __name__ == "__main__": + import json + file_folders = ['RR', 'EDF_INTERRUPT', 'JSQ', 'LLD'] + #file_folders = ['SHINJUKU', 'DARC', 'EDF_INTERRUPT'] + #file_folders = ['SHINJUKU'] + latency = defaultdict(list) + slow_down = defaultdict(list) + slow_down_99_9 = defaultdict(list) + latency_99_9 = defaultdict(list) + slow_down_99_99 = defaultdict(list) + latency_99_99 = defaultdict(list) + + rps_list = [] + + argv = sys.argv[1:] + if len(argv) < 2: + print("usage ", sys.argv[0], "[file key] [file order, 1 is throughput, 2 is last number]") + sys.exit() + + file_order = argv[1] + for key in file_folders: + files_list, file_list_based_last_num, rps_list, last_num_list = file_name(key, argv[0]) + load_dict[key] = rps_list + if file_order == "1": + get_values(key, files_list, latency, slow_down, slow_down_99_9, latency_99_9, slow_down_99_99, latency_99_99) + else: + get_values(key, file_list_based_last_num, latency, slow_down, slow_down_99_9, latency_99_9, slow_down_99_99, latency_99_99) + + print("99 latency:") + for key, value in latency.items(): + print(key, ":", value) + print("99 slow down:") + for key, value in slow_down.items(): + print(key, ":", value) + + js1 = json.dumps(latency) + f1 = open("99_latency.txt", 'w') + f1.write(js1) + f1.close() + + js2 = json.dumps(slow_down) + f2 = open("99_slow_down.txt", 'w') + f2.write(js2) + f2.close() + + js4 = json.dumps(latency_99_9) + f4 = open("99_9_latency.txt", 'w') + f4.write(js4) + f4.close() + + js5 = json.dumps(slow_down_99_9) + f5 = open("99_9_slow_down.txt", 'w') + f5.write(js5) + f5.close() + + js6 = json.dumps(latency_99_99) + f6 = open("99_99_latency.txt", 'w') + f6.write(js6) + f6.close() + + js7 = json.dumps(slow_down_99_99) + f7 = open("99_99_slow_down.txt", 'w') + f7.write(js7) + f7.close() + + js3 = json.dumps(rps_list) + f3 = open("rps.txt", 'w') + f3.write(js3) + f3.close() + + js8 = json.dumps(miss_rate_dict) + f8 = open("seperate_miss_rate.txt", 'w') + f8.write(js8) + f8.close() + + js9 = json.dumps(total_miss_rate_dict) + f9 = open("total_miss_rate.txt", 'w') + f9.write(js9) + f9.close() + + js10 = json.dumps(load_dict) + f10 = open("sload.txt", 'w') + f10.write(js10) + f10.close() + + js11 = json.dumps(weighted_miss_rate_dict) + f11 = open("weighted_miss_rate.txt", 'w') + f11.write(js11) + f11.close() + + js12 = json.dumps(total_requests_dict) + f12 = open("total_requests.txt", 'w') + f12.write(js12) + f12.close() + + js13 = json.dumps(total_meet_dict) + f13 = open("total_meet.txt", 'w') + f13.write(js13) + f13.close() + + js14 = json.dumps(seperate_meet_dict) + f14 = open("seperate_meet.txt", 'w') + f14.write(js14) + f14.close() + + js15 = json.dumps(last_num_list) + f15 = open("last_num.txt", 'w') + f15.write(js15) + f15.close() diff --git a/runtime/tests/parse_batch_profile.py b/runtime/tests/parse_batch_profile.py new file mode 100644 index 000000000..8d76e77cd --- /dev/null +++ b/runtime/tests/parse_batch_profile.py @@ -0,0 +1,128 @@ +import re +import os +import sys +from collections import defaultdict + +mem_dict = defaultdict(list) +funs_dict = defaultdict(list) + +cache_miss_dict = defaultdict(list) +pagefault_dict = defaultdict(list) + +seperate_sending_rate_dict = defaultdict(lambda: defaultdict(list)) +seperate_service_rate_dict = defaultdict(lambda: defaultdict(list)) + +def parse_file(file_path): + #key is request type, value is latency + fo = open(file_path, "r+") + deadline_miss_count = 0 + for line in fo: + line = line.strip() + #latency log line + if "avg RSS" in line: + mem = line.split(":")[1].strip() + mem = int(mem) + print("mem:", mem) + return mem + +#get all file names which contain key_str +def file_name(file_dir, key_str): + print(file_dir, key_str) + file_list = [] + funs_list = [] + + for root, dirs, files in os.walk(file_dir): + print("file:", files) + print("root:", root) + print("dirs:", dirs) + for file_i in files: + if file_i.find(key_str) >= 0: + full_path = os.path.join(os.getcwd() + "/" + root, file_i) + print(full_path) + segs = file_i.split('_') + if len(segs) < 2: + continue + funs=segs[0].strip() + print("funs---------", funs) + file_list.append(full_path) + funs_list.append(int(funs)) + + #file_list = sorted(file_list, key = lambda x: int(x.split('-')[-1].split(".")[0])) + file_list = sorted(file_list, key = lambda x: int(x.split(key_str)[0].split("/")[-1])) + funs_list = sorted(funs_list) + print(file_list) + print("--------------------------------", funs_list) + return file_list, funs_list + +def get_values(key, files_list, mem_dict): + for file_i in files_list: + mem = parse_file(file_i) + mem_dict[key].append(mem) + + +def parse_perf(file_path): + #key is request type, value is latency + with open(file_path, 'r') as file: + file_content = file.read() + + # 使用正则表达式来匹配所需的数据 + cache_miss_rate_pattern = r'([\d.]+)\s+% of all cache refs' + page_faults_pattern = r'(\d[\d,]*)\s+page-faults' + + # 查找 % of all cache refs 的值 + cache_miss_rate_match = re.search(cache_miss_rate_pattern, file_content) + cache_miss_rate = cache_miss_rate_match.group(1) if cache_miss_rate_match else None + + # 查找 page-faults 的值 + page_faults_match = re.search(page_faults_pattern, file_content) + page_faults = page_faults_match.group(1).replace(',', '') if page_faults_match else None + + return float(cache_miss_rate), int(page_faults) + +def get_perf(key, files_list, pagefault_dict, cache_miss_dict): + for file_i in files_list: + cache_miss, pagefault = parse_perf(file_i) + pagefault_dict[key].append(pagefault) + cache_miss_dict[key].append(cache_miss) + +if __name__ == "__main__": + import json + #file_folders = ['SHINJUKU', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU_7', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + file_folders = ['Sledge_pool_per_module', 'Sledge_no_pool', 'EdgeScale'] + #file_folders = ['EdgeScale'] + + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], "[file key]") + sys.exit() + + for key in file_folders: + files_list, funs_list = file_name(key, argv[0]) + funs_dict[key] = funs_list + get_values(key, files_list, mem_dict) + + files_list, funs_list = file_name(key, "_perf") + get_perf(key, files_list, pagefault_dict, cache_miss_dict) + + js1 = json.dumps(mem_dict) + f1 = open("mem.txt", 'w') + f1.write(js1) + f1.close() + + js2 = json.dumps(funs_dict) + f2 = open("funs.txt", 'w') + f2.write(js2) + f2.close() + + js3 = json.dumps(cache_miss_dict) + f3 = open("cache_miss.txt", 'w') + f3.write(js3) + f3.close() + + js4 = json.dumps(pagefault_dict) + f4 = open("pagefault.txt", 'w') + f4.write(js4) + f4.close() + + diff --git a/runtime/tests/parse_cpu_batch.py b/runtime/tests/parse_cpu_batch.py new file mode 100644 index 000000000..1be6c1212 --- /dev/null +++ b/runtime/tests/parse_cpu_batch.py @@ -0,0 +1,78 @@ +import re +import os +import sys +from collections import defaultdict + +sending_rate_dict = defaultdict(list) +service_rate_dict = defaultdict(list) + +#get all file names which contain key_str +def file_name(file_dir, key_str): + print(file_dir, key_str) + file_list = [] + rps_list = [] + + for root, dirs, files in os.walk(file_dir): + print("file:", files) + print("root:", root) + print("dirs:", dirs) + for file_i in files: + if file_i.find(key_str) >= 0: + full_path = os.path.join(os.getcwd() + "/" + root, file_i) + #print(full_path) + segs = file_i.split('-') + if len(segs) < 2: + continue + rps=segs[1] + print("rps---------", rps) + rps=rps.split(".")[0] + file_list.append(full_path) + rps_list.append(rps) + + file_list = sorted(file_list, key = lambda x: int(x.split('-')[-1].split(".")[0])) + rps_list = sorted(rps_list) + print(file_list) + print(rps_list) + return file_list, rps_list + +def get_values(key, files_list, cpu_usages_dict): + for file_i in files_list: + cmd="awk '/Average:/ {print $8}' %s" % file_i + rt=os.popen(cmd).read().strip() + cpu_count=float(rt)/100 + cpu_count=round(cpu_count, 2) + print(cpu_count) + cpu_usages_dict[key].append(cpu_count) + + +if __name__ == "__main__": + import json + #file_folders = ['SHINJUKU', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU_7', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU', 'DARC', 'EDF_SRSF_INTERRUPT'] + file_folders = ['EDF_INTERRUPT-disable-busy-loop-false-disable-autoscaling-true-9','EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-false-9', 'EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-true-27'] + #file_folders = ['EDF_INTERRUPT','EDF_SRSF_INTERRUPT_1'] + #file_folders = ['DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU'] + cpu_usages_dict = defaultdict(list) + rps_list = [] + + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], "[file key]") + sys.exit() + + for key in file_folders: + files_list, rps_list = file_name(key, argv[0]) + get_values(key, files_list, cpu_usages_dict) + + print("cpu usage:") + for key, value in cpu_usages_dict.items(): + print(key, ":", value) + + js1 = json.dumps(cpu_usages_dict) + f1 = open("cpu.txt", 'w') + f1.write(js1) + f1.close() + + diff --git a/runtime/tests/parse_power_consumption.py b/runtime/tests/parse_power_consumption.py new file mode 100644 index 000000000..55e0a208d --- /dev/null +++ b/runtime/tests/parse_power_consumption.py @@ -0,0 +1,94 @@ +import re +import os +import sys +from collections import defaultdict + +sending_rate_dict = defaultdict(list) +service_rate_dict = defaultdict(list) + +#get all file names which contain key_str +def file_name(file_dir, key_str): + print(file_dir, key_str) + file_list = [] + rps_list = [] + + for root, dirs, files in os.walk(file_dir): + print("file:", files) + print("root:", root) + print("dirs:", dirs) + for file_i in files: + if file_i.find(key_str) >= 0: + full_path = os.path.join(os.getcwd() + "/" + root, file_i) + #print(full_path) + segs = file_i.split('-') + if len(segs) < 2: + continue + rps=segs[1] + print("rps---------", rps) + rps=rps.split(".")[0] + file_list.append(full_path) + rps_list.append(rps) + + file_list = sorted(file_list, key = lambda x: int(x.split('-')[-1].split(".")[0])) + rps_list = sorted(rps_list) + print(file_list) + print(rps_list) + return file_list, rps_list + +def get_values(key, files_list, core_energy_consum_dict, pkg_energy_consum_dict): + for file_i in files_list: + with open(file_i, 'r') as file: + print(file_i) + lines = file.readlines() + + core_line_index = next(i for i, line in enumerate(lines) if "Domain CORE" in line) + pkg_line_index = next(i for i, line in enumerate(lines) if "Domain PKG" in line) + + core_value = float(lines[core_line_index+1].split()[-2]) + pkg_value = float(lines[pkg_line_index+1].split()[-2]) + + #print("Domain CORE 的值:", core_value) + #print("Domain PKG 的值:", pkg_value) + + core_energy_consum_dict[key].append(core_value) + pkg_energy_consum_dict[key].append(pkg_value) + + +if __name__ == "__main__": + import json + #file_folders = ['SHINJUKU', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU_7', 'SHINJUKU_25', 'DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU', 'DARC', 'EDF_SRSF_INTERRUPT'] + file_folders = ['EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-true-27', 'EDF_INTERRUPT-disable-busy-loop-true-disable-autoscaling-false-27'] + #file_folders = ['EDF_INTERRUPT','EDF_SRSF_INTERRUPT_1'] + #file_folders = ['DARC', 'EDF_SRSF_INTERRUPT'] + #file_folders = ['SHINJUKU'] + core_energy_consum_dict = defaultdict(list) + pkg_energy_consum_dict = defaultdict(list) + rps_list = [] + + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], "[file key]") + sys.exit() + + for key in file_folders: + files_list, rps_list = file_name(key, argv[0]) + get_values(key, files_list, core_energy_consum_dict, pkg_energy_consum_dict) + + print("core consume:") + for key, value in core_energy_consum_dict.items(): + print(key, ":", value) + for key, value in pkg_energy_consum_dict.items(): + print(key, ":", value) + + js1 = json.dumps(core_energy_consum_dict) + f1 = open("core_consume.txt", 'w') + f1.write(js1) + f1.close() + + js2 = json.dumps(pkg_energy_consum_dict) + f2 = open("pkg_consume.txt", 'w') + f2.write(js2) + f2.close() + diff --git a/runtime/tests/parse_single.py b/runtime/tests/parse_single.py new file mode 100644 index 000000000..69c48088f --- /dev/null +++ b/runtime/tests/parse_single.py @@ -0,0 +1,58 @@ +import re +import os +import sys +from collections import defaultdict + +#get all file names which contain key_str +def file_name(file_dir, key_str): + throughput_table = defaultdict(list) + errors_table = defaultdict(list) + for root, dirs, files in os.walk(file_dir): + if root != os.getcwd(): + continue + for file_i in files: + if file_i.find(key_str) >= 0: + segs = file_i.split('-') + if len(segs) < 3: + continue + #print(file_i) + cores_num = segs[1] + concurrency = segs[3].split(".")[0] + print("core:", cores_num, " concurrency:", concurrency) + get_values(cores_num, concurrency, file_i, throughput_table, errors_table) + #file_table[key].append(file_i) + s_result = sorted(throughput_table.items()) + for i in range(len(s_result)): + print(s_result[i], "errors request:", errors_table[s_result[i][0]]) + for i in range(len(s_result)): + print(int(float(((s_result[i][1][0])))),end=" ") + print(); + #sys.exit() + +def get_values(core, concurrency, file_name, throughput_table, errors_table): + fo = open(file_name, "r+") + total_throughput = 0 + for line in fo: + line = line.strip() + if "throughput is" in line: + print("line is ", line); + i_th = float(line.split(" ")[2]) + total_throughput += i_th + throughput_table[int(core)].append(total_throughput) + break; + + print("throughput:", total_throughput) + + +if __name__ == "__main__": + import json + argv = sys.argv[1:] + if len(argv) < 1: + print("usage ", sys.argv[0], " file containing key word") + sys.exit() + + file_name(os.getcwd(), argv[0]) + + #for key, value in files_tables.items(): + # get_values(key, value, miss_deadline_rate, total_latency, running_times, preemption_count, total_miss_deadline_rate) + diff --git a/runtime/tests/redblacktree.c b/runtime/tests/redblacktree.c new file mode 100644 index 000000000..5514a2435 --- /dev/null +++ b/runtime/tests/redblacktree.c @@ -0,0 +1,617 @@ +// RedblackTreeChatGPT.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include +#include +#include + +// Define the colors for Red-Black Tree +typedef enum { RED, BLACK } Color; + +typedef struct NodeData { + int deadline; + int exe_time; + +}NodeData; + +// Node structure +typedef struct Node { + NodeData* data; + int sum_left; + Color color; + struct Node *left, *right, *parent, *duplicated; +} Node; + +// Red-Black Tree structure +typedef struct RBTree { + Node *root; +} RBTree; + +// Function prototypes +NodeData* createNodeData(int deadline, int exe_time); +Node* createNode(NodeData* data, Color color, Node* left, Node* right, Node* parent); +RBTree* createTree(); +void leftRotate(RBTree *tree, Node *x); +void rightRotate(RBTree *tree, Node *x); +void insertFixup(RBTree *tree, Node *z); +void insert(RBTree *tree, NodeData* data); +Node* search(RBTree *tree, NodeData* data); +Node* searchByKey(RBTree* tree, NodeData* data); +void transplant(RBTree *tree, Node *u, Node *v); +Node* minimum(Node *node); +void deleteFixup(RBTree *tree, Node *x); +void removeNode(RBTree *tree, Node *z); +void deleteNode(RBTree *tree, NodeData* data); +void inorder(Node *node); +void destroyTree(Node *node); +int getTotalExeTimeOf(Node* node); + + +int getTotalExeTimeOf(Node* node) { + int total = 0; + Node* cur = node; + while (cur != NULL) { + total += cur->data->exe_time; + cur = cur->duplicated; + } + + return total; +} + +NodeData* createNodeData(int deadline, int exe_time){ + NodeData* data = (NodeData*)malloc(sizeof(NodeData)); + data->deadline = deadline; + data->exe_time = exe_time; + + return data; +} + +Node* createNode(NodeData* data, Color color, Node* left, Node* right, Node* parent) { + Node* newNode = (Node*)malloc(sizeof(Node)); + newNode->data = data; + newNode->color = color; + newNode->left = left; + newNode->right = right; + newNode->parent = parent; + newNode->sum_left = 0; + newNode->duplicated = NULL; + return newNode; +} + +RBTree* createTree() { + RBTree* tree = (RBTree*)malloc(sizeof(RBTree)); + tree->root = NULL; + return tree; +} + +int getSumSubTree(Node* root) { + if (root == NULL) { + return 0; + } + + return getTotalExeTimeOf(root) + root->sum_left + getSumSubTree(root->right); +} + +void leftRotate(RBTree *tree, Node *x) { + Node *y = x->right; + x->right = y->left; + if (y->left != NULL) y->left->parent = x; + y->parent = x->parent; + if (x->parent == NULL) tree->root = y; + else if (x == x->parent->left) x->parent->left = y; + else x->parent->right = y; + y->left = x; + x->parent = y; + y->sum_left += x->data->exe_time + x->sum_left; +} + +void rightRotate(RBTree *tree, Node *x) { + Node *y = x->left; + x->left = y->right; + + int new_sum_left = 0; + if (y->right != NULL) { + y->right->parent = x; + new_sum_left = getSumSubTree( y->right ); //y->right->data->exe_time + y->right->sum_left; + } + x->sum_left = new_sum_left; + y->parent = x->parent; + if (x->parent == NULL) tree->root = y; + else if (x == x->parent->right) x->parent->right = y; + else x->parent->left = y; + y->right = x; + x->parent = y; +} + +void insertFixup(RBTree *tree, Node *z) { + while (z->parent != NULL && z->parent->color == RED) { + if (z->parent == z->parent->parent->left) { + Node *y = z->parent->parent->right; + if (y != NULL && y->color == RED) { + z->parent->color = BLACK; + y->color = BLACK; + z->parent->parent->color = RED; + z = z->parent->parent; + } else { + if (z == z->parent->right) { + z = z->parent; + leftRotate(tree, z); + } + z->parent->color = BLACK; + z->parent->parent->color = RED; + rightRotate(tree, z->parent->parent); + } + } else { + Node *y = z->parent->parent->left; + if (y != NULL && y->color == RED) { + z->parent->color = BLACK; + y->color = BLACK; + z->parent->parent->color = RED; + z = z->parent->parent; + } else { + if (z == z->parent->left) { + z = z->parent; + rightRotate(tree, z); + } + z->parent->color = BLACK; + z->parent->parent->color = RED; + leftRotate(tree, z->parent->parent); + } + } + } + tree->root->color = BLACK; +} + +void insert(RBTree *tree, NodeData* data) { + Node *z = createNode(data, RED, NULL, NULL, NULL); + Node *y = NULL; + Node *x = tree->root; + while (x != NULL) { + y = x; + + //if found a duplicated deadline, inserted the node + //at the end of the linklist + if (z->data->deadline == x->data->deadline) { + //find the tail the link list; + Node* tail = x; + while (tail->duplicated != NULL) { + tail = tail->duplicated; + } + //append the new node at the end of the list; + tail->duplicated = z; + z->color = x->color; + z->sum_left = x->sum_left; + return; + } + else if (z->data->deadline < x->data->deadline) { + x->sum_left += z->data->exe_time; + x = x->left; + } + else { + x = x->right; + } + } + + z->parent = y; + if (y == NULL) { + tree->root = z; + } + else if (z->data->deadline < y->data->deadline) { + y->left = z; + } + else { + y->right = z; + } + + insertFixup(tree, z); +} + +int getSumLessThan(Node* root, int deadline){ + if ( root == NULL ) { + return 0; + } + if ( deadline == root->data->deadline ) { + return root->sum_left; + } + else if ( deadline < root->data->deadline ) { + return getSumLessThan(root->left, deadline); + } + else { + return getTotalExeTimeOf( root ) + root->sum_left + getSumLessThan(root->right, deadline ); + } + +} + + +Node* search(RBTree *tree, NodeData* data) { + Node *current = tree->root; + while (current != NULL && current->data->deadline != data->deadline ) { + if (data->deadline < current->data->deadline) { + current = current->left; + } + else { + current = current->right; + } + } + + while (current && current->data->exe_time != data->exe_time) { + current = current->duplicated; + } + + return current; +} + +Node* searchByKey(RBTree* tree, NodeData* data) { + Node* current = tree->root; + while (current != NULL && current->data->deadline != data->deadline) { + if (data->deadline < current->data->deadline) { + current = current->left; + } + else { + current = current->right; + } + } + + return current; +} + +void transplant(RBTree *tree, Node *u, Node *v) { + if (u->parent == NULL) tree->root = v; + else if (u == u->parent->left) u->parent->left = v; + else u->parent->right = v; + if (v != NULL) v->parent = u->parent; +} + +Node* minimum(Node *node) { + while (node->left != NULL) { + node = node->left; + } + return node; +} + +void deleteFixup(RBTree *tree, Node *x) { + while (x != tree->root && (x == NULL || x->color == BLACK)) { + + if (x == NULL){ + break; + } + + if ( x == x->parent->left) { + Node *w = x->parent->right; + if (w->color == RED) { + w->color = BLACK; + x->parent->color = RED; + leftRotate(tree, x->parent); + w = x->parent->right; + } + if ((w->left == NULL || w->left->color == BLACK) && (w->right == NULL || w->right->color == BLACK)) { + w->color = RED; + x = x->parent; + } else { + if (w->right == NULL || w->right->color == BLACK) { + if (w->left != NULL) w->left->color = BLACK; + w->color = RED; + rightRotate(tree, w); + w = x->parent->right; + } + w->color = x->parent->color; + x->parent->color = BLACK; + if (w->right != NULL) w->right->color = BLACK; + leftRotate(tree, x->parent); + x = tree->root; + } + } else { + Node *w = x->parent->left; + if (w->color == RED) { + w->color = BLACK; + x->parent->color = RED; + rightRotate(tree, x->parent); + w = x->parent->left; + } + if ((w->right == NULL || w->right->color == BLACK) && (w->left == NULL || w->left->color == BLACK)) { + w->color = RED; + x = x->parent; + } else { + if (w->left == NULL || w->left->color == BLACK) { + if (w->right != NULL) w->right->color = BLACK; + w->color = RED; + leftRotate(tree, w); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = BLACK; + if (w->left != NULL) w->left->color = BLACK; + rightRotate(tree, x->parent); + x = tree->root; + } + } + } + if (x != NULL) x->color = BLACK; +} + +void removeNode(RBTree *tree, Node *z) { + Node *y = z; + Node *x = NULL; + Color y_original_color = y->color; + + if ( z->left != NULL && z->right != NULL ) { + y = minimum(z->right); + + int diff = getTotalExeTimeOf(y) - getTotalExeTimeOf(z); + Node* cur = z->right; + while (cur != y) { + cur->sum_left -= diff; + cur = cur->left; + } + + NodeData* remove_data = z->data; + z->data = y->data; + z->duplicated = y->duplicated; + y->data = remove_data; + y->duplicated = NULL; + + removeNode( tree, y ); + return; + } + + //now the node to be removed has only no children + //or only one child, update the sum_left value of + //all the node along the path from root to z. + Node* current = z; + Node* p = current->parent; + while (p != NULL) { + if (p->left == current) + { + p->sum_left -= z->data->exe_time; + } + current = p; + p = current->parent; + } + + if (z->left == NULL) { + x = z->right; + transplant(tree, z, z->right); + + } else if (z->right == NULL) { + x = z->left; + transplant(tree, z, z->left); + + } + + free(z); + if (y_original_color == BLACK) { + deleteFixup(tree, x); + } +} + +void deleteNode(RBTree *tree, NodeData* data) { + Node *z = searchByKey(tree, data); + if (z != NULL) { + //if there are duplicated nodes in Z, + //we just need to remove duplicated one. + if (z->duplicated != NULL) { + Node* cur = z; + Node* prev = NULL; + while (cur && cur->data->exe_time != data->exe_time) { + prev = cur; + cur = cur->duplicated; + } + + //if the target node has been found + if (cur != NULL) { + //update the sumLeft in all of its parent node. + Node* current = z; + Node* p = current->parent; + while (p != NULL) { + if (p->left == current) + { + p->sum_left -= data->exe_time; + } + current = p; + p = current->parent; + } + } + + //if the removed node is the head of the linkedlist; + if (cur == z) { + //copy the data from the removed node. + Node* newroot = z->duplicated; + newroot->color = z->color; + newroot->sum_left = z->sum_left; + newroot->left = z->left; + if (newroot->left != NULL) { + newroot->left->parent = newroot; + } + newroot->right = z->right; + if (newroot->right != NULL) { + newroot->right->parent = newroot; + } + + newroot->parent = z->parent; + + if (z->parent) { + if (z->parent->left = z) { + z->parent->left = newroot; + } + else { + z->parent->right = newroot; + } + } + //clean up the remove node z; + memset( z, 0, sizeof(Node)); + } + else { //remove the node from the link list; + prev->duplicated = cur->duplicated; + } + } + else{ + removeNode(tree, z); + } + } +} + +void inorder(Node *node) { + if (node != NULL) { + inorder(node->left); + printf("%d(%d) ", node->data->deadline, node->data->exe_time); + inorder(node->right); + } +} + +void destroyTree(Node *node) { + if (node != NULL) { + destroyTree(node->left); + destroyTree(node->right); + free(node); + + } +} + +// Helper function to print the tree +void printTree(Node* root, int space) { + if (root != NULL) { + space += 10; + printTree(root->right, space); + printf("\n"); + for (int i = 10; i < space; i++) + printf(" "); + printf("%d\n", root->data->deadline); + printTree(root->left, space); + } +} + + +int main() { + printf("============Test the program from ChatGPT===============\n"); + + RBTree *tree = createTree(); + const int size = 64; + int keys[size]; // = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; //{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + int vals[size]; //= { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; //{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + for (int i = 1; i <= size; ++i) { + keys[i - 1] = i; + vals[i - 1] = i; + } + + NodeData* arr[size]; + + for ( int i = 0; i < size; ++i ) + { + arr[i] = createNodeData(keys[i], vals[i]); + insert( tree, arr[i] ); + } + + printTree( tree->root, 0 ); + + printf("===============================================" ); + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline) ); + } + + printf("\n============insert the duplicated value: 8===============" ); + insert(tree, arr[7]); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + printf("\n============insert the duplicated value: 10===============" ); + insert(tree, arr[9]); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + printf("\n============remove the duplicated value: 8===============" ); + deleteNode(tree, arr[7]); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + printf("\n============remove the duplicated value: 10===============" ); + deleteNode(tree, arr[9]); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + printf("\n============remove the single value: 20===============" ); + deleteNode(tree, arr[19]); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + printf("\n============insert the duplicated value: 41===============" ); + insert(tree, arr[40]); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + printf("\n============remove the single value: 40===============" ); + deleteNode(tree, arr[39]); + + inorder(tree->root); + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + + /*printf("============delete the value 8===============" ); + deleteNode(tree, arr[7]); + printTree( tree->root, 0 ); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline) ); + } + + printf("============delete the root value 6===============" ); + deleteNode(tree, arr[5]); + printTree( tree->root, 0 ); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline) ); + } + + printf("============delete the root value 32===============" ); + deleteNode(tree, arr[31]); + printTree(tree->root, 0); + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline)); + } + + + printf("============insert the root value 6===============" ); + insert(tree, arr[5]); + printTree( tree->root, 0 ); + + + printf("============insert the root value 8===============" ); + insert(tree, arr[7]); + printTree( tree->root, 0 ); + + + for (int i = 0; i < size; i++) { + printf("Sum of values less than %d: %d\n", arr[i]->deadline, getSumLessThan(tree->root, arr[i]->deadline) ); + }*/ + + destroyTree(tree->root); + free(tree); + + return 0; +} + + +// Run program: Ctrl + F5 or Debug > Start Without Debugging menu +// Debug program: F5 or Debug > Start Debugging menu + +// Tips for Getting Started: +// 1. Use the Solution Explorer window to add/manage files +// 2. Use the Team Explorer window to connect to source control +// 3. Use the Output window to see build output and other messages +// 4. Use the Error List window to view errors +// 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project +// 6. In the future, to open this project again, go to File > Open > Project and select the .sln file diff --git a/runtime/tests/run_perf.sh b/runtime/tests/run_perf.sh new file mode 100755 index 000000000..ec238f9e9 --- /dev/null +++ b/runtime/tests/run_perf.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +function usage { + echo "$0 [output]" + exit 1 +} + +if [ $# != 1 ] ; then + usage + exit 1; +fi + +pid=`ps -ef|grep "sledgert"|grep -v grep |awk '{print $2}'` +output=$1 + +cpu=$(lscpu | awk '/^Core\(s\) per socket:/ {cores=$4} /^Socket\(s\):/ {sockets=$2} END {print cores * sockets}') +last_core=$((cpu - 1)) #get the last core + +#taskset -c $last_core sudo perf stat -B -e context-switches,cache-references,cache-misses,cycles,instructions,page-faults -p $pid -o $output 2>&1 & +#taskset -c $last_core sudo perf stat -e syscalls:sys_enter_mmap,syscalls:sys_enter_mprotect,syscalls:sys_enter_munmap,context-switches -p $pid -o $output 2>&1 & +taskset -c $last_core sudo perf stat -C 4 -e syscalls:sys_enter_mmap,syscalls:sys_enter_mprotect,syscalls:sys_enter_munmap,context-switches -o $output 2>&1 & + diff --git a/runtime/tests/scp.sh b/runtime/tests/scp.sh new file mode 100755 index 000000000..60e8dfbc6 --- /dev/null +++ b/runtime/tests/scp.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +if [ "$#" -lt 2 ]; then + echo "Usage: $0 target_path file1 [file2 ... fileN]" + exit 1 +fi + +TARGET_DIR="$1" +shift # remove first parameter, so $@ is the file list + +PRIVATE_KEY="/my_mount/sledge-serverless-framework/runtime/tests/id_rsa" +TARGET_USER="xiaosuGW" +TARGET_HOST="10.10.1.2" + +chmod 400 "$PRIVATE_KEY" + +#loop copy +for file in "$@"; do + if [ -f "$file" ]; then + scp -i "$PRIVATE_KEY" "$file" "$TARGET_USER@$TARGET_HOST:$TARGET_DIR" + if [ $? -eq 0 ]; then + echo "Successfully copied $file to $TARGET_DIR" + else + echo "Failed to copy $file to $TARGET_DIR" + fi + else + echo "File $file does not exist, skipping." + fi +done diff --git a/runtime/tests/sed_json.sh b/runtime/tests/sed_json.sh new file mode 100755 index 000000000..4c2c671fd --- /dev/null +++ b/runtime/tests/sed_json.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +if [ $# -ne 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +FILENAME=$1 +NUMBER1=$2 +NUMBER2=$3 + +# Validate if file exists +if [ ! -f "$FILENAME" ]; then + echo "File $FILENAME does not exist." + exit 1 +fi + +# Replace the "n-resas" value in the 11th line with NUMBER1 +sed -i "11s/\(\"n-resas\": \)[0-9]\+/\1$NUMBER1/" "$FILENAME" + +# Replace the "n-resas" value in the 21st line with NUMBER2 +sed -i "22s/\(\"n-resas\": \)[0-9]\+/\1$NUMBER2/" "$FILENAME" + +echo "The file $FILENAME has been updated." + diff --git a/runtime/tests/start.sh b/runtime/tests/start.sh new file mode 100755 index 000000000..2d4687cda --- /dev/null +++ b/runtime/tests/start.sh @@ -0,0 +1,54 @@ +#!/bin/bash +ulimit -n 655350 + +function usage { + echo "$0 [worker num] [listener num] [first worker core id]" + exit 1 +} + +if [ $# != 3 ] ; then + usage + exit 1; +fi + +worker_num=$1 +listener_num=$2 +first_worker_core_id=$3 + +declare project_path="$( + cd "$(dirname "$0")/../.." + pwd +)" +echo $project_path +path=`pwd` +export SLEDGE_DISABLE_PREEMPTION=true +export SLEDGE_DISABLE_BUSY_LOOP=true +export SLEDGE_DISABLE_AUTOSCALING=true +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=true +#export SLEDGE_SIGALRM_HANDLER=TRIAGED +export SLEDGE_FIRST_WORKER_COREID=$first_worker_core_id +export SLEDGE_NWORKERS=$worker_num +export SLEDGE_NLISTENERS=$listener_num +export SLEDGE_WORKER_GROUP_SIZE=1 +export SLEDGE_SCHEDULER=EDF +#export SLEDGE_DISPATCHER=DARC +#export SLEDGE_DISPATCHER=SHINJUKU +export SLEDGE_DISPATCHER=EDF_INTERRUPT +export SLEDGE_SANDBOX_PERF_LOG=$path/server.log +#echo $SLEDGE_SANDBOX_PERF_LOG +cd $project_path/runtime/bin +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_big_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_armcifar10.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_png2bmp.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/mulitple_linear_chain.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing3.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/fib.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/hash.json +LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/empty.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_sodresize.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_sodresize.json + diff --git a/runtime/tests/start_func_density_test.sh b/runtime/tests/start_func_density_test.sh new file mode 100755 index 000000000..81d46cd3a --- /dev/null +++ b/runtime/tests/start_func_density_test.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +ulimit -n 655350 +ulimit -s unlimited +ulimit -m unlimited +sudo sysctl -w vm.max_map_count=262144 + +function usage { + echo "$0 [dispatcher policy:SHINJUKU, EDF_INTERRUPT, DARC or TO_GLOBAL_QUEUE] [scheduler policy: EDF or FIFO] [disable busy loop] [worker num] [listener num] [first worker core id] [json file] [server log]" + exit 1 +} + +if [ $# != 8 ] ; then + usage + exit 1; +fi + +dispatcher_policy=$1 +scheduler_policy=$2 +disable_busy_loop=$3 +worker_num=$4 +listener_num=$5 +first_worker_core_id=$6 +json_file=$7 +server_log=$8 +worker_group_size=$((worker_num / listener_num)) + +declare project_path="$( + cd "$(dirname "$0")/../.." + pwd +)" +echo $project_path +path=`pwd` +export SLEDGE_DISABLE_PREEMPTION=true +#only works for TO_GLOBAL_QUEUE and FIFO +export SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ=false +export SLEDGE_DISABLE_BUSY_LOOP=$disable_busy_loop +export SLEDGE_DISABLE_AUTOSCALING=true +#only works for FIFO scheduler +export SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ=false +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=true +#export SLEDGE_SIGALRM_HANDLER=TRIAGED +export SLEDGE_FIRST_WORKER_COREID=$first_worker_core_id +export SLEDGE_NWORKERS=$worker_num +export SLEDGE_NLISTENERS=$listener_num +export SLEDGE_WORKER_GROUP_SIZE=$worker_group_size +export SLEDGE_SCHEDULER=$scheduler_policy +#export SLEDGE_DISPATCHER=DARC +#export SLEDGE_DISPATCHER=SHINJUKU +export SLEDGE_DISPATCHER=$dispatcher_policy +export SLEDGE_SANDBOX_PERF_LOG=$path/$server_log +#echo $SLEDGE_SANDBOX_PERF_LOG +cd $project_path/runtime/bin +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_big_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_armcifar10.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_png2bmp.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/mulitple_linear_chain.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing3.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/fib.json +LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/$json_file +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/hash.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/empty.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_sodresize.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_sodresize.json + diff --git a/runtime/tests/start_monitor.sh b/runtime/tests/start_monitor.sh new file mode 100755 index 000000000..19c14b87c --- /dev/null +++ b/runtime/tests/start_monitor.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +function usage { + echo "$0 [pidstat file]" + exit 1 +} + +if [ $# != 1 ] ; then + usage + exit 1; +fi + +pidstat_file=$1 +sledge_pid=`ps -ef|grep "sledgert"|grep -v grep |awk '{print $2}'` +sleep 6 && pidstat -u -p $sledge_pid 1 1800 > $pidstat_file 2>&1 & diff --git a/runtime/tests/start_single_request.sh b/runtime/tests/start_single_request.sh new file mode 100755 index 000000000..2d4687cda --- /dev/null +++ b/runtime/tests/start_single_request.sh @@ -0,0 +1,54 @@ +#!/bin/bash +ulimit -n 655350 + +function usage { + echo "$0 [worker num] [listener num] [first worker core id]" + exit 1 +} + +if [ $# != 3 ] ; then + usage + exit 1; +fi + +worker_num=$1 +listener_num=$2 +first_worker_core_id=$3 + +declare project_path="$( + cd "$(dirname "$0")/../.." + pwd +)" +echo $project_path +path=`pwd` +export SLEDGE_DISABLE_PREEMPTION=true +export SLEDGE_DISABLE_BUSY_LOOP=true +export SLEDGE_DISABLE_AUTOSCALING=true +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=true +#export SLEDGE_SIGALRM_HANDLER=TRIAGED +export SLEDGE_FIRST_WORKER_COREID=$first_worker_core_id +export SLEDGE_NWORKERS=$worker_num +export SLEDGE_NLISTENERS=$listener_num +export SLEDGE_WORKER_GROUP_SIZE=1 +export SLEDGE_SCHEDULER=EDF +#export SLEDGE_DISPATCHER=DARC +#export SLEDGE_DISPATCHER=SHINJUKU +export SLEDGE_DISPATCHER=EDF_INTERRUPT +export SLEDGE_SANDBOX_PERF_LOG=$path/server.log +#echo $SLEDGE_SANDBOX_PERF_LOG +cd $project_path/runtime/bin +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_big_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_armcifar10.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_png2bmp.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/mulitple_linear_chain.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing3.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/fib.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/hash.json +LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/empty.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_sodresize.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_sodresize.json + diff --git a/runtime/tests/start_test.sh b/runtime/tests/start_test.sh new file mode 100755 index 000000000..e92f03eb3 --- /dev/null +++ b/runtime/tests/start_test.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +#1. For LLD + worker FIFO, dispatcher assign requests to worker with LLD, and worker schedule local queue task with RR, so SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ +# should be true, SLEDGE_DISABLE_PREEMPTION should be false +#2. For TO_GLOBAL_QUEUE + worker FIFO, dispatcher put requests to a global queue, and each worker compete to get requests from the global queue, so +# SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ should be false, SLEDGE_DISABLE_PREEMPTION doesnt matter, for scalability test, set SLEDGE_DISABLE_PREEMPTION to true to +# remove context switch. +#3. Other non FIFO schedulers, dispatcher assign requests to worker and interrupt workers, so SLEDGE_DISABLE_PREEMPTION should be true, +# SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ should be true. + +ulimit -n 655350 + +function usage { + echo "$0 [worker num] [listener num] [first worker core id] [dispatcher policy, SHINJUKU, EDF_INTERRUPT, DARC, LLD, or TO_GLOBAL_QUEUE] [scheduler policy, EDF or FIFO] [server log file] [disable busy loop] [disable service time simulation] [disable get requests from GQ] [disable preemption] [json config]" + exit 1 +} + +if [ $# != 11 ] ; then + usage + exit 1; +fi + +worker_num=$1 +listener_num=$2 +first_worker_core_id=$3 +dispatcher_policy=$4 +scheduler_policy=$5 +server_log=$6 +disable_busy_loop=$7 +disable_autoscaling=true +disable_service_ts_simulation=$8 +disable_get_req_from_GQ=${9} +disable_preemption=${10} +json_config=${11} + +if [ "$scheduler_policy" = "FIFO" ]; then + worker_group_size=$worker_num +else + worker_group_size=$((worker_num / listener_num)) +fi + +declare project_path="$( + cd "$(dirname "$0")/../.." + pwd +)" +echo $project_path +path=`pwd` +export SLEDGE_DISABLE_PREEMPTION=$disable_preemption +#only works for FIFO scheduler +export SLEDGE_DISABLE_GET_REQUESTS_FROM_GQ=$disable_get_req_from_GQ +export SLEDGE_FIFO_QUEUE_BATCH_SIZE=5 +export SLEDGE_DISABLE_BUSY_LOOP=$disable_busy_loop +export SLEDGE_DISABLE_AUTOSCALING=$disable_autoscaling +#export SLEDGE_SIGALRM_HANDLER=TRIAGED +export SLEDGE_DISABLE_EXPONENTIAL_SERVICE_TIME_SIMULATION=$disable_service_ts_simulation +export SLEDGE_FIRST_WORKER_COREID=$first_worker_core_id +export SLEDGE_NWORKERS=$worker_num +export SLEDGE_NLISTENERS=$listener_num +export SLEDGE_WORKER_GROUP_SIZE=$worker_group_size +export SLEDGE_SCHEDULER=$scheduler_policy +#export SLEDGE_DISPATCHER=DARC +export SLEDGE_DISPATCHER=$dispatcher_policy +export SLEDGE_SCHEDULER=$scheduler_policy +#export SLEDGE_DISPATCHER=EDF_INTERRUPT +export SLEDGE_SANDBOX_PERF_LOG=$path/$server_log +#echo $SLEDGE_SANDBOX_PERF_LOG +cd $project_path/runtime/bin +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_big_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_armcifar10.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_png2bmp.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/mulitple_linear_chain.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_multiple_image_processing3.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/hash.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/empty.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/fib.json +LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/$json_config +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_fibonacci.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/test_sodresize.json +#LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./sledgert ../tests/my_sodresize.json + diff --git a/runtime/tests/stop_monitor.sh b/runtime/tests/stop_monitor.sh new file mode 100755 index 000000000..fa67a47b5 --- /dev/null +++ b/runtime/tests/stop_monitor.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +pid=`ps -ef|grep "pidstat"|grep -v grep |awk '{print $2}'` +echo $pid +kill -2 $pid diff --git a/runtime/tests/test_dequeue.cpp b/runtime/tests/test_dequeue.cpp new file mode 100644 index 000000000..7f17baca3 --- /dev/null +++ b/runtime/tests/test_dequeue.cpp @@ -0,0 +1,193 @@ +// C implementation of De-queue using circular array +// Maximum size of array or Dequeue +#include +#include +#define MAX 100 + +using namespace std; +// A structure to represent a Deque +struct Deque { + int arr[MAX]; + int front; + int rear; + int size; +}; + + +void init_deque(struct Deque * queue, int size) { + assert(queue != NULL); + queue->front = -1; + queue->rear = 0; + queue->size = size; +} + +// Checks whether Deque is full or not. +bool isFull(struct Deque * queue) +{ + assert(queue != NULL); + return ((queue->front == 0 && queue->rear == queue->size - 1) + || queue->front == queue->rear + 1); +} + +// Checks whether Deque is empty or not. +bool isEmpty(struct Deque * queue) { + + assert(queue != NULL); + return (queue->front == -1); +} + +// Inserts an element at front +void insertfront(struct Deque * queue, int key) +{ + assert(queue != NULL); + // check whether Deque if full or not + if (isFull(queue)) { + printf( "Overflow\n"); + return; + } + + // If queue is initially empty + if (queue->front == -1) { + queue->front = 0; + queue->rear = 0; + } + + // front is at first position of queue + else if (queue->front == 0) + queue->front = queue->size - 1; + + else // decrement front end by '1' + queue->front = queue->front - 1; + + // insert current element into Deque + queue->arr[queue->front] = key; +} + +// function to inset element at rear end +// of Deque. +void insertrear(struct Deque * queue, int key) +{ + assert(queue != NULL); + + if (isFull(queue)) { + printf(" Overflow\n"); + return; + } + + // If queue is initially empty + if (queue->front == -1) { + queue->front = 0; + queue->rear = 0; + } + + // rear is at last position of queue + else if (queue->rear == queue->size - 1) + queue->rear = 0; + + // increment rear end by '1' + else + queue->rear = queue->rear + 1; + + // insert current element into Deque + queue->arr[queue->rear] = key; +} + +// Deletes element at front end of Deque +void deletefront(struct Deque * queue) +{ + assert(queue != NULL); + + // check whether Deque is empty or not + if (isEmpty(queue)) { + printf("Queue Underflow\n"); + return; + } + + // Deque has only one element + if (queue->front == queue->rear) { + queue->front = -1; + queue->rear = -1; + } + else { + // back to initial position + if (queue->front == queue->size - 1) + queue->front = 0; + + else // increment front by '1' to remove current + // front value from Deque + queue->front = queue->front + 1; + } +} + +// Delete element at rear end of Deque +void deleterear(struct Deque * queue) +{ + assert(queue != NULL); + if (isEmpty(queue)) { + printf(" Underflow\n"); + return; + } + + // Deque has only one element + if (queue->front == queue->rear) { + queue->front = -1; + queue->rear = -1; + } + else if (queue->rear == 0) + queue->rear = queue->size - 1; + else + queue->rear = queue->rear - 1; +} + +// Returns front element of Deque +int getFront(struct Deque * queue) +{ + assert(queue != NULL); + // check whether Deque is empty or not + if (isEmpty(queue)) { + printf(" Underflow\n"); + return -1; + } + return queue->arr[queue->front]; +} + +// function return rear element of Deque +int getRear(struct Deque * queue) +{ + assert(queue != NULL); + // check whether Deque is empty or not + if (isEmpty(queue) || queue->rear < 0) { + printf(" Underflow\n"); + return -1; + } + return queue->arr[queue->rear]; +} + +// Driver code +int main() +{ + struct Deque dq; + init_deque(&dq, 5); + + // Function calls + printf("Insert element at rear end : 5 \n"); + insertrear(&dq, 5); + + printf("insert element at rear end : 10 \n"); + insertrear(&dq, 10); + + printf("get rear element %d \n", getRear(&dq)); + + deleterear(&dq); + printf("After delete rear element new rear become %d ", getRear(&dq)); + + printf("inserting element at front end \n"); + insertfront(&dq, 15); + + printf("get front element %d\n", getFront(&dq)); + + deletefront(&dq); + + printf("After delete front element new front become %d", getFront(&dq)); + return 0; +} diff --git a/runtime/tests/test_tsc.c b/runtime/tests/test_tsc.c new file mode 100644 index 000000000..0c80a8460 --- /dev/null +++ b/runtime/tests/test_tsc.c @@ -0,0 +1,179 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#define LISTENER_COUNTS 3 +#define WORKER_COUNTS 9 +#define LISTENER_THREAD_START_CORE_ID 1 + + +int listener_threads[LISTENER_COUNTS] = {1, 2, 3}; +int worker_threads[WORKER_COUNTS] = {4, 5, 6, 7, 8, 9, 10, 11, 12}; +int runtime_first_worker_processor = 4; +thread_local int global_worker_thread_idx; +thread_local int dispatcher_id; +thread_local int group_worker_thread_idx; +int diff_cycles[LISTENER_COUNTS][WORKER_COUNTS] = {0}; +//thread_local uint64_t current_cycles = 0; +uint64_t current_worker_cycles[WORKER_COUNTS] = {0}; +uint64_t current_listener_cycles[LISTENER_COUNTS] = {0}; +int runtime_worker_group_size = 3; +int stop = 0; +pthread_t *runtime_worker_threads; +pthread_t *runtime_listener_threads; +thread_local pthread_t listener_thread_id; +thread_local uint8_t dispatcher_thread_idx; +int *runtime_worker_threads_argument; +int *runtime_listener_threads_argument; + +//#if defined(X86_64) || defined(x86_64) + +unsigned long long int +__getcycles(void) +{ + unsigned long long int cpu_time_in_cycles = 0; + unsigned int cycles_lo; + unsigned int cycles_hi; + __asm__ volatile("rdtsc" : "=a"(cycles_lo), "=d"(cycles_hi)); + cpu_time_in_cycles = (unsigned long long int)cycles_hi << 32 | cycles_lo; + + return cpu_time_in_cycles; +} + +//#endif + +void * +worker_thread_main(void *argument) +{ + /* Index was passed via argument */ + global_worker_thread_idx = *(int *)argument; + + /* Set dispatcher id for this worker */ + dispatcher_id = global_worker_thread_idx / runtime_worker_group_size; + + group_worker_thread_idx = global_worker_thread_idx - dispatcher_id * runtime_worker_group_size; + + printf("global thread %d's dispatcher id is %d group size is %d group id is %d\n", global_worker_thread_idx, + dispatcher_id, runtime_worker_group_size, group_worker_thread_idx); + while(stop == 0) { + current_worker_cycles[global_worker_thread_idx] = __getcycles(); + } +} +/** + * Starts all worker threads and sleeps forever on pthread_join, which should never return + */ +void +runtime_start_runtime_worker_threads() +{ + for (int i = 0; i < WORKER_COUNTS; i++) { + printf("start %d thread\n", i); + runtime_worker_threads_argument[i] = i; + /* Pass the value we want the threads to use when indexing into global arrays of per-thread values */ + int ret = pthread_create(&runtime_worker_threads[i], NULL, worker_thread_main, (void *)&runtime_worker_threads_argument[i]); + if (ret) { + perror("pthread_create"); + exit(-1); + } + + cpu_set_t cs; + CPU_ZERO(&cs); + CPU_SET(runtime_first_worker_processor + i, &cs); + ret = pthread_setaffinity_np(runtime_worker_threads[i], sizeof(cs), &cs); + assert(ret == 0); + printf("Starting %d worker thread(s), pin to core %d\n", i, runtime_first_worker_processor + i); + } +} + +void * +listener_thread_main(void *dummy) +{ + /* Index was passed via argument */ + dispatcher_thread_idx = *(int *)dummy; + while(stop == 0) { + current_listener_cycles[dispatcher_thread_idx] = __getcycles(); + } +} + +void +listener_thread_initialize(uint8_t thread_id) +{ + printf("Starting listener thread\n"); + + cpu_set_t cs; + + CPU_ZERO(&cs); + CPU_SET(LISTENER_THREAD_START_CORE_ID + thread_id, &cs); + + runtime_listener_threads_argument[thread_id] = thread_id; + + int ret = pthread_create(&runtime_listener_threads[thread_id], NULL, listener_thread_main, (void *)&runtime_listener_threads_argument[thread_id]); + listener_thread_id = runtime_listener_threads[thread_id]; + assert(ret == 0); + ret = pthread_setaffinity_np(listener_thread_id, sizeof(cpu_set_t), &cs); + assert(ret == 0); + + printf("\tListener %d thread, pin to core %d\n", thread_id, LISTENER_THREAD_START_CORE_ID + thread_id); +} + +void listener_threads_initialize() { + printf("Starting %d listener thread(s)\n", LISTENER_COUNTS); + for (int i = 0; i < LISTENER_COUNTS; i++) { + listener_thread_initialize(i); + } +} + +int main() { + + cpu_set_t cs; + + CPU_ZERO(&cs); + CPU_SET(0, &cs); + + int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs); + assert(ret == 0); + + runtime_worker_threads = calloc(WORKER_COUNTS, sizeof(pthread_t)); + assert(runtime_worker_threads != NULL); + runtime_listener_threads = calloc(LISTENER_COUNTS, sizeof(pthread_t)); + assert(runtime_listener_threads != NULL); + runtime_worker_threads_argument = calloc(WORKER_COUNTS, sizeof(int)); + assert(runtime_worker_threads_argument != NULL); + + runtime_listener_threads_argument = calloc(WORKER_COUNTS, sizeof(int)); + assert(runtime_listener_threads_argument != NULL); + + runtime_start_runtime_worker_threads(); + listener_threads_initialize(); + + sleep(5); + stop = 1; + for (int i = 0; i < WORKER_COUNTS; i++) { + int ret = pthread_join(runtime_worker_threads[i], NULL); + if (ret) { + perror("worker pthread_join"); + exit(-1); + } + } + + for (int i = 0; i < LISTENER_COUNTS; i++) { + int ret = pthread_join(runtime_listener_threads[i], NULL); + if (ret) { + perror("listener pthread_join"); + exit(-1); + } + } + + for(int i = 0; i < LISTENER_COUNTS; i++) { + for (int j = 0; j < WORKER_COUNTS; j++) { + int diff = current_worker_cycles[j] - current_listener_cycles[i]; + diff_cycles[i][j] = diff; + printf("listener %d diff worker %d is %d\n", i, j, diff); + } + } +} diff --git a/runtime/tests/vision_apps.json b/runtime/tests/vision_apps.json new file mode 100644 index 000000000..1b6ba42c9 --- /dev/null +++ b/runtime/tests/vision_apps.json @@ -0,0 +1,47 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/sift", + "request-type": 1, + "n-resas": 3, + "group-id": 1, + "path": "sift.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 113, + "relative-deadline-us": 11300, + "http-resp-content-type": "text/plain" + }, + { + "route": "/multi_ncut", + "request-type": 2, + "n-resas": 2, + "group-id": 2, + "path": "multi_ncut.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 1100, + "relative-deadline-us": 2200, + "http-resp-content-type": "text/plain" + }, + { + "route": "/cifar10", + "request-type": 3, + "n-resas": 1, + "group-id": 3, + "path": "cifar10.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 2060, + "relative-deadline-us": 4120, + "http-resp-content-type": "text/plain" + } + + ] + + } + +] + diff --git a/runtime/tests/vision_apps_dispatcher.json b/runtime/tests/vision_apps_dispatcher.json new file mode 100644 index 000000000..c9ffcb9ba --- /dev/null +++ b/runtime/tests/vision_apps_dispatcher.json @@ -0,0 +1,36 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/sift", + "request-type": 1, + "n-resas": 3, + "group-id": 1, + "path": "sift.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 113, + "relative-deadline-us": 1130, + "http-resp-content-type": "text/plain" + }, + { + "route": "/cifar10", + "request-type": 2, + "n-resas": 1, + "group-id": 2, + "path": "cifar10.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 2060, + "relative-deadline-us": 20600, + "http-resp-content-type": "text/plain" + } + + ] + + } + +] + diff --git a/runtime/tests/vision_apps_same_6apps.json b/runtime/tests/vision_apps_same_6apps.json new file mode 100644 index 000000000..851666ff6 --- /dev/null +++ b/runtime/tests/vision_apps_same_6apps.json @@ -0,0 +1,80 @@ +[ + { + "name": "gwu", + "port": 31850, + "replenishment-period-us": 0, + "max-budget-us": 0, + "routes": [ + { + "route": "/tracking", + "request-type": 1, + "n-resas": 1, + "group-id": 1, + "path": "tracking.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 6, + "relative-deadline-us": 10000, + "http-resp-content-type": "text/plain" + }, + { + "route": "/disparity", + "request-type": 2, + "n-resas": 1, + "group-id": 1, + "path": "disparity.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 11, + "relative-deadline-us": 10000, + "http-resp-content-type": "text/plain" + }, + { + "route": "/mser", + "request-type": 3, + "n-resas": 1, + "group-id": 1, + "path": "mser.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 16, + "relative-deadline-us": 10000, + "http-resp-content-type": "text/plain" + }, + { + "route": "/sift", + "request-type": 4, + "n-resas": 1, + "group-id": 2, + "path": "sift.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 113, + "relative-deadline-us": 1000, + "http-resp-content-type": "text/plain" + }, + { + "route": "/multi_ncut", + "request-type": 5, + "n-resas": 1, + "group-id": 2, + "path": "multi_ncut.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 326, + "relative-deadline-us": 600, + "http-resp-content-type": "text/plain" + }, + { + "route": "/cifar10", + "request-type": 6, + "n-resas": 6, + "group-id": 3, + "path": "cifar10.wasm.so", + "admissions-percentile": 70, + "expected-execution-us": 2060, + "relative-deadline-us": 3000, + "http-resp-content-type": "text/plain" + } + + ] + + } + +] +