diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 00000000..484f5e31 --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,27 @@ +name: Benchmark + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: + - master + +jobs: + check_benchmark: + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Check benchmark + uses: actions-rs/cargo@v1 + with: + command: bench diff --git a/Cargo.lock b/Cargo.lock index 6872c72a..9371192d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -590,6 +590,7 @@ dependencies = [ "clap", "criterion-plot", "csv", + "futures", "itertools 0.10.1", "lazy_static", "num-traits", @@ -602,6 +603,7 @@ dependencies = [ "serde_derive", "serde_json", "tinytemplate", + "tokio 1.9.0", "walkdir", ] diff --git a/Cargo.toml b/Cargo.toml index c6764c66..413a0a3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,8 +35,9 @@ serde_json = "1.0" serde_yaml = "0.8" tilejson = "0.2" -[dev-dependencies] -criterion = "0.3" +[dev-dependencies.criterion] +version = "0.3.4" +features = ["async_futures", "async_tokio", "html_reports"] [dev-dependencies.cargo-husky] version = "1" @@ -44,5 +45,5 @@ default-features = false features = ["run-for-all", "prepush-hook", "run-cargo-fmt", "run-cargo-clippy", "run-cargo-test"] [[bench]] -name = "server" +name = "sources" harness = false diff --git a/README.md b/README.md index aa67b54c..9c7d80bd 100755 --- a/README.md +++ b/README.md @@ -616,3 +616,11 @@ Make your changes, and check if all the tests are running ```shell DATABASE_URL=postgres://postgres@localhost/db cargo test ``` + +You can also run benchmarks with + +```shell +DATABASE_URL=postgres://postgres@localhost/db cargo bench +``` + +An HTML report displaying the results of the benchmark will be generated under `target/criterion/report/index.html` diff --git a/benches/server.rs b/benches/server.rs deleted file mode 100644 index f497da83..00000000 --- a/benches/server.rs +++ /dev/null @@ -1,37 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; - -use actix_web::dev::Service; -use actix_web::{test, App}; - -use martin::dev::{mock_function_sources, mock_state, mock_table_sources}; -use martin::server::router; - -fn criterion_benchmark(c: &mut Criterion) { - let state = test::run_on(|| mock_state(mock_table_sources(), mock_function_sources())); - let mut app = test::init_service(App::new().app_data(state).configure(router)); - - c.bench_function("/public.table_source/0/0/0.pbf", |b| { - b.iter(|| { - let req = test::TestRequest::get() - .uri("/public.table_source/0/0/0.pbf") - .to_request(); - - let future = test::run_on(|| app.call(req)); - let _response = test::block_on(future).unwrap(); - }) - }); - - c.bench_function("/rpc/public.function_source/0/0/0.pbf", |b| { - b.iter(|| { - let req = test::TestRequest::get() - .uri("/rpc/public.function_source/0/0/0.pbf") - .to_request(); - - let future = test::run_on(|| app.call(req)); - let _response = test::block_on(future).unwrap(); - }) - }); -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/benches/sources.rs b/benches/sources.rs new file mode 100644 index 00000000..3ee442d4 --- /dev/null +++ b/benches/sources.rs @@ -0,0 +1,118 @@ +use std::collections::HashMap; + +use criterion::Criterion; +use criterion::{criterion_group, criterion_main}; + +use martin::composite_source::CompositeSource; +use martin::dev::make_pool; +use martin::function_source::FunctionSource; +use martin::source::{Source, Xyz}; +use martin::table_source::TableSource; + +fn mock_table_source(schema: &str, table: &str) -> TableSource { + TableSource { + id: format!("{}.{}", schema, table), + schema: schema.to_owned(), + table: table.to_owned(), + id_column: None, + geometry_column: "geom".to_owned(), + srid: 3857, + extent: Some(4096), + buffer: Some(64), + clip_geom: Some(true), + geometry_type: None, + properties: HashMap::new(), + } +} + +fn mock_function_source(schema: &str, function: &str) -> FunctionSource { + FunctionSource { + id: format!("{}.{}", schema, function), + schema: schema.to_owned(), + function: function.to_owned(), + } +} + +async fn get_table_source() { + let source = mock_table_source("public", "table_source"); + let _tilejson = source.get_tilejson(); +} + +async fn get_table_source_tile() { + let pool = make_pool(); + let mut connection = pool.get().unwrap(); + + let source = mock_table_source("public", "table_source"); + let xyz = Xyz { z: 0, x: 0, y: 0 }; + + let _tile = source.get_tile(&mut connection, &xyz, &None).unwrap(); +} + +async fn get_composite_source() { + let points1 = mock_table_source("public", "points1"); + let points2 = mock_table_source("public", "points2"); + + let source = CompositeSource { + id: "public.points1,public.points2".to_owned(), + table_sources: vec![points1, points2], + }; + + let _tilejson = source.get_tilejson(); +} + +async fn get_composite_source_tile() { + let pool = make_pool(); + let mut connection = pool.get().unwrap(); + + let points1 = mock_table_source("public", "points1"); + let points2 = mock_table_source("public", "points2"); + + let source = CompositeSource { + id: "public.points1,public.points2".to_owned(), + table_sources: vec![points1, points2], + }; + + let xyz = Xyz { z: 0, x: 0, y: 0 }; + let _tile = source.get_tile(&mut connection, &xyz, &None).unwrap(); +} + +async fn get_function_source() { + let source = mock_function_source("public", "function_source"); + let _tilejson = source.get_tilejson(); +} + +async fn get_function_source_tile() { + let pool = make_pool(); + let mut connection = pool.get().unwrap(); + + let source = mock_function_source("public", "function_source"); + let xyz = Xyz { z: 0, x: 0, y: 0 }; + + let _tile = source.get_tile(&mut connection, &xyz, &None).unwrap(); +} + +fn table_source(c: &mut Criterion) { + c.bench_function("get_table_source", |b| b.iter(|| get_table_source())); + c.bench_function("get_table_source_tile", |b| { + b.iter(|| get_table_source_tile()) + }); +} + +fn composite_source(c: &mut Criterion) { + c.bench_function("get_composite_source", |b| { + b.iter(|| get_composite_source()) + }); + c.bench_function("get_composite_source_tile", |b| { + b.iter(|| get_composite_source_tile()) + }); +} + +fn function_source(c: &mut Criterion) { + c.bench_function("get_function_source", |b| b.iter(|| get_function_source())); + c.bench_function("get_function_source_tile", |b| { + b.iter(|| get_function_source_tile()) + }); +} + +criterion_group!(benches, table_source, composite_source, function_source); +criterion_main!(benches); diff --git a/src/dev.rs b/src/dev.rs index a8bc08d6..a5614a84 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use actix::{Actor, Addr, SyncArbiter}; use crate::coordinator_actor::CoordinatorActor; -use crate::db::setup_connection_pool; +use crate::db::{setup_connection_pool, Pool}; use crate::db_actor::DbActor; use crate::function_source::{FunctionSource, FunctionSources}; use crate::server::AppState; @@ -86,17 +86,23 @@ pub fn mock_function_sources() -> Option { Some(function_sources) } -pub fn mock_state( - table_sources: Option, - function_sources: Option, - watch_mode: bool, -) -> AppState { +pub fn make_pool() -> Pool { let connection_string: String = env::var("DATABASE_URL").unwrap(); info!("Connecting to {}", connection_string); let pool = setup_connection_pool(&connection_string, Some(1), false).unwrap(); info!("Connected to {}", connection_string); + pool +} + +pub fn mock_state( + table_sources: Option, + function_sources: Option, + watch_mode: bool, +) -> AppState { + let pool = make_pool(); + let db = SyncArbiter::start(3, move || DbActor(pool.clone())); let coordinator: Addr<_> = CoordinatorActor::default().start();