-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initiate benchmark, add chat example
- Loading branch information
1 parent
bf85d58
commit e9af9d1
Showing
9 changed files
with
322 additions
and
2 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Performance Evaluation | ||
To ensure reliable and fair evaluations, the first step is setting up a stable and dedicated environment. This means minimizing factors that could influence performance results in any benchmarking process. The provided [guide](https://easyperf.net/blog/2019/08/02/Perf-measurement-environment-on-Linux) explains how to set up a consistent Linux environment effectively. | ||
|
||
All tests were done on my workstations, which have an INTEL Corei7 1265U at 4.8GHz, 32 GB RAM, and run Mint 21.1 with Kernel 5.15.0-91-generic. | ||
|
||
## Experiment Overview | ||
|
||
The focus of the evaluation is on Round Trip Time (RTT) using a ping-pong application for each framework. This synthetic benchmark is crucial as it gives the minimum latency and shows how the system behaves under "async contention." RTT measures the time it takes for a message to be sent and acknowledged. | ||
|
||
The picture below explains the ping-pong application and how RTT is calculated. | ||
|
||
![RTT](../.github/artifacts/rtt.png) | ||
|
||
## Looking at the results | ||
|
||
In the following, we are presenting the RTT results for all the frameworks under two different scenarios: over localhost and over the network | ||
### Localhost | ||
|
||
In our first series of tests, the ping-pong application is executed on a single machine, leveraging only localhost communication. | ||
|
||
To replicate these experiments, you can build and run the RTT test by following these instructions: | ||
|
||
``` | ||
git clone https://github.com/enum-class/cring-benchmarks.git | ||
``` | ||
``` | ||
# To build boost-asio and libuv ping-pong server | ||
cmake -B Release . | ||
cmake --build Release | ||
``` | ||
``` | ||
# To build glommio and tokio pingpong server | ||
cargo build release | ||
``` | ||
``` | ||
# To run different servers single thread | ||
./Release/boost-server -t 1 | ||
./Release/uv-server -t 1 | ||
./target/release/monoio-server --cores 1 | ||
./target/release/glommio-server --cores 1 | ||
taskset -c 1 ./target/release/tokio-server --cores 1 | ||
``` | ||
``` | ||
# To run different servers multi-thread thread | ||
./Release/boost-server -t 4 | ||
./target/release/monoio-server --cores 1 2 3 4 | ||
./target/release/glommio-server --cores 1 2 3 4 | ||
taskset -c 1-4 ./target/release/tokio-server --cores 1 2 3 4 | ||
``` | ||
``` | ||
# Run client | ||
git clone https://github.com/enum-class/cring.git | ||
cmake -B Release -DCRING_BENCHMARK=ON . | ||
cmake --build Release | ||
taskset -c 1-4 ./Release/benchmark/pingpong-client -t 4 -c 400 | ||
``` | ||
|
||
One very important aspect to mention is that RTT depends on the load of the system. As you can see from the figure below, as the number of messages per second increases, RTT decreases. This is due to the fact that when messages are sent at a low rate, the processes are more likely to be de-scheduled by the operating system. This operation adds additional latency since the processes need to be rescheduled when messages are sent and received. This is true for both the Rust code and the classical ping, which is reported as a reference baseline for RTT. | ||
|
||
#### Extreme performance testing | ||
In this test we will start a fixed number of connections on the client side. The more connections, the higher the load on the server. This test aims to detect the extreme performance of the system. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,33 @@ | ||
find_package(Threads REQUIRED) | ||
|
||
set(ECHO_CLIENT_SOURCES | ||
echo-client.c | ||
echo/echo-client.c | ||
) | ||
add_executable(echo-client ${ECHO_CLIENT_SOURCES}) | ||
target_link_libraries(echo-client PRIVATE | ||
libcring | ||
Threads::Threads | ||
) | ||
target_include_directories(echo-client PRIVATE utility) | ||
|
||
set(ECHO_SERVER_SOURCES | ||
echo-server.c | ||
echo/echo-server.c | ||
) | ||
add_executable(echo-server ${ECHO_SERVER_SOURCES}) | ||
target_link_libraries(echo-server PRIVATE | ||
libcring | ||
Threads::Threads | ||
) | ||
target_include_directories(echo-server PRIVATE utility) | ||
|
||
|
||
set(CHAT_SERVER_SOURCES | ||
chat/chat-server.c | ||
) | ||
add_executable(chat-server ${CHAT_SERVER_SOURCES}) | ||
target_link_libraries(chat-server PRIVATE | ||
libcring | ||
Threads::Threads | ||
) | ||
target_include_directories(chat-server PRIVATE utility) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
|
||
#include <Executor.h> | ||
#include "utils.h" | ||
|
||
#define PACKET_SIZE 1024 | ||
#define JOIN_MESSAGE "Someone joined" | ||
#define LEFT_MESSAGE "Someone left" | ||
|
||
struct Message { | ||
char buffer[PACKET_SIZE]; | ||
size_t length; | ||
struct Message *next; | ||
}; | ||
|
||
struct ChatSession; | ||
|
||
struct Participant { | ||
struct ChatSession *session; | ||
struct Participant *next; | ||
}; | ||
|
||
struct ChatRoom { | ||
struct Participant *participants; | ||
}; | ||
|
||
struct ChatSession { | ||
int fd; | ||
struct ChatRoom *room; | ||
struct Message *write_messages; | ||
}; | ||
|
||
int push(struct Message **messages, const char *msg, size_t len) | ||
{ | ||
if (messages == NULL || msg == NULL || len == 0) | ||
return 0; | ||
|
||
struct Message *new_msg = (struct Message *)malloc(sizeof(struct Message)); | ||
if (new_msg == NULL) | ||
return 0; | ||
|
||
strncpy(new_msg->buffer, msg, len); | ||
new_msg->next = NULL; | ||
new_msg->length = len; | ||
|
||
if (*messages == NULL) { | ||
*messages = new_msg; | ||
} else { | ||
struct Message *current = *messages; | ||
while (current->next != NULL) | ||
current = current->next; | ||
|
||
current->next = new_msg; | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
int pop(struct Message **messages, char *dst) | ||
{ | ||
if (messages == NULL || dst == NULL) | ||
return 0; | ||
|
||
struct Message *current = *messages; | ||
if (!current) | ||
return 0; | ||
|
||
if (current->next) | ||
*messages = current->next; | ||
else | ||
*messages = NULL; | ||
|
||
size_t length = current->length; | ||
strncpy(dst, current->buffer, length); | ||
free(current); | ||
|
||
return length; | ||
} | ||
|
||
void sendto_room(struct ChatRoom *room, struct ChatSession *session, | ||
const char *msg, size_t length) | ||
{ | ||
if (!room || !msg) | ||
return; | ||
|
||
struct Participant *current = room->participants; | ||
while (current) { | ||
if (current->session != session) | ||
push(¤t->session->write_messages, msg, length); | ||
current = current->next; | ||
} | ||
} | ||
|
||
int join(struct ChatRoom *room, struct ChatSession *session) | ||
{ | ||
if (!room || !session) | ||
return 0; | ||
|
||
struct Participant *new_par = | ||
(struct Participant *)malloc(sizeof(struct Participant)); | ||
if (!new_par) | ||
return 0; | ||
|
||
new_par->next = NULL; | ||
new_par->session = session; | ||
|
||
if (room->participants == NULL) { | ||
room->participants = new_par; | ||
} else { | ||
struct Participant *current = room->participants; | ||
while (current->next) | ||
current = current->next; | ||
current->next = new_par; | ||
} | ||
|
||
sendto_room(room, session, JOIN_MESSAGE, strlen(JOIN_MESSAGE)); | ||
return 1; | ||
} | ||
|
||
int leave(struct ChatRoom *room, struct ChatSession *session) | ||
{ | ||
if (!room || !session) | ||
return 0; | ||
|
||
struct Participant *current = room->participants; | ||
if (current->session == session) { | ||
room->participants = current->next; | ||
} else { | ||
while (current->next) { | ||
if (current->next->session == session) | ||
break; | ||
current = current->next; | ||
} | ||
struct Participant *tmp = current->next; | ||
current->next = current->next->next; | ||
current = tmp; | ||
} | ||
|
||
free(current); | ||
sendto_room(room, NULL, LEFT_MESSAGE, strlen(LEFT_MESSAGE)); | ||
return 1; | ||
} | ||
|
||
void stop(struct ChatSession *session) | ||
{ | ||
if (!session) | ||
return; | ||
leave(session->room, session); | ||
close(session->fd); | ||
free(session); | ||
session = NULL; | ||
} | ||
|
||
void reader(struct Executor *executor, void *data) | ||
{ | ||
struct ChatSession *session = (struct ChatSession *)data; | ||
|
||
char buffer[PACKET_SIZE]; | ||
|
||
while (session) { | ||
ssize_t r_len = async_read(executor, session->fd, buffer, PACKET_SIZE); | ||
if (r_len <= 0) { | ||
fprintf(stderr, "Error in reading from socket\n"); | ||
stop(session); | ||
return; | ||
} | ||
|
||
sendto_room(session->room, session, buffer, r_len); | ||
} | ||
} | ||
|
||
void writer(struct Executor *executor, void *data) | ||
{ | ||
char buffer[PACKET_SIZE]; | ||
struct ChatSession *session = (struct ChatSession *)data; | ||
|
||
while (session) { | ||
ssize_t len = pop(&session->write_messages, buffer); | ||
if (len <= 0) { | ||
struct __kernel_timespec ts; | ||
ts.tv_sec = 1; | ||
ts.tv_nsec = 0; | ||
async_wait(executor, &ts); | ||
continue; | ||
} | ||
|
||
ssize_t w_len = async_write(executor, session->fd, buffer, len); | ||
if (w_len != len) { | ||
stop(session); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
void start(struct Executor *executor, void *data) | ||
{ | ||
struct ChatSession *session = (struct ChatSession *)data; | ||
|
||
join(session->room, session); | ||
|
||
async_exec(executor, &reader, session); | ||
async_exec(executor, &writer, session); | ||
} | ||
|
||
void chat_server(struct Executor *executor, void *data) | ||
{ | ||
struct ChatRoom room; | ||
int server_fd = *(int *)data; | ||
|
||
while (true) { | ||
int fd = async_accept(executor, server_fd); | ||
|
||
struct ChatSession *session = | ||
(struct ChatSession *)malloc(sizeof(struct ChatSession)); | ||
session->fd = fd; | ||
session->room = &room; | ||
session->write_messages = NULL; | ||
|
||
async_exec(executor, &start, session); | ||
} | ||
} | ||
|
||
int main(void) | ||
{ | ||
int server_fd = setup_listen("127.0.0.1", 40000); | ||
|
||
struct Executor executor; | ||
if (init_executor(&executor, 40, 1000) < 0) { | ||
fprintf(stderr, "Error in init_executor\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
async_exec(&executor, &chat_server, &server_fd); | ||
|
||
run(&executor); | ||
free_executor(&executor); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,4 +63,5 @@ int main(void) | |
async_exec(&executor, &echo_client, NULL); | ||
|
||
run(&executor); | ||
free_executor(&executor); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.