Skip to content

Commit 7fb6883

Browse files
authored
Merge pull request #66 from stackhpc/operation-bench
Add operations benchmark
2 parents 8e4be4a + 6759667 commit 7fb6883

File tree

11 files changed

+129
-40
lines changed

11 files changed

+129
-40
lines changed

.github/workflows/pull-request.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ jobs:
1414

1515
- name: Build
1616
run: make build
17+
docs:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v3
22+
23+
- name: Build documentation
24+
run: make docs
25+
lint:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v3
30+
31+
- name: Lint
32+
run: make lint
1733
test:
1834
runs-on: ubuntu-latest
1935
steps:

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ serde_test = "1.0"
5151
name = "byte_order"
5252
harness = false
5353

54+
[[bench]]
55+
name = "operations"
56+
harness = false
57+
5458
[[bench]]
5559
name = "shuffle"
5660
harness = false

Makefile

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22
build:
33
@docker buildx build -t reductionist .
44

5+
.PHONY: docs
6+
docs:
7+
@docker buildx build --build-arg PROFILE=dev --target builder -t reductionist-test .
8+
@docker run --rm -e RUSTDOCFLAGS="-D warnings" reductionist-test cargo doc --no-deps
9+
10+
.PHONY: lint
11+
lint:
12+
@docker buildx build --build-arg PROFILE=dev --target builder -t reductionist-test .
13+
@docker run --rm reductionist-test cargo check --color always
14+
515
.PHONY: test
616
test:
717
@docker buildx build --build-arg PROFILE=dev --target builder -t reductionist-test .
8-
@docker run --rm reductionist-test cargo check --color always
918
@docker run --rm reductionist-test cargo test --color always
10-
@docker run --rm reductionist-test cargo bench --color always
1119

1220
.PHONY: run
1321
run:

benches/compression.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use axum::body::Bytes;
77
use flate2::read::{GzEncoder, ZlibEncoder};
88
use flate2::Compression;
99
use std::io::Read;
10+
// Bring trait into scope to use as_bytes method.
11+
use zerocopy::AsBytes;
1012

