Skip to content

Commit c355c08

Browse files
committed
init commit
0 parents  commit c355c08

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+43305
-0
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.ipynb filter=ipynb_filter

.gitignore

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# autogen grpc source
2+
chunkdemo.grpc.pb*
3+
chunkdemo.pb.*
4+
5+
# emacs temp and backup files
6+
*~
7+
#*#
8+
9+
# python
10+
*.pyc
11+
*__pycache__/
12+
13+
# cmake
14+
cmake/
15+
16+
# autogen python source
17+
chunkdemo_pb2*
18+
19+
# VSCode settings files
20+
.vscode

README.md

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
### Large Array Serialization using gRPC
2+
3+
#### Prerequisites
4+
5+
Install `gRPC` for C++ using by following the directions at [gRPC Quick Start](https://grpc.io/docs/languages/cpp/quickstart/). This will install both `gRPC` and `protobuf`, which you will need to compile the server and client. If you wish you use the Python server as well, install `Python 3.6` or newer.
6+
7+
You will also need `cmake` to build the server and client, and the directions for installing a modern version of `cmake` are also at [gRPC Quick Start](https://grpc.io/docs/languages/cpp/quickstart/).
8+
9+
10+
#### C++ Server
11+
12+
Enter the `cpp` directory and run the following to build the `demo_server`
13+
14+
```
15+
mkdir build
16+
cd build
17+
cmake ..
18+
make -j
19+
```
20+
21+
Next, start the `demo_server` with:
22+
23+
24+
```
25+
./demo_server
26+
```
27+
28+
You should see:
29+
30+
```
31+
Server listening on 0.0.0.0:50000
32+
```
33+
34+
The C++ server is now waiting for connections from port 50000.
35+
36+
37+
#### C++ Client
38+
39+
After compiling the C++ server, start the C++ client with
40+
41+
```
42+
./demo_client
43+
```
44+
45+
This will automatically run the streaming demo:
46+
47+
```
48+
./demo_client
49+
Connected to server at: localhost:50000
50+
Created an INT32 array on the server size 10000000
51+
Array is 38.15 MB
52+
53+
Testing with byte stream...
54+
Using chunk size 256 kB
55+
Average time: 0.0167268
56+
Approx speed: 2.23 GBps
57+
58+
Testing with repeated messages...
59+
Average time: 0.0932537
60+
Approx speed: 409.07 MBps
61+
62+
Testing with repeated chunked messages...
63+
Average time: 0.0300367
64+
Approx speed: 1.24 GBps
65+
66+
```
67+
68+
Use the `-h` flag to see the options available when running the client.
69+
70+
```
71+
./demo_client -h
72+
```
73+
74+
```
75+
demo_client [options]
76+
Options:
77+
-h Print this help
78+
--target Set the target channel (default localhost:50000)
79+
--chunk_size Set the chunk size in kB (default 256)
80+
--skip_repeated Skip testing the repeated messages
81+
--array_size Size of array in int32
82+
--ntimes_stream Number of times to test the stream
83+
```
84+
85+
#### Python Client
86+
87+
First, ensure you have the following packages installed:
88+
89+
- `grpcio`
90+
- `google-api-python-client`
91+
92+
93+
Also, make sure that the version of the packages is greater than or equal to your `gRPC` C++ version.
94+
95+
96+
Next, enter the `python` directory and run:
97+
98+
```
99+
make
100+
```
101+
102+
This will generate the python gRPC interface files `chunkdemo_pb2.py`
103+
and `chunkdemo_pb2_grpc.py`. While you have the C++ server running,
104+
run:
105+
106+
```
107+
python client.py
108+
```
109+
110+
This will connect to the server at your local host and run a basic
111+
array transfer test using both a byte stream and repeated messages.
112+
113+
```
114+
Connected to server at 127.0.0.1:50000
115+
Created an INT32 array on the server size 20000000
116+
Testing with byte stream...
117+
Average time: 0.03763810700038448
118+
Aprox speed: 2.0GiB
119+
120+
Testing with repeated messages...
121+
Average time: 0.8858063033355089
122+
Aprox speed: 86.1MiB
123+
124+
```

benchmark/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
venv
2+
build
3+
.*done

benchmark/CMakeLists.txt

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
project(grpc_benchmark)
2+
cmake_minimum_required(VERSION 3.1)
3+
4+
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
5+
conan_basic_setup()
6+
7+
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fopenmp -Wall -Wextra -Wpedantic -Werror -Wno-unused-parameter -Wno-pragmas")
8+
9+
add_subdirectory(generated_src)
10+
add_subdirectory(src)

benchmark/Makefile

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# This Makefile ties together the steps required to install C++ and
2+
# Python dependencies. It only works on Linux, but can serve as
3+
# self-documenting instructions for building on Windows.
4+
# As such, they are ordered roughly in chronological order.
5+
6+
# Commands with a preceding '@' are for the purposes of make only,
7+
# there is no need to reproduce then on Windows.
8+
9+
PROTOC=build/protoc
10+
PROTOS_DIR=src/protos
11+
GRPC_CPP_PLUGIN_PATH=build/grpc_cpp_plugin
12+
VENV_ACTIVATE=venv/bin/activate
13+
14+
SHELL := /bin/bash
15+
16+
UNCONSTRAINED_RESULT_FILES= results/sync.csv results/threaded_client.csv
17+
CONSTRAINED_RESULT_FILES= results/rate_100mbit.csv results/delay_1ms.csv results/compressionlevel_3_rate_100mbit.csv
18+
19+
all: compile_project $(UNCONSTRAINED_RESULT_FILES) $(CONSTRAINED_RESULT_FILES)
20+
21+
venv/bin/activate:
22+
@echo "### Creating Python virtual environment ###"
23+
python3 -m venv venv
24+
25+
venv/.done: requirements.txt venv/bin/activate
26+
@echo "### Installing Python dependencies ###"
27+
. $(VENV_ACTIVATE) && \
28+
pip install -U pip && \
29+
pip install -r requirements.txt && \
30+
conan profile new --detect --force grpc_cpp_benchmark && \
31+
conan profile update settings.compiler.libcxx=libstdc++11 grpc_cpp_benchmark && \
32+
conan remote add -f conancenter https://center.conan.io
33+
@touch $@
34+
35+
RENDERED_TEMPLATE_FILES= src/protos/send_array.proto src/common/types_lookup.hpp src/client/sync_client.cpp src/server/sync_server.cpp src/client/sync_client_fixed_chunksize.cpp
36+
37+
$(RENDERED_TEMPLATE_FILES): % : render_templates.py venv/.done %.in
38+
@echo "### Rendering input file from template ###"
39+
. $(VENV_ACTIVATE) && python $< $@.in $@
40+
41+
build/.deps_done: conanfile.txt venv/.done
42+
@echo "### Installing C++ dependencies ###"
43+
mkdir -p build
44+
. $(VENV_ACTIVATE) && conan install -if build --build=* --profile=grpc_cpp_benchmark .
45+
@touch $@
46+
47+
generated_src/.generate_done: src/protos/send_array.proto build/.deps_done
48+
@echo "### Compiling .proto file to C++ source ###"
49+
$(PROTOC) --proto_path=$(PROTOS_DIR) --cpp_out=generated_src --grpc_out=generated_src --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) send_array.proto
50+
@touch $@
51+
52+
BIN_OUTPUT_FILES= build/bin/measure_memcpy build/bin/sync_client build/bin/sync_client_fixed_chunksize build/bin/sync_server
53+
54+
$(BIN_OUTPUT_FILES): compile_project;
55+
56+
compile_project: build/.deps_done generated_src/.generate_done $(RENDERED_TEMPLATE_FILES) CMakeLists.txt **/CMakeLists.txt
57+
@echo "### Setting up CMake ###"
58+
cd build && cmake ..
59+
@echo "### Compiling the C++ source ###"
60+
$(MAKE) -C build
61+
62+
$(UNCONSTRAINED_RESULT_FILES): run_unconstrained_measurements;
63+
run_unconstrained_measurements: runner/run_measurements.py build/bin/sync_client build/bin/sync_server
64+
@echo "### Running unconstrained measurements ###"
65+
. $(VENV_ACTIVATE) && python $<
66+
67+
68+
$(CONSTRAINED_RESULT_FILES): runner/run_constrained.py build/bin/sync_client_fixed_chunksize build/bin/sync_server
69+
@echo "### Running constrained measurements ###"
70+
. $(VENV_ACTIVATE) && python $< $@
71+
72+
results/memcpy.csv: build/bin/measure_memcpy
73+
build/bin/measure_memcpy > $@
74+
75+
clean:
76+
rm -rf venv
77+
rm -rf build
78+
rm -rf generated_src/*.cc
79+
rm -rf generated_src/*.h
80+
rm -rf **/.*done
81+
rm -f src/protos/*.proto
82+
rm -f src/types_lookup.hpp
83+
rm -f results/*.csv
84+
85+
.PHONY: clean compile_project all run_constrained_measurements

benchmark/README.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# gRPC array benchmark
2+
3+
## Design goal
4+
5+
The goal of this benchmark is to test the performance of sending an array via gRPC, in various different ways and for different data types.
6+
7+
As such, architecturally sound interfaces were sacrificed where that would make it harder to measure performance. Do **not** use this code as an array sending library without at least _some_ modification.
8+
9+
## Building
10+
11+
### Prerequisites
12+
13+
- `gcc` version 7 or newer
14+
- `python3`
15+
- `cmake` version 3.16 or newer (older versions may work, but weren't tested)
16+
17+
Networking tools (only required for some parts)
18+
- `tc`
19+
- `iperf`
20+
21+
22+
### Build steps
23+
24+
On Linux, the whole project can be built with
25+
26+
```bash
27+
make -j 1
28+
```
29+
30+
However, it's quite likely that you will encounter edge cases that the ``Makefile`` doesn't cover.
31+
32+
On Windows, it should still be possible to build and run the benchmarks. Follow the ``Makefile`` from top to bottom for instructions.
33+
34+
Benchmarks which include bandwidth throttling and artificial latency are implemented specifically with Linux tools.
35+
36+
Some peculiarities:
37+
- The `.proto` files, as well as some of the C++ source, are written as a [Jinja2](https://jinja2docs.readthedocs.io/) template. The `render_templates.py` script expands these. This is a quick and dirty way of supporting all possible `protobuf` types, without _that_ much template magic.
38+
- The `tc qdisc` commands for limiting the netword require `sudo`. As it stands, the `make` command will block prompt for the password when executing these commands.

benchmark/conanfile.txt

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[requires]
2+
grpc/1.39.1
3+
4+
[generators]
5+
cmake
6+
7+
[imports]
8+
bin, protoc* -> . @ root_package=protobuf
9+
bin, grpc_cpp_plugin -> . @ root_package=grpc

benchmark/generated_src/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.cc
2+
*.h
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
add_library(send_array STATIC send_array.pb.cc send_array.grpc.pb.cc)
2+
3+
target_include_directories(send_array PUBLIC ${CONAN_LIB_DIRS})
4+
target_include_directories(send_array PUBLIC .)
5+
6+
target_link_libraries(send_array ${CONAN_LIBS})

benchmark/render_templates.py

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import sys
2+
3+
import pathlib
4+
5+
import jinja2
6+
7+
DIR_PATH = pathlib.Path(__file__).parent
8+
9+
TYPES_LIST = [
10+
("double", "double"),
11+
("float", "float"),
12+
]
13+
for int_prefix_proto in [
14+
"int",
15+
"sint",
16+
"sfixed",
17+
]:
18+
for size in [
19+
32,
20+
64,
21+
]:
22+
TYPES_LIST.append((f"{int_prefix_proto}{size}", f"int{size}_t"))
23+
24+
25+
def render_template(*, in_file, out_file, context):
26+
with open(in_file, "r") as in_f:
27+
template = jinja2.Template(in_f.read())
28+
29+
with open(out_file, "w") as out_f:
30+
out_f.write(template.render(**context))
31+
32+
33+
if __name__ == "__main__":
34+
in_file = sys.argv[1]
35+
out_file = sys.argv[2]
36+
37+
render_template(
38+
in_file=in_file,
39+
out_file=out_file,
40+
context=dict(data_types=TYPES_LIST),
41+
)

benchmark/requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
conan
2+
jinja2

benchmark/results/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.csv
2+
!examples/**/*.csv

0 commit comments

Comments
 (0)