Skip to content

Commit 11a989d

Browse files
committed
Rework benchmarks
This commit reworks our benchmark suite: * Use criterion instead of std-lib bencher * Benchmark more different sizes * Add benchmarks for other rust db-crates as comparision * Add a new insert benchmark
1 parent 70ff916 commit 11a989d

19 files changed

Lines changed: 3305 additions & 252 deletions

.github/workflows/benches.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
on:
2+
pull_request:
3+
types:
4+
- labeled
5+
6+
name: Benchmarks
7+
8+
jobs:
9+
benchmarks:
10+
if: github.event.label.name == 'run-benchmarks'
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
backend: ["postgres", "sqlite", "mysql"]
15+
steps:
16+
- name: Checkout sources
17+
if: steps.bench.outputs.triggered == 'true'
18+
uses: actions/checkout@v2
19+
20+
- name: Install postgres (Linux)
21+
if: matrix.backend == 'postgres'
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get install -y libpq-dev postgresql
25+
echo "host all all 127.0.0.1/32 md5" > sudo tee -a /etc/postgresql/10/main/pg_hba.conf
26+
sudo service postgresql restart && sleep 3
27+
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
28+
sudo service postgresql restart && sleep 3
29+
echo '::set-env name=PG_DATABASE_URL::postgres://postgres:postgres@localhost/'
30+
31+
- name: Install sqlite (Linux)
32+
if: matrix.backend == 'sqlite'
33+
run: |
34+
sudo apt-get update
35+
sudo apt-get install -y libsqlite3-dev
36+
echo '::set-env name=SQLITE_DATABASE_URL::/tmp/test.db'
37+
38+
39+
- name: Install mysql (Linux)
40+
if: matrix.backend == 'mysql'
41+
run: |
42+
sudo apt-get update
43+
sudo apt-get -y install mysql-server libmysqlclient-dev
44+
sudo /etc/init.d/mysql start
45+
mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot -proot
46+
echo '::set-env name=MYSQL_DATABASE_URL::mysql://root:root@localhost/diesel_test'
47+
48+
- name: Run benches
49+
uses: jasonwilliams/criterion-compare-action@move_to_actions
50+
with:
51+
cwd: "diesel_bench"
52+
token: ${{ secrets.GITHUB_TOKEN }}
53+

.github/workflows/ci.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
- '0.[0-9]+.x'
88
- '1.[0-9]+.x'
99

10+
1011
name: CI Tests
1112

1213
jobs:
@@ -125,7 +126,7 @@ jobs:
125126
brew install mysql
126127
brew services start mysql
127128
brew services stop mysql;sleep 3;brew services start mysql
128-
sleep 2
129+
sleep 3
129130
macos_mysql_version="$(ls /usr/local/Cellar/mysql)"
130131
/usr/local/Cellar/mysql/${macos_mysql_version}/bin/mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot
131132
echo '::set-env name=MYSQL_DATABASE_URL::mysql://root@localhost/diesel_test'
@@ -260,6 +261,12 @@ jobs:
260261
command: test
261262
args: --manifest-path diesel_dynamic_schema/Cargo.toml --no-default-features --features "diesel/${{ matrix.backend }}"
262263

264+
- name: Run diesel_benches
265+
uses: actions-rs/cargo@v1
266+
with:
267+
command: test
268+
args: --manifest-path diesel_bench/Cargo.toml --no-default-features --features "${{ matrix.backend }}" --bench benchmarks
269+
263270
- name: Run rustdoc (nightly)
264271
if: matrix.run == 'nightly'
265272
uses: actions-rs/cargo@v1