1113
fn compress_gzip(data: &[u8]) -> Bytes {
1214
// Adapated from flate2 documentation.
@@ -39,10 +41,9 @@ fn criterion_benchmark(c: &mut Criterion) {
3941
for (compression, name) in compression_algs {
4042
for size_k in [64, 256, 1024] {
4143
let size = size_k * 1024;
42-
let data: Vec<u8> = (0_u32..size)
43-
.map(|i| u8::try_from(i % 256).unwrap())
44-
.collect::<Vec<u8>>();
45-
let compressed = compress(compression, data.as_ref());
44+
let data: Vec<i64> = (0_i64..size).map(|i| i % 256).collect::<Vec<i64>>();
45+
let bytes = Bytes::copy_from_slice(data.as_bytes());
46+
let compressed = compress(compression, &bytes);
4647
let name = format!("decompress({}, {})", name, size);
4748
c.bench_function(&name, |b| {
4849
b.iter(|| {

benches/operations.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/// Benchmarks for numerical operations.
2+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
3+
use reductionist::error::ActiveStorageError;
4+
use reductionist::models::{DType, RequestData, Response};
5+
use reductionist::operation::Operation;
6+
use reductionist::operations;
7+
use reductionist::types::Missing;
8+
use url::Url;
9+
// Bring trait into scope to use as_bytes method.
10+
use zerocopy::AsBytes;
11+
12+
fn get_test_request_data() -> RequestData {
13+
RequestData {
14+
source: Url::parse("http://example.com").unwrap(),
15+
bucket: "bar".to_string(),
16+
object: "baz".to_string(),
17+
dtype: DType::Int32,
18+
byte_order: None,
19+
offset: None,
20+
size: None,
21+
shape: None,
22+
order: None,
23+
selection: None,
24+
compression: None,
25+
filters: None,
26+
missing: None,
27+
}
28+
}
29+
30+
type ExecuteFn = dyn Fn(&RequestData, Vec<u8>) -> Result<Response, ActiveStorageError>;
31+
32+
fn criterion_benchmark(c: &mut Criterion) {
33+
for size_k in [64, 256, 1024, 4096] {
34+
let size = size_k * 1024;
35+
let data: Vec<i64> = (0_i64..size).map(|i| i % 256).collect::<Vec<i64>>();
36+
let data: Vec<u8> = data.as_bytes().into();
37+
let missings = vec![
38+
None,
39+
Some(Missing::MissingValue(42.into())),
40+
Some(Missing::MissingValues(vec![42.into()])),
41+
Some(Missing::ValidMax(128.into())),
42+
Some(Missing::ValidMin(128.into())),
43+
Some(Missing::ValidRange(5.into(), 250.into())),
44+
];
45+
let operations: [(&str, Box<ExecuteFn>); 5] = [
46+
("count", Box::new(operations::Count::execute)),
47+
("max", Box::new(operations::Max::execute)),
48+
("min", Box::new(operations::Min::execute)),
49+
("select", Box::new(operations::Select::execute)),
50+
("sum", Box::new(operations::Sum::execute)),
51+
];
52+
for (op_name, execute) in operations {
53+
for missing in missings.clone() {
54+
let name = format!("{}({}, {:?})", op_name, size, missing);
55+
c.bench_function(&name, |b| {
56+
b.iter(|| {
57+
let mut request_data = get_test_request_data();
58+
request_data.dtype = DType::Int64;
59+
request_data.missing = missing.clone();
60+
execute(&request_data, black_box(data.clone())).unwrap();
61+
})
62+
});
63+
}
64+
}
65+
}
66+
}
67+
68+
criterion_group!(benches, criterion_benchmark);
69+
criterion_main!(benches);

benches/shuffle.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
/// Benchmarks for the byte shuffle filter implementation.
2+
use axum::body::Bytes;
23
use criterion::{black_box, criterion_group, criterion_main, Criterion};
34
use reductionist::filters::shuffle;
5+
// Bring trait into scope to use as_bytes method.
6+
use zerocopy::AsBytes;
47

58
fn criterion_benchmark(c: &mut Criterion) {
6-
for size_k in [64, 256, 1024] {
9+
for size_k in [64, 256, 1024, 4096] {
710
let size = size_k * 1024;
8-
let data: Vec<u8> = (0_u32..size)
9-
.map(|i| u8::try_from(i % 256).unwrap())
10-
.collect::<Vec<u8>>();
11-
let bytes = data.into();
11+
let data: Vec<i64> = (0_i64..size).map(|i| i % 256).collect::<Vec<i64>>();
12+
let bytes = Bytes::copy_from_slice(data.as_bytes());
1213
for element_size in [2, 4, 8] {
1314
let name = format!("deshuffle({}, {})", size, element_size);
1415
c.bench_function(&name, |b| {

src/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ fn router() -> Router {
104104

105105
/// Reductionist Server Service type alias
106106
///
107-
/// This type implements [tower_service::Service].
108-
// FIXME: The Service type should be some form of tower_service::Service, but couldn't find the
107+
/// This type implements [tower::Service].
108+
// FIXME: The Service type should be some form of tower::Service, but couldn't find the
109109
// necessary trait bounds.
110110
pub type Service = tower_http::normalize_path::NormalizePath<Router>;
111111

src/array.rs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -218,65 +218,47 @@ mod tests {
218218
let value: u32 = 42;
219219
let mut buf = maligned::align_first::<u8, maligned::A4>(4);
220220
buf.extend_from_slice(&value.to_ne_bytes());
221-
assert_eq!(
222-
[value],
223-
from_bytes::<u32>(&mut buf).unwrap()
224-
);
221+
assert_eq!([value], from_bytes::<u32>(&mut buf).unwrap());
225222
}
226223

227224
#[test]
228225
fn from_bytes_u64() {
229226
let value: u64 = u64::max_value();
230227
let mut buf = maligned::align_first::<u8, maligned::A8>(8);
231228
buf.extend_from_slice(&value.to_ne_bytes());
232-
assert_eq!(
233-
[value],
234-
from_bytes::<u64>(&mut buf).unwrap()
235-
);
229+
assert_eq!([value], from_bytes::<u64>(&mut buf).unwrap());
236230
}
237231

238232
#[test]
239233
fn from_bytes_i32() {
240234
let value: i32 = -42;
241235
let mut buf = maligned::align_first::<u8, maligned::A4>(4);
242236
buf.extend_from_slice(&value.to_ne_bytes());
243-
assert_eq!(
244-
[value],
245-
from_bytes::<i32>(&mut buf).unwrap()
246-
);
237+
assert_eq!([value], from_bytes::<i32>(&mut buf).unwrap());
247238
}
248239

249240
#[test]
250241
fn from_bytes_i64() {
251242
let value: i64 = i64::min_value();
252243
let mut buf = maligned::align_first::<u8, maligned::A8>(8);
253244
buf.extend_from_slice(&value.to_ne_bytes());
254-
assert_eq!(
255-
[value],
256-
from_bytes::<i64>(&mut buf).unwrap()
257-
);
245+
assert_eq!([value], from_bytes::<i64>(&mut buf).unwrap());
258246
}
259247

260248
#[test]
261249
fn from_bytes_f32() {
262250
let value: f32 = f32::min_value();
263251
let mut buf = maligned::align_first::<u8, maligned::A4>(4);
264252
buf.extend_from_slice(&value.to_ne_bytes());
265-
assert_eq!(
266-
[value],
267-
from_bytes::<f32>(&mut buf).unwrap()
268-
);
253+
assert_eq!([value], from_bytes::<f32>(&mut buf).unwrap());
269254
}
270255

271256
#[test]
272257
fn from_bytes_f64() {
273258
let value: f64 = f64::max_value();
274259
let mut buf = maligned::align_first::<u8, maligned::A8>(8);
275260
buf.extend_from_slice(&value.to_ne_bytes());
276-
assert_eq!(
277-
[value],
278-
from_bytes::<f64>(&mut buf).unwrap()
279-
);
261+
assert_eq!([value], from_bytes::<f64>(&mut buf).unwrap());
280262
}
281263

282264
fn assert_from_bytes_error<T: std::fmt::Debug>(result: Result<T, ActiveStorageError>) {

src/operation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub trait Operation {
5656
/// # Arguments
5757
///
5858
/// * `request_data`: RequestData object for the request
59-
/// * `data`: Vec<u8> containing data to operate on.
59+
/// * `data`: [`Vec<u8>`] containing data to operate on.
6060
fn execute(
6161
request_data: &models::RequestData,
6262
data: Vec<u8>,

src/types/missing.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ pub enum Missing<T> {
3030
}
3131

3232
impl Missing<DValue> {
33-
/// Validate a Missing<DValue> object for a given DType.
33+
/// Validate a [`Missing<DValue>`](crate::types::Missing) object for a given
34+
/// [DType](crate::models::DType).
3435
pub fn validate(&self, dtype: DType) -> Result<(), ValidationError> {
3536
match dtype {
3637
DType::Int32 => Missing::<i32>::validate_dvalue(self),
@@ -44,7 +45,8 @@ impl Missing<DValue> {
4445
}
4546

4647
impl<T: PartialOrd + Serialize + TryFromDValue> Missing<T> {
47-
/// Validate a Missing<DValue> for Missing<T> where T is a supported primitive numeric type.
48+
/// Validate a [`Missing<DValue>`](crate::types::Missing) for Missing<T> where T is a supported
49+
/// primitive numeric type.
4850
fn validate_dvalue(missing: &Missing<DValue>) -> Result<(), ValidationError> {
4951
// Perform a conversion to the primitive based type.
5052
let missing_primitive = Self::try_from(missing).map_err(|err| {

0 commit comments

Comments
 (0)