Skip to content

Commit

Permalink
initiate benchmark, add chat example
Browse files Browse the repository at this point in the history
  • Loading branch information
enum-class committed Jan 25, 2024
1 parent bf85d58 commit e9af9d1
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 2 deletions.
Binary file added .github/artifacts/rtt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ endif()

if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_definitions(-DNDEBUG)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()

set(CMAKE_CXX_FLAGS_RELEASE "-O3")
set(CMAKE_EXPORT_COMPILE_COMMANDS On)
add_compile_options(-Wall -Wextra -pedantic -Werror)

Expand Down
61 changes: 61 additions & 0 deletions benchmarks/README.md
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.
1 change: 1 addition & 0 deletions benchmarks/pingpong-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ void *init_server(void *data)
init_executor(&executor, FRAME_COUNT, RING_SIZE);
async_exec(&executor, &pingpong_server, &server_fd);
run(&executor);
free_executor(&executor);
pthread_exit(NULL);
}

Expand Down
18 changes: 16 additions & 2 deletions examples/CMakeLists.txt
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)

240 changes: 240 additions & 0 deletions examples/chat/chat-server.c
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(&current->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);
}
1 change: 1 addition & 0 deletions examples/echo-client.c → examples/echo/echo-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ int main(void)
async_exec(&executor, &echo_client, NULL);

run(&executor);
free_executor(&executor);
}
1 change: 1 addition & 0 deletions examples/echo-server.c → examples/echo/echo-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ int main(void)
async_exec(&executor, &echo_server, &server_fd);

run(&executor);
free_executor(&executor);
return 0;
}
File renamed without changes.

0 comments on commit e9af9d1

Please sign in to comment.