diesel_bench/Cargo.toml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
[package]
2+
name = "diesel_bench"
3+
version = "0.1.0"
4+
authors = []
5+
edition = "2018"
6+
build = "build.rs"
7+
autobenches = false
8+
9+
[workspace]
10+
11+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12+
13+
[dependencies]
14+
dotenv = "0.15"
15+
criterion = "=0.3.2" # critcmp only supports 0.3.2
16+
sqlx = {version = "0.4.0-beta.1", optional = true}
17+
async-std = { version = "1.5", optional = true}
18+
rusqlite = {version = "0.23", optional = true}
19+
rust_postgres = {version = "0.17", optional = true, package = "postgres"}
20+
rust_mysql = {version = "20.0.1", optional = true, package = "mysql"}
21+
rustorm = {version = "0.17", optional = true}
22+
rustorm_dao = {version = "0.5", optional = true}
23+
quaint = {version = "0.2.0-alpha.13", optional = true}
24+
tokio = {version = "0.2", optional = true}
25+
serde = {version = "1", optional = true, features = ["derive"]}
26+
27+
[dependencies.diesel]
28+
path = "../diesel"
29+
default-features = false
30+
features = []
31+
32+
[build-dependencies]
33+
diesel = { path = "../diesel", default-features = false }
34+
diesel_migrations = { path = "../diesel_migrations" }
35+
dotenv = "0.15"
36+
37+
38+
[[bench]]
39+
name = "benchmarks"
40+
path = "benches/lib.rs"
41+
bench = true
42+
harness = false
43+
44+
[features]
45+
default = []
46+
postgres = ["diesel/postgres"]
47+
sqlite = ["diesel/sqlite"]
48+
mysql = ["diesel/mysql"]
49+
50+
[patch.crates-io]
51+
rustorm = {git = "https://github.com/weiznich/rustorm"}
52+
rustorm_dao = {git = "https://github.com/weiznich/rustorm"}
53+
quaint = {git = "https://github.com/prisma/quaint", rev = "ec9384f"}

diesel_bench/Readme.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# A benchmark suite for relational database connection crates in rust
2+
3+
This directory contains a basic benchmark suite that allows to compare different crates that can be used to connect to relational database systems. Those benchmarks are created with the following goals:
4+
5+
a) To track diesels performance and find potential regressions
6+
b) To evaluate potential alternatives for the currently used C-dependencies
7+
c) To compare diesel with the competing crates
8+
9+
It currently supports the following database systems:
10+
11+
* PostgreSQL
12+
* MySQL/MariaDB
13+
* SQLite
14+
15+
and the following crates:
16+
17+
* Diesel
18+
* [SQLx](https://github.com/launchbadge/sqlx)
19+
* [Rustorm](https://github.com/ivanceras/rustorm)
20+
* [Quaint](https://github.com/prisma/quaint)
21+
* [Postgres](https://github.com/sfackler/rust-postgres)
22+
* [Rusqlite](https://github.com/rusqlite/rusqlite)
23+
* [Mysql](https://github.com/blackbeam/rust-mysql-simple)
24+
25+
By default only diesels own benchmarks are executed. To run the benchmark do the following:
26+
27+
```sh
28+
$ DATABASE_URL=your://database/url diesel migration run --migration-dir ../migrations/$backend
29+
$ DATABASE_URL=your://database/url cargo bench --features "$backend"
30+
```
31+
32+
To enable other crates add the following features:
33+
34+
* `SQLx: ` `sqlx sqlx/$backend async-std`
35+
* `Rustorm`: `rustorm rustorm/with-$backend rustorm_dao`
36+
* `Quaint`: `quaint quaint/$backend tokio quaint/serde-support serde`
37+
* `Postgres`: `rust-postgres`
38+
* `Rusqlite`: `rusqlite`
39+
* `Mysql`: `rust-mysql`
40+
41+
## Benchmarks
42+
43+
### Common data structures
44+
45+
#### Table definitions
46+
47+
The following schema definition was used. (For Mysql/Sqlite postgres specific types where replaced by their equivalent type).
48+
```sql
49+
CREATE TABLE users (
50+
id SERIAL PRIMARY KEY,
51+
name VARCHAR NOT NULL,
52+
hair_color VARCHAR
53+
);
54+
55+
CREATE TABLE posts (
56+
id SERIAL PRIMARY KEY,
57+
user_id INTEGER NOT NULL,
58+
title VARCHAR NOT NULL,
59+
body TEXT
60+
);
61+
62+
CREATE TABLE comments (
63+
id SERIAL PRIMARY KEY,
64+
post_id INTEGER NOT NULL,
65+
text TEXT NOT NULL
66+
);
67+
68+
```
69+
70+
71+
72+
73+
#### Struct definitions
74+
75+
```rust
76+
pub struct User {
77+
pub id: i32,
78+
pub name: String,
79+
pub hair_color: Option<String>,
80+
}
81+
82+
pub struct Post {
83+
pub id: i32,
84+
pub user_id: i32,
85+
pub title: String,
86+
pub body: Option<String>,
87+
}
88+
89+
pub struct Comment {
90+
id: i32,
91+
post_id: i32,
92+
text: String,
93+
}
94+
```
95+
96+
Field types are allowed to differ, to whatever type is expected compatible by the corresponding crate.
97+
98+
### `bench_trivial_query`
99+
100+
101+
This benchmark tests how entities from a single table are loaded. Before starting the benchmark 1, 10, 100, 1000 or 10000 entries are inserted into the `users` table. For this the `id` of the user is provided by the autoincrementing id, the `name` is set to `User {id}` and the `hair_color` is set to `Null`. An implementation of this benchmark is expected to return a list of the type `User.
102+
103+
### `bench_medium_complex_query`
104+
105+
This benchmark tests how entities from more than one table are loaded. Before starting the benchmark 1, 10, 1000, 10000 entries are inserted into the `users` table. For this the `id` of the user is provided by the autoincrementing id, the `name` is set to `User {id}` and the `hair_color` is set to `"black"` for even id's, to `"brown"` otherwise. An implementation of this benchmark is expected to return a list of the type `(User, Option<Post>)` so that matching pairs of `User` and `Option<Post>` are returned. Though the `posts` table is empty the corresponding implementation needs to query both tables.
106+
107+
### `bench_insert`
108+
109+
This benchmark tests how fast entities are inserted into the database. For this each implementation gets a size how many entries are scheduled to be inserted into the database. An implementation of this benchmark is expected to insert as many entries into the user table as the number provided by the benchmark framework. It is not required to clean up the already inserted entries at any time. Newly inserted users are generated using the following rules: `id` of the user is provided by the autoincrementing id, the `name` is set to `User {batch_id}` and the `hair_color` is set to `"hair_color"`.
110+
111+
### `bench_loading_associations_sequentially`
112+
113+
This benchmark tests how fast a complex set of entities is received from the database.
114+
Before starting the benchmark a large amount of data needs to be inserted into the database. The `users` table is required to contain 100 entries (or for sqlite 9 entries) based on the following rules: `id` is determined by the autoincrementing column, `name` is set to `User {batch_id}` and `hair_color` is set to `"black"` for even id's, otherwise to `"brown"`. For each entry in the `users` table, 10 entries in the `posts` table need to exist. Each entry in the `posts` table is based on the following rules: `id` is autogenerated by autoincrementing column, `title` is set to `Post {post_batch_id} for user {user_id}` where `post_batch_id` referees to number of the current post in relation to the user (so between 0 and 9), `user_id` is set to the corresponding user's id, and `body` is set to `NULL`. For each entry in the `posts` table 10 entries in the `comments` table are generated based on the following rules:
115+
`id` is set to the autoincrementing default value, `text` is set to `Comment {comment_batch_id} on post {post_id}` where `comment_batch_id` referees to the number of the current comment in relation to the post (so between 0 and 9), `post_id` is set tho the corresponding posts's id.
116+
An implementation of the benchmark is expected to return a list of the type `(User, Vec<(Post, Vec<Comment>)>)`, that contains all users wit all corresponding comments and posts grouped by their corresponding associations.

0 commit comments

Comments
 (0)