From 7174060ded24b97a0811fddbb67e6c7037b498a4 Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 27 Aug 2026 00:28:02 +0800 Subject: [PATCH 1/5] refactor(channel): collect existing implementations --- asyncband/src/{mpsc => channel}/error.rs | 0 asyncband/src/channel/mod.rs | 24 +++++++++++++++++++ asyncband/src/{ => channel}/mpsc/bounded.rs | 8 +++---- asyncband/src/{ => channel}/mpsc/mod.rs | 10 ++++---- asyncband/src/{ => channel}/mpsc/unbounded.rs | 6 ++--- asyncband/src/{ => channel}/oneshot/mod.rs | 0 .../src/{ => channel}/oneshot/receiver.rs | 18 +++++++------- asyncband/src/{ => channel}/oneshot/sender.rs | 16 ++++++------- asyncband/src/{ => channel}/oneshot/tests.rs | 0 asyncband/src/lib.rs | 5 ++-- 10 files changed, 56 insertions(+), 31 deletions(-) rename asyncband/src/{mpsc => channel}/error.rs (100%) create mode 100644 asyncband/src/channel/mod.rs rename asyncband/src/{ => channel}/mpsc/bounded.rs (98%) rename asyncband/src/{ => channel}/mpsc/mod.rs (88%) rename asyncband/src/{ => channel}/mpsc/unbounded.rs (99%) rename asyncband/src/{ => channel}/oneshot/mod.rs (100%) rename asyncband/src/{ => channel}/oneshot/receiver.rs (98%) rename asyncband/src/{ => channel}/oneshot/sender.rs (97%) rename asyncband/src/{ => channel}/oneshot/tests.rs (100%) diff --git a/asyncband/src/mpsc/error.rs b/asyncband/src/channel/error.rs similarity index 100% rename from asyncband/src/mpsc/error.rs rename to asyncband/src/channel/error.rs diff --git a/asyncband/src/channel/mod.rs b/asyncband/src/channel/mod.rs new file mode 100644 index 0000000..87b7b9a --- /dev/null +++ b/asyncband/src/channel/mod.rs @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#[cfg(feature = "mpsc")] +mod error; + +#[cfg(feature = "mpsc")] +pub mod mpsc; +#[cfg(feature = "oneshot")] +pub mod oneshot; diff --git a/asyncband/src/mpsc/bounded.rs b/asyncband/src/channel/mpsc/bounded.rs similarity index 98% rename from asyncband/src/mpsc/bounded.rs rename to asyncband/src/channel/mpsc/bounded.rs index 60f0da0..08aefd4 100644 --- a/asyncband/src/mpsc/bounded.rs +++ b/asyncband/src/channel/mpsc/bounded.rs @@ -28,13 +28,13 @@ use std::sync::atomic::Ordering; use std::task::Context; use std::task::Poll; +use super::RecvError; +use super::SendError; +use super::TryRecvError; +use super::TrySendError; use crate::internal::atomic_waker::AtomicWaker; use crate::internal::semaphore::Acquire; use crate::internal::semaphore::Semaphore; -use crate::mpsc::RecvError; -use crate::mpsc::SendError; -use crate::mpsc::TryRecvError; -use crate::mpsc::error::TrySendError; /// Creates a bounded mpsc channel for communicating between asynchronous /// tasks with backpressure. diff --git a/asyncband/src/mpsc/mod.rs b/asyncband/src/channel/mpsc/mod.rs similarity index 88% rename from asyncband/src/mpsc/mod.rs rename to asyncband/src/channel/mpsc/mod.rs index 87c7c8f..2134bfc 100644 --- a/asyncband/src/mpsc/mod.rs +++ b/asyncband/src/channel/mpsc/mod.rs @@ -18,16 +18,16 @@ //! A multi-producer, single-consumer queue for sending values between asynchronous tasks. mod bounded; -mod error; mod unbounded; pub use bounded::BoundedReceiver; pub use bounded::BoundedSender; pub use bounded::bounded; -pub use error::RecvError; -pub use error::SendError; -pub use error::TryRecvError; -pub use error::TrySendError; pub use unbounded::UnboundedReceiver; pub use unbounded::UnboundedSender; pub use unbounded::unbounded; + +pub use super::error::RecvError; +pub use super::error::SendError; +pub use super::error::TryRecvError; +pub use super::error::TrySendError; diff --git a/asyncband/src/mpsc/unbounded.rs b/asyncband/src/channel/mpsc/unbounded.rs similarity index 99% rename from asyncband/src/mpsc/unbounded.rs rename to asyncband/src/channel/mpsc/unbounded.rs index eb446be..3ee892a 100644 --- a/asyncband/src/mpsc/unbounded.rs +++ b/asyncband/src/channel/mpsc/unbounded.rs @@ -26,10 +26,10 @@ use std::sync::atomic::Ordering; use std::task::Context; use std::task::Poll; +use super::RecvError; +use super::SendError; +use super::TryRecvError; use crate::internal::atomic_waker::AtomicWaker; -use crate::mpsc::RecvError; -use crate::mpsc::SendError; -use crate::mpsc::TryRecvError; /// Creates an unbounded mpsc channel for communicating between asynchronous /// tasks without backpressure. diff --git a/asyncband/src/oneshot/mod.rs b/asyncband/src/channel/oneshot/mod.rs similarity index 100% rename from asyncband/src/oneshot/mod.rs rename to asyncband/src/channel/oneshot/mod.rs diff --git a/asyncband/src/oneshot/receiver.rs b/asyncband/src/channel/oneshot/receiver.rs similarity index 98% rename from asyncband/src/oneshot/receiver.rs rename to asyncband/src/channel/oneshot/receiver.rs index 29f913b..7d232dc 100644 --- a/asyncband/src/oneshot/receiver.rs +++ b/asyncband/src/channel/oneshot/receiver.rs @@ -24,16 +24,16 @@ use std::sync::atomic::fence; use std::task::Context; use std::task::Poll; -use crate::oneshot::AWAKING; -use crate::oneshot::Channel; -use crate::oneshot::DISCONNECTED; -use crate::oneshot::EMPTY; -use crate::oneshot::MESSAGE; -use crate::oneshot::RECEIVING; +use super::AWAKING; +use super::Channel; +use super::DISCONNECTED; +use super::EMPTY; +use super::MESSAGE; +use super::RECEIVING; #[cfg(doc)] -use crate::oneshot::Sender; -use crate::oneshot::deallocate_empty_channel; -use crate::oneshot::drop_message_and_deallocate_channel; +use super::Sender; +use super::deallocate_empty_channel; +use super::drop_message_and_deallocate_channel; /// Receives a value from the associated [`Sender`]. pub struct Receiver { diff --git a/asyncband/src/oneshot/sender.rs b/asyncband/src/channel/oneshot/sender.rs similarity index 97% rename from asyncband/src/oneshot/sender.rs rename to asyncband/src/channel/oneshot/sender.rs index adf45ee..58a712c 100644 --- a/asyncband/src/oneshot/sender.rs +++ b/asyncband/src/channel/oneshot/sender.rs @@ -22,15 +22,15 @@ use std::ptr::NonNull; use std::sync::atomic::Ordering; use std::sync::atomic::fence; -use crate::oneshot::Channel; -use crate::oneshot::DISCONNECTED; -use crate::oneshot::EMPTY; -use crate::oneshot::MESSAGE; -use crate::oneshot::RECEIVING; +use super::Channel; +use super::DISCONNECTED; +use super::EMPTY; +use super::MESSAGE; +use super::RECEIVING; #[cfg(doc)] -use crate::oneshot::Receiver; -use crate::oneshot::deallocate_empty_channel; -use crate::oneshot::drop_message_and_deallocate_channel; +use super::Receiver; +use super::deallocate_empty_channel; +use super::drop_message_and_deallocate_channel; /// Sends a value to the associated [`Receiver`]. pub struct Sender { diff --git a/asyncband/src/oneshot/tests.rs b/asyncband/src/channel/oneshot/tests.rs similarity index 100% rename from asyncband/src/oneshot/tests.rs rename to asyncband/src/channel/oneshot/tests.rs diff --git a/asyncband/src/lib.rs b/asyncband/src/lib.rs index 41a52d8..733275a 100644 --- a/asyncband/src/lib.rs +++ b/asyncband/src/lib.rs @@ -100,6 +100,7 @@ //! //! While incubation status is not necessarily a reflection of the completeness or stability of the //! code, it does indicate that the project has yet to be fully endorsed by the ASF. +mod channel; mod internal; #[cfg(feature = "barrier")] @@ -111,7 +112,7 @@ pub mod condvar; #[cfg(feature = "latch")] pub mod latch; #[cfg(feature = "mpsc")] -pub mod mpsc; +pub use self::channel::mpsc; #[cfg(feature = "mutex")] pub mod mutex; #[cfg(any( @@ -122,7 +123,7 @@ pub mod mutex; ))] pub mod once; #[cfg(feature = "oneshot")] -pub mod oneshot; +pub use self::channel::oneshot; #[cfg(feature = "pool")] pub mod pool; #[cfg(feature = "rwlock")] From 706b57ae46e1f9d2776f1b5de997a788be9a0d02 Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 27 Aug 2026 00:33:10 +0800 Subject: [PATCH 2/5] refactor(mpsc): keep errors local --- asyncband/src/channel/mod.rs | 3 --- asyncband/src/channel/{ => mpsc}/error.rs | 0 asyncband/src/channel/mpsc/mod.rs | 10 +++++----- 3 files changed, 5 insertions(+), 8 deletions(-) rename asyncband/src/channel/{ => mpsc}/error.rs (100%) diff --git a/asyncband/src/channel/mod.rs b/asyncband/src/channel/mod.rs index 87b7b9a..58689a6 100644 --- a/asyncband/src/channel/mod.rs +++ b/asyncband/src/channel/mod.rs @@ -15,9 +15,6 @@ // specific language governing permissions and limitations // under the License. -#[cfg(feature = "mpsc")] -mod error; - #[cfg(feature = "mpsc")] pub mod mpsc; #[cfg(feature = "oneshot")] diff --git a/asyncband/src/channel/error.rs b/asyncband/src/channel/mpsc/error.rs similarity index 100% rename from asyncband/src/channel/error.rs rename to asyncband/src/channel/mpsc/error.rs diff --git a/asyncband/src/channel/mpsc/mod.rs b/asyncband/src/channel/mpsc/mod.rs index 2134bfc..87c7c8f 100644 --- a/asyncband/src/channel/mpsc/mod.rs +++ b/asyncband/src/channel/mpsc/mod.rs @@ -18,16 +18,16 @@ //! A multi-producer, single-consumer queue for sending values between asynchronous tasks. mod bounded; +mod error; mod unbounded; pub use bounded::BoundedReceiver; pub use bounded::BoundedSender; pub use bounded::bounded; +pub use error::RecvError; +pub use error::SendError; +pub use error::TryRecvError; +pub use error::TrySendError; pub use unbounded::UnboundedReceiver; pub use unbounded::UnboundedSender; pub use unbounded::unbounded; - -pub use super::error::RecvError; -pub use super::error::SendError; -pub use super::error::TryRecvError; -pub use super::error::TrySendError; From 35b5975ead2565adb8e4223277a99dde372b3d7c Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 27 Aug 2026 00:43:27 +0800 Subject: [PATCH 3/5] test(channel): add ecosystem performance baselines --- Cargo.lock | 83 +++++++ Cargo.toml | 2 + benchmarks/Cargo.toml | 9 + benchmarks/README.md | 58 +++++ benchmarks/ecosystem/main.rs | 26 ++ benchmarks/ecosystem/mpsc.rs | 467 +++++++++++++++++++++++++++++++++++ 6 files changed, 645 insertions(+) create mode 100644 benchmarks/README.md create mode 100644 benchmarks/ecosystem/main.rs create mode 100644 benchmarks/ecosystem/mpsc.rs diff --git a/Cargo.lock b/Cargo.lock index 4a150d3..0c320cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "asyncband" version = "0.6.7" @@ -70,8 +82,12 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" name = "benchmarks" version = "0.0.0" dependencies = [ + "async-channel", "asyncband", "divan", + "flume", + "pollster", + "tokio", ] [[package]] @@ -181,6 +197,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "condtype" version = "1.3.0" @@ -216,6 +241,12 @@ dependencies = [ "url", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + [[package]] name = "deranged" version = "0.5.8" @@ -283,6 +314,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "examples" version = "0.0.0" @@ -297,6 +348,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -312,6 +374,12 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + [[package]] name = "getrandom" version = "0.2.17" @@ -550,6 +618,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -806,6 +880,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 2cc043c..872eae1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,9 +35,11 @@ asyncband = { path = "asyncband" } hashbrown = { version = "0.17.1", default-features = false } # Dev dependencies +async-channel = { version = "2.5.0" } cargo_metadata = { version = "0.23.1" } clap = { version = "4.6.5" } divan = { version = "0.1.21" } +flume = { version = "0.12.0", default-features = false } pollster = { version = "1.0.1" } semver = { version = "1.0.28" } serde = { version = "1.0.229" } diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index 6905629..0a90b54 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -23,6 +23,7 @@ edition.workspace = true rust-version.workspace = true [dev-dependencies] +async-channel = { workspace = true } asyncband = { workspace = true, features = [ "barrier", "blocking", @@ -42,11 +43,19 @@ asyncband = { workspace = true, features = [ "waitgroup", ] } divan = { workspace = true } +flume = { workspace = true, features = ["async"] } +pollster = { workspace = true } +tokio = { workspace = true, features = ["sync"] } [[bench]] harness = false name = "benchmarks" path = "main.rs" +[[bench]] +harness = false +name = "ecosystem" +path = "ecosystem/main.rs" + [lints] workspace = true diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 0000000..3de84fd --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,58 @@ + + +# Benchmarks + +`benchmarks` measures Asyncband primitives in isolation. The `ecosystem` target compares channel operations with semantically similar Rust channels so a new implementation does not hide a large performance regression behind API differences. + +## Running + +Run the repository benchmark workflow, including the ecosystem target: + +```shell +cargo x bench +``` + +For a shorter development loop, select the companion target and optionally a Divan name filter: + +```shell +cargo bench -p benchmarks --bench ecosystem +cargo bench -p benchmarks --bench ecosystem -- mpsc::bounded_concurrent +``` + +Use a release build on an otherwise idle machine, record the CPU and operating system, and compare implementations in the same invocation. Absolute results from different machines are not directly comparable. + +## Channel methodology + +The current suite covers the MPSC API that Asyncband exposes today: + +- bounded capacity is 64 messages; +- ready-path cases reuse an empty channel and measure one send/receive round trip; +- concurrent cases move 16,384 messages from 1, 2, 4, or 8 producer threads to one consumer; +- channel construction, thread spawning, and thread joining stay outside the timed section; +- async operations are driven by a minimal standards-based executor or a benchmark waker, so no peer gets a dedicated runtime; +- every implementation receives the same `usize` values and the consumer computes a checksum to keep the work observable. + +The peer set is Asyncband from the current checkout, Tokio 1.53.1, async-channel 2.5.0, and flume 0.12.0. `Cargo.lock` records the exact resolved versions. Tokio has the same MPSC topology; async-channel and flume are MPMC implementations measured with one receiver, so their extra receiver capability is a documented semantic difference. async-channel exposes unbounded `send` as a future, so the unbounded cases use its non-waiting `try_send` path to match the other implementations' immediate sends. Benchmark-only peers are dev dependencies of the `benchmarks` package and do not become runtime dependencies of `asyncband`. + +The suite is a regression signal rather than a fastest-wins contest. Investigate a sustained result above 3x the closest semantic peer. Treat an order-of-magnitude gap as blocking unless a documented semantic or resource tradeoff explains it. + +## Extending the matrix + +Add comparable cases when Asyncband exposes another topology; do not publish peer-only rows as an Asyncband baseline. SPMC and MPMC queue work should add competing-consumer and balanced producer/consumer cases. Broadcast work should add fanout and producer-contention cases while documenting delivery, overwrite, and lag semantics. Watch work should compare latest-state notification rather than queue throughput. diff --git a/benchmarks/ecosystem/main.rs b/benchmarks/ecosystem/main.rs new file mode 100644 index 0000000..e5cb0fc --- /dev/null +++ b/benchmarks/ecosystem/main.rs @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod mpsc; + +#[allow(dead_code)] +#[path = "../support.rs"] +mod support; + +fn main() { + divan::main(); +} diff --git a/benchmarks/ecosystem/mpsc.rs b/benchmarks/ecosystem/mpsc.rs new file mode 100644 index 0000000..f3319ff --- /dev/null +++ b/benchmarks/ecosystem/mpsc.rs @@ -0,0 +1,467 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use std::marker::PhantomData; +use std::sync::Arc; +use std::sync::Barrier; +use std::task::Context; +use std::thread; +use std::thread::JoinHandle; + +use divan::Bencher; +use divan::black_box; +use divan::counter::ItemsCount; + +use super::support::bench_context; +use super::support::poll_ready; + +const BOUNDED_CAPACITY: usize = 64; +const BATCH_MESSAGES: usize = 16_384; +const PRODUCER_COUNTS: &[usize] = &[1, 2, 4, 8]; + +struct Asyncband; +struct Tokio; +struct AsyncChannel; +struct Flume; + +trait BoundedMpsc: Send + Sync + 'static { + type Sender: Clone + Send + 'static; + type Receiver: Send + 'static; + + fn channel(capacity: usize) -> (Self::Sender, Self::Receiver); + fn try_send(sender: &Self::Sender, value: usize); + fn try_recv(receiver: &mut Self::Receiver) -> usize; + fn send_ready(sender: &Self::Sender, value: usize, context: &mut Context<'_>); + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize; + fn send_blocking(sender: &Self::Sender, value: usize); + fn recv_blocking(receiver: &mut Self::Receiver) -> usize; +} + +trait UnboundedMpsc: Send + Sync + 'static { + type Sender: Clone + Send + 'static; + type Receiver: Send + 'static; + + fn channel() -> (Self::Sender, Self::Receiver); + fn send(sender: &Self::Sender, value: usize); + fn try_recv(receiver: &mut Self::Receiver) -> usize; + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize; + fn recv_blocking(receiver: &mut Self::Receiver) -> usize; +} + +impl BoundedMpsc for Asyncband { + type Receiver = asyncband::mpsc::BoundedReceiver; + type Sender = asyncband::mpsc::BoundedSender; + + fn channel(capacity: usize) -> (Self::Sender, Self::Receiver) { + asyncband::mpsc::bounded(capacity) + } + + fn try_send(sender: &Self::Sender, value: usize) { + sender.try_send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn send_ready(sender: &Self::Sender, value: usize, context: &mut Context<'_>) { + poll_ready(sender.send(value), context).unwrap(); + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv(), context).unwrap() + } + + fn send_blocking(sender: &Self::Sender, value: usize) { + pollster::block_on(sender.send(value)).unwrap(); + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv()).unwrap() + } +} + +impl BoundedMpsc for Tokio { + type Receiver = tokio::sync::mpsc::Receiver; + type Sender = tokio::sync::mpsc::Sender; + + fn channel(capacity: usize) -> (Self::Sender, Self::Receiver) { + tokio::sync::mpsc::channel(capacity) + } + + fn try_send(sender: &Self::Sender, value: usize) { + sender.try_send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn send_ready(sender: &Self::Sender, value: usize, context: &mut Context<'_>) { + poll_ready(sender.send(value), context).unwrap(); + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv(), context).unwrap() + } + + fn send_blocking(sender: &Self::Sender, value: usize) { + pollster::block_on(sender.send(value)).unwrap(); + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv()).unwrap() + } +} + +impl BoundedMpsc for AsyncChannel { + type Receiver = async_channel::Receiver; + type Sender = async_channel::Sender; + + fn channel(capacity: usize) -> (Self::Sender, Self::Receiver) { + async_channel::bounded(capacity) + } + + fn try_send(sender: &Self::Sender, value: usize) { + sender.try_send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn send_ready(sender: &Self::Sender, value: usize, context: &mut Context<'_>) { + poll_ready(sender.send(value), context).unwrap(); + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv(), context).unwrap() + } + + fn send_blocking(sender: &Self::Sender, value: usize) { + pollster::block_on(sender.send(value)).unwrap(); + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv()).unwrap() + } +} + +impl BoundedMpsc for Flume { + type Receiver = flume::Receiver; + type Sender = flume::Sender; + + fn channel(capacity: usize) -> (Self::Sender, Self::Receiver) { + flume::bounded(capacity) + } + + fn try_send(sender: &Self::Sender, value: usize) { + sender.try_send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn send_ready(sender: &Self::Sender, value: usize, context: &mut Context<'_>) { + poll_ready(sender.send_async(value), context).unwrap(); + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv_async(), context).unwrap() + } + + fn send_blocking(sender: &Self::Sender, value: usize) { + pollster::block_on(sender.send_async(value)).unwrap(); + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv_async()).unwrap() + } +} + +impl UnboundedMpsc for Asyncband { + type Receiver = asyncband::mpsc::UnboundedReceiver; + type Sender = asyncband::mpsc::UnboundedSender; + + fn channel() -> (Self::Sender, Self::Receiver) { + asyncband::mpsc::unbounded() + } + + fn send(sender: &Self::Sender, value: usize) { + sender.send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv(), context).unwrap() + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv()).unwrap() + } +} + +impl UnboundedMpsc for Tokio { + type Receiver = tokio::sync::mpsc::UnboundedReceiver; + type Sender = tokio::sync::mpsc::UnboundedSender; + + fn channel() -> (Self::Sender, Self::Receiver) { + tokio::sync::mpsc::unbounded_channel() + } + + fn send(sender: &Self::Sender, value: usize) { + sender.send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv(), context).unwrap() + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv()).unwrap() + } +} + +impl UnboundedMpsc for AsyncChannel { + type Receiver = async_channel::Receiver; + type Sender = async_channel::Sender; + + fn channel() -> (Self::Sender, Self::Receiver) { + async_channel::unbounded() + } + + fn send(sender: &Self::Sender, value: usize) { + sender.try_send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv(), context).unwrap() + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv()).unwrap() + } +} + +impl UnboundedMpsc for Flume { + type Receiver = flume::Receiver; + type Sender = flume::Sender; + + fn channel() -> (Self::Sender, Self::Receiver) { + flume::unbounded() + } + + fn send(sender: &Self::Sender, value: usize) { + sender.send(value).unwrap(); + } + + fn try_recv(receiver: &mut Self::Receiver) -> usize { + receiver.try_recv().unwrap() + } + + fn recv_ready(receiver: &mut Self::Receiver, context: &mut Context<'_>) -> usize { + poll_ready(receiver.recv_async(), context).unwrap() + } + + fn recv_blocking(receiver: &mut Self::Receiver) -> usize { + pollster::block_on(receiver.recv_async()).unwrap() + } +} + +trait ConcurrentMpsc: Send + Sync + 'static { + type Sender: Clone + Send + 'static; + type Receiver: Send + 'static; + + fn channel() -> (Self::Sender, Self::Receiver); + fn send(sender: &Self::Sender, value: usize); + fn recv(receiver: &mut Self::Receiver) -> usize; +} + +struct Bounded(PhantomData); + +impl ConcurrentMpsc for Bounded { + type Receiver = C::Receiver; + type Sender = C::Sender; + + fn channel() -> (Self::Sender, Self::Receiver) { + C::channel(BOUNDED_CAPACITY) + } + + fn send(sender: &Self::Sender, value: usize) { + C::send_blocking(sender, value); + } + + fn recv(receiver: &mut Self::Receiver) -> usize { + C::recv_blocking(receiver) + } +} + +struct Unbounded(PhantomData); + +impl ConcurrentMpsc for Unbounded { + type Receiver = C::Receiver; + type Sender = C::Sender; + + fn channel() -> (Self::Sender, Self::Receiver) { + C::channel() + } + + fn send(sender: &Self::Sender, value: usize) { + C::send(sender, value); + } + + fn recv(receiver: &mut Self::Receiver) -> usize { + C::recv_blocking(receiver) + } +} + +struct ConcurrentBatch { + receiver: C::Receiver, + start: Arc, + workers: Vec>, +} + +impl ConcurrentBatch { + fn new(producer_count: usize) -> Self { + assert_eq!(BATCH_MESSAGES % producer_count, 0); + + let (sender, receiver) = C::channel(); + let start = Arc::new(Barrier::new(producer_count + 1)); + let messages_per_producer = BATCH_MESSAGES / producer_count; + let workers = (0..producer_count) + .map(|producer| { + let sender = sender.clone(); + let start = start.clone(); + thread::spawn(move || { + start.wait(); + let first = producer * messages_per_producer; + for offset in 0..messages_per_producer { + C::send(&sender, black_box(first + offset)); + } + }) + }) + .collect(); + drop(sender); + + Self { + receiver, + start, + workers, + } + } + + fn run(&mut self) -> usize { + self.start.wait(); + let mut checksum = 0usize; + for _ in 0..BATCH_MESSAGES { + checksum = checksum.wrapping_add(C::recv(&mut self.receiver)); + } + black_box(checksum) + } +} + +impl Drop for ConcurrentBatch { + fn drop(&mut self) { + let panicking = thread::panicking(); + for worker in self.workers.drain(..) { + let result = worker.join(); + if !panicking { + result.expect("benchmark producer panicked"); + } + } + } +} + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn bounded_try_round_trip(bencher: Bencher) { + let (sender, mut receiver) = C::channel(BOUNDED_CAPACITY); + + bencher.bench_local(|| { + C::try_send(&sender, black_box(usize::MAX)); + black_box(C::try_recv(&mut receiver)) + }); +} + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn bounded_ready_round_trip(bencher: Bencher) { + let mut context = bench_context(); + let (sender, mut receiver) = C::channel(BOUNDED_CAPACITY); + + bencher.bench_local(|| { + C::send_ready(&sender, black_box(usize::MAX), &mut context); + black_box(C::recv_ready(&mut receiver, &mut context)) + }); +} + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn unbounded_ready_round_trip(bencher: Bencher) { + let mut context = bench_context(); + let (sender, mut receiver) = C::channel(); + + bencher.bench_local(|| { + C::send(&sender, black_box(usize::MAX)); + black_box(C::recv_ready(&mut receiver, &mut context)) + }); +} + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn unbounded_try_round_trip(bencher: Bencher) { + let (sender, mut receiver) = C::channel(); + + bencher.bench_local(|| { + C::send(&sender, black_box(usize::MAX)); + black_box(C::try_recv(&mut receiver)) + }); +} + +#[divan::bench( + types = [Asyncband, Tokio, AsyncChannel, Flume], + args = PRODUCER_COUNTS, + sample_count = 20, + sample_size = 1, + counter = ItemsCount::new(BATCH_MESSAGES), +)] +fn bounded_concurrent(bencher: Bencher, producer_count: usize) { + bencher + .with_inputs(|| ConcurrentBatch::>::new(producer_count)) + .bench_local_refs(|batch| batch.run()); +} + +#[divan::bench( + types = [Asyncband, Tokio, AsyncChannel, Flume], + args = PRODUCER_COUNTS, + sample_count = 20, + sample_size = 1, + counter = ItemsCount::new(BATCH_MESSAGES), +)] +fn unbounded_concurrent(bencher: Bencher, producer_count: usize) { + bencher + .with_inputs(|| ConcurrentBatch::>::new(producer_count)) + .bench_local_refs(|batch| batch.run()); +} From 0abd46125d2e17c77cf357a177ac6ceb79d56862 Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 27 Aug 2026 00:59:01 +0800 Subject: [PATCH 4/5] refactor(benchmarks): organize by primitive and operation --- benchmarks/README.md | 4 +- benchmarks/barrier/mod.rs | 18 ++ benchmarks/{barrier.rs => barrier/wait.rs} | 8 +- .../{blocking.rs => blocking/block_on.rs} | 10 - benchmarks/blocking/mod.rs | 19 ++ benchmarks/blocking/wait_timeout.rs | 31 +++ benchmarks/condvar/mod.rs | 18 ++ benchmarks/{condvar.rs => condvar/wait.rs} | 8 +- .../ecosystem/{mpsc.rs => mpsc/adapters.rs} | 200 +----------------- benchmarks/ecosystem/mpsc/bounded.rs | 66 ++++++ benchmarks/ecosystem/mpsc/mod.rs | 21 ++ benchmarks/ecosystem/mpsc/support.rs | 135 ++++++++++++ benchmarks/ecosystem/mpsc/unbounded.rs | 65 ++++++ benchmarks/latch/mod.rs | 18 ++ benchmarks/{latch.rs => latch/wait.rs} | 6 +- benchmarks/{mpsc.rs => mpsc/bounded.rs} | 29 +-- benchmarks/mpsc/mod.rs | 19 ++ benchmarks/mpsc/unbounded.rs | 47 ++++ benchmarks/{mutex.rs => mutex/lock.rs} | 8 +- benchmarks/mutex/mod.rs | 18 ++ benchmarks/once/get_or_init.rs | 75 +++++++ benchmarks/once/mod.rs | 19 ++ benchmarks/{once.rs => once/wait.rs} | 56 +---- .../{once_map.rs => once_map/compute.rs} | 48 ++--- benchmarks/once_map/get.rs | 43 ++++ benchmarks/once_map/mod.rs | 20 ++ benchmarks/once_map/support.rs | 28 +++ benchmarks/oneshot/mod.rs | 18 ++ benchmarks/{oneshot.rs => oneshot/send.rs} | 6 +- benchmarks/{pool.rs => pool/bounded.rs} | 21 +- benchmarks/pool/mod.rs | 19 ++ benchmarks/pool/unbounded.rs | 32 +++ benchmarks/rwlock/mod.rs | 19 ++ benchmarks/rwlock/read.rs | 41 ++++ benchmarks/{rwlock.rs => rwlock/write.rs} | 26 +-- .../{semaphore.rs => semaphore/acquire.rs} | 32 +-- benchmarks/semaphore/mod.rs | 19 ++ benchmarks/semaphore/release.rs | 44 ++++ benchmarks/shutdown/mod.rs | 18 ++ .../request_shutdown.rs} | 8 +- benchmarks/singleflight/mod.rs | 18 ++ .../{singleflight.rs => singleflight/work.rs} | 18 +- benchmarks/waitgroup/mod.rs | 18 ++ .../{waitgroup.rs => waitgroup/wait.rs} | 6 +- 44 files changed, 981 insertions(+), 419 deletions(-) create mode 100644 benchmarks/barrier/mod.rs rename benchmarks/{barrier.rs => barrier/wait.rs} (94%) rename benchmarks/{blocking.rs => blocking/block_on.rs} (90%) create mode 100644 benchmarks/blocking/mod.rs create mode 100644 benchmarks/blocking/wait_timeout.rs create mode 100644 benchmarks/condvar/mod.rs rename benchmarks/{condvar.rs => condvar/wait.rs} (95%) rename benchmarks/ecosystem/{mpsc.rs => mpsc/adapters.rs} (60%) create mode 100644 benchmarks/ecosystem/mpsc/bounded.rs create mode 100644 benchmarks/ecosystem/mpsc/mod.rs create mode 100644 benchmarks/ecosystem/mpsc/support.rs create mode 100644 benchmarks/ecosystem/mpsc/unbounded.rs create mode 100644 benchmarks/latch/mod.rs rename benchmarks/{latch.rs => latch/wait.rs} (96%) rename benchmarks/{mpsc.rs => mpsc/bounded.rs} (74%) create mode 100644 benchmarks/mpsc/mod.rs create mode 100644 benchmarks/mpsc/unbounded.rs rename benchmarks/{mutex.rs => mutex/lock.rs} (93%) create mode 100644 benchmarks/mutex/mod.rs create mode 100644 benchmarks/once/get_or_init.rs create mode 100644 benchmarks/once/mod.rs rename benchmarks/{once.rs => once/wait.rs} (59%) rename benchmarks/{once_map.rs => once_map/compute.rs} (82%) create mode 100644 benchmarks/once_map/get.rs create mode 100644 benchmarks/once_map/mod.rs create mode 100644 benchmarks/once_map/support.rs create mode 100644 benchmarks/oneshot/mod.rs rename benchmarks/{oneshot.rs => oneshot/send.rs} (92%) rename benchmarks/{pool.rs => pool/bounded.rs} (86%) create mode 100644 benchmarks/pool/mod.rs create mode 100644 benchmarks/pool/unbounded.rs create mode 100644 benchmarks/rwlock/mod.rs create mode 100644 benchmarks/rwlock/read.rs rename benchmarks/{rwlock.rs => rwlock/write.rs} (71%) rename benchmarks/{semaphore.rs => semaphore/acquire.rs} (85%) create mode 100644 benchmarks/semaphore/mod.rs create mode 100644 benchmarks/semaphore/release.rs create mode 100644 benchmarks/shutdown/mod.rs rename benchmarks/{shutdown.rs => shutdown/request_shutdown.rs} (92%) create mode 100644 benchmarks/singleflight/mod.rs rename benchmarks/{singleflight.rs => singleflight/work.rs} (92%) create mode 100644 benchmarks/waitgroup/mod.rs rename benchmarks/{waitgroup.rs => waitgroup/wait.rs} (96%) diff --git a/benchmarks/README.md b/benchmarks/README.md index 3de84fd..e51f96e 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -21,6 +21,8 @@ `benchmarks` measures Asyncband primitives in isolation. The `ecosystem` target compares channel operations with semantically similar Rust channels so a new implementation does not hide a large performance regression behind API differences. +Source files use a primitive/operation layout such as `semaphore/acquire.rs` and `once_map/compute.rs`. Shared setup stays in the nearest `support.rs`. Ecosystem comparisons remain a separate target under `ecosystem/` and follow the same layout within each compared primitive. + ## Running Run the repository benchmark workflow, including the ecosystem target: @@ -33,7 +35,7 @@ For a shorter development loop, select the companion target and optionally a Div ```shell cargo bench -p benchmarks --bench ecosystem -cargo bench -p benchmarks --bench ecosystem -- mpsc::bounded_concurrent +cargo bench -p benchmarks --bench ecosystem -- mpsc::bounded::concurrent ``` Use a release build on an otherwise idle machine, record the CPU and operating system, and compare implementations in the same invocation. Absolute results from different machines are not directly comparable. diff --git a/benchmarks/barrier/mod.rs b/benchmarks/barrier/mod.rs new file mode 100644 index 0000000..3554069 --- /dev/null +++ b/benchmarks/barrier/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod wait; diff --git a/benchmarks/barrier.rs b/benchmarks/barrier/wait.rs similarity index 94% rename from benchmarks/barrier.rs rename to benchmarks/barrier/wait.rs index 46296a5..5433f2a 100644 --- a/benchmarks/barrier.rs +++ b/benchmarks/barrier/wait.rs @@ -21,10 +21,10 @@ use asyncband::barrier::Barrier; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const PARTICIPANT_COUNTS: &[usize] = &[1, 8, 32]; diff --git a/benchmarks/blocking.rs b/benchmarks/blocking/block_on.rs similarity index 90% rename from benchmarks/blocking.rs rename to benchmarks/blocking/block_on.rs index 7feb810..d7524bb 100644 --- a/benchmarks/blocking.rs +++ b/benchmarks/blocking/block_on.rs @@ -20,7 +20,6 @@ use std::sync::mpsc; use std::task::Poll; use std::task::Waker; use std::thread; -use std::time::Duration; use asyncband::blocking::FutureExt as _; use divan::Bencher; @@ -31,15 +30,6 @@ fn ready(bencher: Bencher) { bencher.bench_local(|| async { black_box(42usize) }.block_on()); } -#[divan::bench] -fn ready_with_timeout(bencher: Bencher) { - bencher.bench_local(|| { - async { black_box(42usize) } - .wait_timeout(Duration::ZERO) - .unwrap() - }); -} - #[divan::bench] fn self_wake(bencher: Bencher) { bencher.bench_local(|| { diff --git a/benchmarks/blocking/mod.rs b/benchmarks/blocking/mod.rs new file mode 100644 index 0000000..3961981 --- /dev/null +++ b/benchmarks/blocking/mod.rs @@ -0,0 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod block_on; +mod wait_timeout; diff --git a/benchmarks/blocking/wait_timeout.rs b/benchmarks/blocking/wait_timeout.rs new file mode 100644 index 0000000..1cd1efd --- /dev/null +++ b/benchmarks/blocking/wait_timeout.rs @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use std::time::Duration; + +use asyncband::blocking::FutureExt as _; +use divan::Bencher; +use divan::black_box; + +#[divan::bench] +fn ready_with_timeout(bencher: Bencher) { + bencher.bench_local(|| { + async { black_box(42usize) } + .wait_timeout(Duration::ZERO) + .unwrap() + }); +} diff --git a/benchmarks/condvar/mod.rs b/benchmarks/condvar/mod.rs new file mode 100644 index 0000000..3554069 --- /dev/null +++ b/benchmarks/condvar/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod wait; diff --git a/benchmarks/condvar.rs b/benchmarks/condvar/wait.rs similarity index 95% rename from benchmarks/condvar.rs rename to benchmarks/condvar/wait.rs index 1072a40..5f7d89c 100644 --- a/benchmarks/condvar.rs +++ b/benchmarks/condvar/wait.rs @@ -22,10 +22,10 @@ use asyncband::mutex::Mutex; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const WAITER_COUNTS: &[usize] = &[1, 8, 32]; diff --git a/benchmarks/ecosystem/mpsc.rs b/benchmarks/ecosystem/mpsc/adapters.rs similarity index 60% rename from benchmarks/ecosystem/mpsc.rs rename to benchmarks/ecosystem/mpsc/adapters.rs index f3319ff..7e78c21 100644 --- a/benchmarks/ecosystem/mpsc.rs +++ b/benchmarks/ecosystem/mpsc/adapters.rs @@ -15,30 +15,16 @@ // specific language governing permissions and limitations // under the License. -use std::marker::PhantomData; -use std::sync::Arc; -use std::sync::Barrier; use std::task::Context; -use std::thread; -use std::thread::JoinHandle; -use divan::Bencher; -use divan::black_box; -use divan::counter::ItemsCount; +use crate::support::poll_ready; -use super::support::bench_context; -use super::support::poll_ready; +pub struct Asyncband; +pub struct Tokio; +pub struct AsyncChannel; +pub struct Flume; -const BOUNDED_CAPACITY: usize = 64; -const BATCH_MESSAGES: usize = 16_384; -const PRODUCER_COUNTS: &[usize] = &[1, 2, 4, 8]; - -struct Asyncband; -struct Tokio; -struct AsyncChannel; -struct Flume; - -trait BoundedMpsc: Send + Sync + 'static { +pub trait BoundedMpsc: Send + Sync + 'static { type Sender: Clone + Send + 'static; type Receiver: Send + 'static; @@ -51,7 +37,7 @@ trait BoundedMpsc: Send + Sync + 'static { fn recv_blocking(receiver: &mut Self::Receiver) -> usize; } -trait UnboundedMpsc: Send + Sync + 'static { +pub trait UnboundedMpsc: Send + Sync + 'static { type Sender: Clone + Send + 'static; type Receiver: Send + 'static; @@ -293,175 +279,3 @@ impl UnboundedMpsc for Flume { pollster::block_on(receiver.recv_async()).unwrap() } } - -trait ConcurrentMpsc: Send + Sync + 'static { - type Sender: Clone + Send + 'static; - type Receiver: Send + 'static; - - fn channel() -> (Self::Sender, Self::Receiver); - fn send(sender: &Self::Sender, value: usize); - fn recv(receiver: &mut Self::Receiver) -> usize; -} - -struct Bounded(PhantomData); - -impl ConcurrentMpsc for Bounded { - type Receiver = C::Receiver; - type Sender = C::Sender; - - fn channel() -> (Self::Sender, Self::Receiver) { - C::channel(BOUNDED_CAPACITY) - } - - fn send(sender: &Self::Sender, value: usize) { - C::send_blocking(sender, value); - } - - fn recv(receiver: &mut Self::Receiver) -> usize { - C::recv_blocking(receiver) - } -} - -struct Unbounded(PhantomData); - -impl ConcurrentMpsc for Unbounded { - type Receiver = C::Receiver; - type Sender = C::Sender; - - fn channel() -> (Self::Sender, Self::Receiver) { - C::channel() - } - - fn send(sender: &Self::Sender, value: usize) { - C::send(sender, value); - } - - fn recv(receiver: &mut Self::Receiver) -> usize { - C::recv_blocking(receiver) - } -} - -struct ConcurrentBatch { - receiver: C::Receiver, - start: Arc, - workers: Vec>, -} - -impl ConcurrentBatch { - fn new(producer_count: usize) -> Self { - assert_eq!(BATCH_MESSAGES % producer_count, 0); - - let (sender, receiver) = C::channel(); - let start = Arc::new(Barrier::new(producer_count + 1)); - let messages_per_producer = BATCH_MESSAGES / producer_count; - let workers = (0..producer_count) - .map(|producer| { - let sender = sender.clone(); - let start = start.clone(); - thread::spawn(move || { - start.wait(); - let first = producer * messages_per_producer; - for offset in 0..messages_per_producer { - C::send(&sender, black_box(first + offset)); - } - }) - }) - .collect(); - drop(sender); - - Self { - receiver, - start, - workers, - } - } - - fn run(&mut self) -> usize { - self.start.wait(); - let mut checksum = 0usize; - for _ in 0..BATCH_MESSAGES { - checksum = checksum.wrapping_add(C::recv(&mut self.receiver)); - } - black_box(checksum) - } -} - -impl Drop for ConcurrentBatch { - fn drop(&mut self) { - let panicking = thread::panicking(); - for worker in self.workers.drain(..) { - let result = worker.join(); - if !panicking { - result.expect("benchmark producer panicked"); - } - } - } -} - -#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] -fn bounded_try_round_trip(bencher: Bencher) { - let (sender, mut receiver) = C::channel(BOUNDED_CAPACITY); - - bencher.bench_local(|| { - C::try_send(&sender, black_box(usize::MAX)); - black_box(C::try_recv(&mut receiver)) - }); -} - -#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] -fn bounded_ready_round_trip(bencher: Bencher) { - let mut context = bench_context(); - let (sender, mut receiver) = C::channel(BOUNDED_CAPACITY); - - bencher.bench_local(|| { - C::send_ready(&sender, black_box(usize::MAX), &mut context); - black_box(C::recv_ready(&mut receiver, &mut context)) - }); -} - -#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] -fn unbounded_ready_round_trip(bencher: Bencher) { - let mut context = bench_context(); - let (sender, mut receiver) = C::channel(); - - bencher.bench_local(|| { - C::send(&sender, black_box(usize::MAX)); - black_box(C::recv_ready(&mut receiver, &mut context)) - }); -} - -#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] -fn unbounded_try_round_trip(bencher: Bencher) { - let (sender, mut receiver) = C::channel(); - - bencher.bench_local(|| { - C::send(&sender, black_box(usize::MAX)); - black_box(C::try_recv(&mut receiver)) - }); -} - -#[divan::bench( - types = [Asyncband, Tokio, AsyncChannel, Flume], - args = PRODUCER_COUNTS, - sample_count = 20, - sample_size = 1, - counter = ItemsCount::new(BATCH_MESSAGES), -)] -fn bounded_concurrent(bencher: Bencher, producer_count: usize) { - bencher - .with_inputs(|| ConcurrentBatch::>::new(producer_count)) - .bench_local_refs(|batch| batch.run()); -} - -#[divan::bench( - types = [Asyncband, Tokio, AsyncChannel, Flume], - args = PRODUCER_COUNTS, - sample_count = 20, - sample_size = 1, - counter = ItemsCount::new(BATCH_MESSAGES), -)] -fn unbounded_concurrent(bencher: Bencher, producer_count: usize) { - bencher - .with_inputs(|| ConcurrentBatch::>::new(producer_count)) - .bench_local_refs(|batch| batch.run()); -} diff --git a/benchmarks/ecosystem/mpsc/bounded.rs b/benchmarks/ecosystem/mpsc/bounded.rs new file mode 100644 index 0000000..bda7107 --- /dev/null +++ b/benchmarks/ecosystem/mpsc/bounded.rs @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use divan::Bencher; +use divan::black_box; +use divan::counter::ItemsCount; + +use super::adapters::AsyncChannel; +use super::adapters::Asyncband; +use super::adapters::BoundedMpsc; +use super::adapters::Flume; +use super::adapters::Tokio; +use super::support::BATCH_MESSAGES; +use super::support::BOUNDED_CAPACITY; +use super::support::Bounded; +use super::support::ConcurrentBatch; +use super::support::PRODUCER_COUNTS; +use crate::support::bench_context; + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn try_round_trip(bencher: Bencher) { + let (sender, mut receiver) = C::channel(BOUNDED_CAPACITY); + + bencher.bench_local(|| { + C::try_send(&sender, black_box(usize::MAX)); + black_box(C::try_recv(&mut receiver)) + }); +} + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn ready_round_trip(bencher: Bencher) { + let mut context = bench_context(); + let (sender, mut receiver) = C::channel(BOUNDED_CAPACITY); + + bencher.bench_local(|| { + C::send_ready(&sender, black_box(usize::MAX), &mut context); + black_box(C::recv_ready(&mut receiver, &mut context)) + }); +} + +#[divan::bench( + types = [Asyncband, Tokio, AsyncChannel, Flume], + args = PRODUCER_COUNTS, + sample_count = 20, + sample_size = 1, + counter = ItemsCount::new(BATCH_MESSAGES), +)] +fn concurrent(bencher: Bencher, producer_count: usize) { + bencher + .with_inputs(|| ConcurrentBatch::>::new(producer_count)) + .bench_local_refs(|batch| batch.run()); +} diff --git a/benchmarks/ecosystem/mpsc/mod.rs b/benchmarks/ecosystem/mpsc/mod.rs new file mode 100644 index 0000000..dd09282 --- /dev/null +++ b/benchmarks/ecosystem/mpsc/mod.rs @@ -0,0 +1,21 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod adapters; +mod bounded; +mod support; +mod unbounded; diff --git a/benchmarks/ecosystem/mpsc/support.rs b/benchmarks/ecosystem/mpsc/support.rs new file mode 100644 index 0000000..aea2885 --- /dev/null +++ b/benchmarks/ecosystem/mpsc/support.rs @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use std::marker::PhantomData; +use std::sync::Arc; +use std::sync::Barrier; +use std::thread; +use std::thread::JoinHandle; + +use divan::black_box; + +use super::adapters::BoundedMpsc; +use super::adapters::UnboundedMpsc; + +pub const BOUNDED_CAPACITY: usize = 64; +pub const BATCH_MESSAGES: usize = 16_384; +pub const PRODUCER_COUNTS: &[usize] = &[1, 2, 4, 8]; + +pub trait ConcurrentMpsc: Send + Sync + 'static { + type Sender: Clone + Send + 'static; + type Receiver: Send + 'static; + + fn channel() -> (Self::Sender, Self::Receiver); + fn send(sender: &Self::Sender, value: usize); + fn recv(receiver: &mut Self::Receiver) -> usize; +} + +pub struct Bounded(PhantomData); + +impl ConcurrentMpsc for Bounded { + type Receiver = C::Receiver; + type Sender = C::Sender; + + fn channel() -> (Self::Sender, Self::Receiver) { + C::channel(BOUNDED_CAPACITY) + } + + fn send(sender: &Self::Sender, value: usize) { + C::send_blocking(sender, value); + } + + fn recv(receiver: &mut Self::Receiver) -> usize { + C::recv_blocking(receiver) + } +} + +pub struct Unbounded(PhantomData); + +impl ConcurrentMpsc for Unbounded { + type Receiver = C::Receiver; + type Sender = C::Sender; + + fn channel() -> (Self::Sender, Self::Receiver) { + C::channel() + } + + fn send(sender: &Self::Sender, value: usize) { + C::send(sender, value); + } + + fn recv(receiver: &mut Self::Receiver) -> usize { + C::recv_blocking(receiver) + } +} + +pub struct ConcurrentBatch { + receiver: C::Receiver, + start: Arc, + workers: Vec>, +} + +impl ConcurrentBatch { + pub fn new(producer_count: usize) -> Self { + assert_eq!(BATCH_MESSAGES % producer_count, 0); + + let (sender, receiver) = C::channel(); + let start = Arc::new(Barrier::new(producer_count + 1)); + let messages_per_producer = BATCH_MESSAGES / producer_count; + let workers = (0..producer_count) + .map(|producer| { + let sender = sender.clone(); + let start = start.clone(); + thread::spawn(move || { + start.wait(); + let first = producer * messages_per_producer; + for offset in 0..messages_per_producer { + C::send(&sender, black_box(first + offset)); + } + }) + }) + .collect(); + drop(sender); + + Self { + receiver, + start, + workers, + } + } + + pub fn run(&mut self) -> usize { + self.start.wait(); + let mut checksum = 0usize; + for _ in 0..BATCH_MESSAGES { + checksum = checksum.wrapping_add(C::recv(&mut self.receiver)); + } + black_box(checksum) + } +} + +impl Drop for ConcurrentBatch { + fn drop(&mut self) { + let panicking = thread::panicking(); + for worker in self.workers.drain(..) { + let result = worker.join(); + if !panicking { + result.expect("benchmark producer panicked"); + } + } + } +} diff --git a/benchmarks/ecosystem/mpsc/unbounded.rs b/benchmarks/ecosystem/mpsc/unbounded.rs new file mode 100644 index 0000000..6c8ac54 --- /dev/null +++ b/benchmarks/ecosystem/mpsc/unbounded.rs @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use divan::Bencher; +use divan::black_box; +use divan::counter::ItemsCount; + +use super::adapters::AsyncChannel; +use super::adapters::Asyncband; +use super::adapters::Flume; +use super::adapters::Tokio; +use super::adapters::UnboundedMpsc; +use super::support::BATCH_MESSAGES; +use super::support::ConcurrentBatch; +use super::support::PRODUCER_COUNTS; +use super::support::Unbounded; +use crate::support::bench_context; + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn ready_round_trip(bencher: Bencher) { + let mut context = bench_context(); + let (sender, mut receiver) = C::channel(); + + bencher.bench_local(|| { + C::send(&sender, black_box(usize::MAX)); + black_box(C::recv_ready(&mut receiver, &mut context)) + }); +} + +#[divan::bench(types = [Asyncband, Tokio, AsyncChannel, Flume])] +fn try_round_trip(bencher: Bencher) { + let (sender, mut receiver) = C::channel(); + + bencher.bench_local(|| { + C::send(&sender, black_box(usize::MAX)); + black_box(C::try_recv(&mut receiver)) + }); +} + +#[divan::bench( + types = [Asyncband, Tokio, AsyncChannel, Flume], + args = PRODUCER_COUNTS, + sample_count = 20, + sample_size = 1, + counter = ItemsCount::new(BATCH_MESSAGES), +)] +fn concurrent(bencher: Bencher, producer_count: usize) { + bencher + .with_inputs(|| ConcurrentBatch::>::new(producer_count)) + .bench_local_refs(|batch| batch.run()); +} diff --git a/benchmarks/latch/mod.rs b/benchmarks/latch/mod.rs new file mode 100644 index 0000000..3554069 --- /dev/null +++ b/benchmarks/latch/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod wait; diff --git a/benchmarks/latch.rs b/benchmarks/latch/wait.rs similarity index 96% rename from benchmarks/latch.rs rename to benchmarks/latch/wait.rs index b2a0a00..b44fa2e 100644 --- a/benchmarks/latch.rs +++ b/benchmarks/latch/wait.rs @@ -21,9 +21,9 @@ use asyncband::latch::Latch; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; const WORKER_COUNTS: &[usize] = &[1, 8, 32]; diff --git a/benchmarks/mpsc.rs b/benchmarks/mpsc/bounded.rs similarity index 74% rename from benchmarks/mpsc.rs rename to benchmarks/mpsc/bounded.rs index f86a7ca..e8b821a 100644 --- a/benchmarks/mpsc.rs +++ b/benchmarks/mpsc/bounded.rs @@ -19,35 +19,12 @@ use asyncband::mpsc; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; const SENDER_COUNTS: &[usize] = &[1, 8, 32]; -#[divan::bench] -fn reregister_pending_receiver(bencher: Bencher) { - let mut context = bench_context(); - let (_sender, mut receiver) = mpsc::unbounded::(); - let mut recv = Box::pin(receiver.recv()); - poll_pending(recv.as_mut(), &mut context); - - bencher.bench_local(|| poll_pending(recv.as_mut(), &mut context)); -} - -#[divan::bench] -fn wake_pending_receiver(bencher: Bencher) { - let mut context = bench_context(); - let (sender, mut receiver) = mpsc::unbounded(); - - bencher.bench_local(|| { - let mut recv = Box::pin(receiver.recv()); - poll_pending(recv.as_mut(), &mut context); - sender.send(black_box(usize::MAX)).unwrap(); - black_box(poll_pinned_ready(recv.as_mut(), &mut context).unwrap()) - }); -} - #[divan::bench(args = SENDER_COUNTS)] fn cancel_backpressured_senders(bencher: Bencher, sender_count: usize) { let mut context = bench_context(); diff --git a/benchmarks/mpsc/mod.rs b/benchmarks/mpsc/mod.rs new file mode 100644 index 0000000..e0ac834 --- /dev/null +++ b/benchmarks/mpsc/mod.rs @@ -0,0 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod bounded; +mod unbounded; diff --git a/benchmarks/mpsc/unbounded.rs b/benchmarks/mpsc/unbounded.rs new file mode 100644 index 0000000..9948c60 --- /dev/null +++ b/benchmarks/mpsc/unbounded.rs @@ -0,0 +1,47 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use asyncband::mpsc; +use divan::Bencher; +use divan::black_box; + +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; + +#[divan::bench] +fn reregister_pending_receiver(bencher: Bencher) { + let mut context = bench_context(); + let (_sender, mut receiver) = mpsc::unbounded::(); + let mut recv = Box::pin(receiver.recv()); + poll_pending(recv.as_mut(), &mut context); + + bencher.bench_local(|| poll_pending(recv.as_mut(), &mut context)); +} + +#[divan::bench] +fn wake_pending_receiver(bencher: Bencher) { + let mut context = bench_context(); + let (sender, mut receiver) = mpsc::unbounded(); + + bencher.bench_local(|| { + let mut recv = Box::pin(receiver.recv()); + poll_pending(recv.as_mut(), &mut context); + sender.send(black_box(usize::MAX)).unwrap(); + black_box(poll_pinned_ready(recv.as_mut(), &mut context).unwrap()) + }); +} diff --git a/benchmarks/mutex.rs b/benchmarks/mutex/lock.rs similarity index 93% rename from benchmarks/mutex.rs rename to benchmarks/mutex/lock.rs index 1367d7f..43b2369 100644 --- a/benchmarks/mutex.rs +++ b/benchmarks/mutex/lock.rs @@ -22,10 +22,10 @@ use asyncband::mutex::Mutex; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const QUEUE_DEPTHS: &[usize] = &[1, 8, 32]; diff --git a/benchmarks/mutex/mod.rs b/benchmarks/mutex/mod.rs new file mode 100644 index 0000000..4701924 --- /dev/null +++ b/benchmarks/mutex/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod lock; diff --git a/benchmarks/once/get_or_init.rs b/benchmarks/once/get_or_init.rs new file mode 100644 index 0000000..f1e0b40 --- /dev/null +++ b/benchmarks/once/get_or_init.rs @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use std::cell::Cell; + +use asyncband::once::OnceCell; +use divan::Bencher; +use divan::black_box; + +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; +use crate::support::wait_until_open; + +const WAITER_COUNTS: &[usize] = &[1, 8, 32]; + +#[divan::bench] +fn initialize_cell(bencher: Bencher) { + let mut context = bench_context(); + + bencher.bench_local(|| { + let cell = OnceCell::new(); + let value = poll_ready( + cell.get_or_init(|| async { black_box(1usize) }), + &mut context, + ); + black_box(*value) + }); +} + +#[divan::bench(args = WAITER_COUNTS)] +fn initialize_cell_waiter_batch(bencher: Bencher, waiter_count: usize) { + let mut context = bench_context(); + + bencher.bench_local(|| { + let cell = OnceCell::new(); + let gate = Cell::new(false); + let mut initializer = Box::pin(cell.get_or_init(|| async { + wait_until_open(&gate).await; + black_box(1usize) + })); + poll_pending(initializer.as_mut(), &mut context); + + let mut waiters = (0..waiter_count) + .map(|_| Box::pin(cell.get_or_init(|| async { unreachable!() }))) + .collect::>(); + for waiter in &mut waiters { + poll_pending(waiter.as_mut(), &mut context); + } + + gate.set(true); + let value = poll_pinned_ready(initializer.as_mut(), &mut context); + black_box(*value); + drop(initializer); + for mut waiter in waiters { + let value = poll_pinned_ready(waiter.as_mut(), &mut context); + black_box(*value); + } + }); +} diff --git a/benchmarks/once/mod.rs b/benchmarks/once/mod.rs new file mode 100644 index 0000000..afcd89c --- /dev/null +++ b/benchmarks/once/mod.rs @@ -0,0 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod get_or_init; +mod wait; diff --git a/benchmarks/once.rs b/benchmarks/once/wait.rs similarity index 59% rename from benchmarks/once.rs rename to benchmarks/once/wait.rs index 567fc6d..31bc131 100644 --- a/benchmarks/once.rs +++ b/benchmarks/once/wait.rs @@ -15,19 +15,16 @@ // specific language governing permissions and limitations // under the License. -use std::cell::Cell; use std::pin::pin; use asyncband::once::Once; -use asyncband::once::OnceCell; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; -use super::support::wait_until_open; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const WAITER_COUNTS: &[usize] = &[1, 8, 32]; @@ -80,48 +77,3 @@ fn complete_waiter_batch(bencher: Bencher, waiter_count: usize) { black_box(once.is_completed()) }); } - -#[divan::bench] -fn initialize_cell(bencher: Bencher) { - let mut context = bench_context(); - - bencher.bench_local(|| { - let cell = OnceCell::new(); - let value = poll_ready( - cell.get_or_init(|| async { black_box(1usize) }), - &mut context, - ); - black_box(*value) - }); -} - -#[divan::bench(args = WAITER_COUNTS)] -fn initialize_cell_waiter_batch(bencher: Bencher, waiter_count: usize) { - let mut context = bench_context(); - - bencher.bench_local(|| { - let cell = OnceCell::new(); - let gate = Cell::new(false); - let mut initializer = Box::pin(cell.get_or_init(|| async { - wait_until_open(&gate).await; - black_box(1usize) - })); - poll_pending(initializer.as_mut(), &mut context); - - let mut waiters = (0..waiter_count) - .map(|_| Box::pin(cell.get_or_init(|| async { unreachable!() }))) - .collect::>(); - for waiter in &mut waiters { - poll_pending(waiter.as_mut(), &mut context); - } - - gate.set(true); - let value = poll_pinned_ready(initializer.as_mut(), &mut context); - black_box(*value); - drop(initializer); - for mut waiter in waiters { - let value = poll_pinned_ready(waiter.as_mut(), &mut context); - black_box(*value); - } - }); -} diff --git a/benchmarks/once_map.rs b/benchmarks/once_map/compute.rs similarity index 82% rename from benchmarks/once_map.rs rename to benchmarks/once_map/compute.rs index 512fc0e..e5733fc 100644 --- a/benchmarks/once_map.rs +++ b/benchmarks/once_map/compute.rs @@ -21,20 +21,21 @@ use asyncband::once::OnceMap; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::defer_input_drop; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; -use super::support::spin_poll_ready; -use super::support::thread_slot_ticket; -use super::support::wait_until_open; -use super::support::yield_polls; +use super::support::CONTENDED_ENTRY_COUNTS; +use super::support::THREAD_COUNTS; +use super::support::preloaded_map; +use crate::support::bench_context; +use crate::support::defer_input_drop; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; +use crate::support::spin_poll_ready; +use crate::support::thread_slot_ticket; +use crate::support::wait_until_open; +use crate::support::yield_polls; const CACHED_ENTRY_COUNTS: &[usize] = &[0, 64, 1024]; const WAITER_COUNTS: &[usize] = &[1, 8, 32]; -const CONTENDED_ENTRY_COUNTS: &[usize] = &[64, 1024]; -const THREAD_COUNTS: &[usize] = &[1, 2, 8, 32]; const MISS_KEY_SPAN: usize = 1 << 16; const COALESCED_LEADER_POLLS: usize = 32; @@ -113,31 +114,6 @@ fn coalesced_compute_batch(bencher: Bencher, waiter_count: usize) { }); } -fn preloaded_map(cached_entries: usize) -> OnceMap { - (0..cached_entries).map(|key| (key, key)).collect() -} - -// The contended benches share one map across OS threads and spread keys with thread_slot_ticket, -// so "disjoint" means threads mostly touch different keys at any moment rather than strict -// per-thread key ownership. -#[divan::bench(threads = THREAD_COUNTS)] -fn contended_get_hit_same_key(bencher: Bencher) { - let map = [(0, 1)].into_iter().collect::>(); - - bencher.bench(|| black_box(map.get(black_box(&0)))); -} - -#[divan::bench(threads = THREAD_COUNTS, args = CONTENDED_ENTRY_COUNTS)] -fn contended_get_hit_disjoint(bencher: Bencher, cached_entries: usize) { - let map = preloaded_map(cached_entries); - - bencher.bench(|| { - let (slot, ticket) = thread_slot_ticket(); - let key = (slot + ticket) % cached_entries; - black_box(map.get(black_box(&key))) - }); -} - #[divan::bench(threads = THREAD_COUNTS)] fn contended_compute_hit_same_key(bencher: Bencher) { let map = [(0, 1)].into_iter().collect::>(); diff --git a/benchmarks/once_map/get.rs b/benchmarks/once_map/get.rs new file mode 100644 index 0000000..cef31a0 --- /dev/null +++ b/benchmarks/once_map/get.rs @@ -0,0 +1,43 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use asyncband::once::OnceMap; +use divan::Bencher; +use divan::black_box; + +use super::support::CONTENDED_ENTRY_COUNTS; +use super::support::THREAD_COUNTS; +use super::support::preloaded_map; +use crate::support::thread_slot_ticket; + +#[divan::bench(threads = THREAD_COUNTS)] +fn contended_get_hit_same_key(bencher: Bencher) { + let map = [(0, 1)].into_iter().collect::>(); + + bencher.bench(|| black_box(map.get(black_box(&0)))); +} + +#[divan::bench(threads = THREAD_COUNTS, args = CONTENDED_ENTRY_COUNTS)] +fn contended_get_hit_disjoint(bencher: Bencher, cached_entries: usize) { + let map = preloaded_map(cached_entries); + + bencher.bench(|| { + let (slot, ticket) = thread_slot_ticket(); + let key = (slot + ticket) % cached_entries; + black_box(map.get(black_box(&key))) + }); +} diff --git a/benchmarks/once_map/mod.rs b/benchmarks/once_map/mod.rs new file mode 100644 index 0000000..7349fcd --- /dev/null +++ b/benchmarks/once_map/mod.rs @@ -0,0 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod compute; +mod get; +mod support; diff --git a/benchmarks/once_map/support.rs b/benchmarks/once_map/support.rs new file mode 100644 index 0000000..44c59c8 --- /dev/null +++ b/benchmarks/once_map/support.rs @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use asyncband::once::OnceMap; + +pub const CONTENDED_ENTRY_COUNTS: &[usize] = &[64, 1024]; +pub const THREAD_COUNTS: &[usize] = &[1, 2, 8, 32]; + +// The contended get and compute benches share one map across OS threads and spread keys with +// thread_slot_ticket, so "disjoint" means threads mostly touch different keys at any moment rather +// than strict per-thread key ownership. +pub fn preloaded_map(cached_entries: usize) -> OnceMap { + (0..cached_entries).map(|key| (key, key)).collect() +} diff --git a/benchmarks/oneshot/mod.rs b/benchmarks/oneshot/mod.rs new file mode 100644 index 0000000..9601ee0 --- /dev/null +++ b/benchmarks/oneshot/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod send; diff --git a/benchmarks/oneshot.rs b/benchmarks/oneshot/send.rs similarity index 92% rename from benchmarks/oneshot.rs rename to benchmarks/oneshot/send.rs index 1b4029b..9b74b6b 100644 --- a/benchmarks/oneshot.rs +++ b/benchmarks/oneshot/send.rs @@ -22,7 +22,7 @@ use std::task::Context; use std::task::Poll; use std::task::Waker; -use asyncband::oneshot; +use asyncband::oneshot::channel; use divan::Bencher; use divan::black_box; @@ -31,7 +31,7 @@ fn send_before_poll(bencher: Bencher) { let mut context = Context::from_waker(Waker::noop()); bencher.bench_local(|| { - let (sender, receiver) = black_box(oneshot::channel()); + let (sender, receiver) = black_box(channel()); let mut receiver = receiver.into_future(); sender.send(black_box(1usize)).unwrap(); @@ -48,7 +48,7 @@ fn poll_before_send(bencher: Bencher) { let mut context = Context::from_waker(Waker::noop()); bencher.bench_local(|| { - let (sender, receiver) = black_box(oneshot::channel()); + let (sender, receiver) = black_box(channel()); let mut receiver = receiver.into_future(); assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending); diff --git a/benchmarks/pool.rs b/benchmarks/pool/bounded.rs similarity index 86% rename from benchmarks/pool.rs rename to benchmarks/pool/bounded.rs index 42e3a82..65f4eea 100644 --- a/benchmarks/pool.rs +++ b/benchmarks/pool/bounded.rs @@ -21,14 +21,13 @@ use std::pin::pin; use asyncband::pool::ManageObject; use asyncband::pool::ObjectStatus; use asyncband::pool::bounded; -use asyncband::pool::unbounded; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const CAPACITIES: &[usize] = &[1, 32, 1024, usize::MAX]; @@ -74,18 +73,6 @@ fn bounded_warm_get_and_return(bencher: Bencher) { }); } -#[divan::bench] -fn unbounded_warm_try_get_and_return(bencher: Bencher) { - let pool = unbounded::Pool::::never_manage(unbounded::PoolConfig::default()); - pool.extend_one(0); - - bencher.bench_local(|| { - let object = pool.try_get().unwrap(); - black_box(*object); - drop(object); - }); -} - #[divan::bench] fn bounded_contended_handoff(bencher: Bencher) { let pool = bounded::Pool::new(bounded::PoolConfig::new(1), Manager); diff --git a/benchmarks/pool/mod.rs b/benchmarks/pool/mod.rs new file mode 100644 index 0000000..e0ac834 --- /dev/null +++ b/benchmarks/pool/mod.rs @@ -0,0 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod bounded; +mod unbounded; diff --git a/benchmarks/pool/unbounded.rs b/benchmarks/pool/unbounded.rs new file mode 100644 index 0000000..dd891cc --- /dev/null +++ b/benchmarks/pool/unbounded.rs @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use asyncband::pool::unbounded; +use divan::Bencher; +use divan::black_box; + +#[divan::bench] +fn unbounded_warm_try_get_and_return(bencher: Bencher) { + let pool = unbounded::Pool::::never_manage(unbounded::PoolConfig::default()); + pool.extend_one(0); + + bencher.bench_local(|| { + let object = pool.try_get().unwrap(); + black_box(*object); + drop(object); + }); +} diff --git a/benchmarks/rwlock/mod.rs b/benchmarks/rwlock/mod.rs new file mode 100644 index 0000000..ef6eca5 --- /dev/null +++ b/benchmarks/rwlock/mod.rs @@ -0,0 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod read; +mod write; diff --git a/benchmarks/rwlock/read.rs b/benchmarks/rwlock/read.rs new file mode 100644 index 0000000..692dbaf --- /dev/null +++ b/benchmarks/rwlock/read.rs @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use asyncband::rwlock::RwLock; +use divan::Bencher; +use divan::black_box; + +use crate::support::bench_context; +use crate::support::poll_ready; + +#[divan::bench] +fn read_heavy_reuse(bencher: Bencher) { + const READS_PER_WRITE: usize = 8; + + let lock = RwLock::new(0usize); + let mut context = bench_context(); + + bencher.bench_local(|| { + for _ in 0..READS_PER_WRITE { + let guard = poll_ready(lock.read(), &mut context); + black_box(*guard); + } + let mut guard = poll_ready(lock.write(), &mut context); + *guard = black_box(guard.wrapping_add(1)); + black_box(*guard) + }); +} diff --git a/benchmarks/rwlock.rs b/benchmarks/rwlock/write.rs similarity index 71% rename from benchmarks/rwlock.rs rename to benchmarks/rwlock/write.rs index f0e9ae2..1321271 100644 --- a/benchmarks/rwlock.rs +++ b/benchmarks/rwlock/write.rs @@ -21,31 +21,13 @@ use asyncband::rwlock::RwLock; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const READER_COUNTS: &[usize] = &[1, 8, 32]; -#[divan::bench] -fn read_heavy_reuse(bencher: Bencher) { - const READS_PER_WRITE: usize = 8; - - let lock = RwLock::new(0usize); - let mut context = bench_context(); - - bencher.bench_local(|| { - for _ in 0..READS_PER_WRITE { - let guard = poll_ready(lock.read(), &mut context); - black_box(*guard); - } - let mut guard = poll_ready(lock.write(), &mut context); - *guard = black_box(guard.wrapping_add(1)); - black_box(*guard) - }); -} - #[divan::bench(args = READER_COUNTS)] fn writer_handoff(bencher: Bencher, reader_count: usize) { let mut context = bench_context(); diff --git a/benchmarks/semaphore.rs b/benchmarks/semaphore/acquire.rs similarity index 85% rename from benchmarks/semaphore.rs rename to benchmarks/semaphore/acquire.rs index bb5db6f..9d3f31d 100644 --- a/benchmarks/semaphore.rs +++ b/benchmarks/semaphore/acquire.rs @@ -22,10 +22,10 @@ use asyncband::semaphore::Semaphore; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const QUEUE_DEPTHS: &[usize] = &[1, 8, 32]; @@ -60,30 +60,6 @@ fn handoff_permit(bencher: Bencher) { }); } -#[divan::bench] -fn fulfill_debt_repeatedly(bencher: Bencher) { - const CYCLES: usize = 64; - - bencher.bench_local(|| { - let semaphore = Semaphore::new(0); - for _ in 0..CYCLES { - semaphore.reduce_permits(black_box(1)); - semaphore.release(black_box(1)); - } - black_box(semaphore.available_permits()) - }); -} - -#[divan::bench] -fn release(bencher: Bencher) { - bencher - .with_inputs(|| Semaphore::new(0)) - .bench_local_values(|semaphore| { - semaphore.release(black_box(1)); - black_box(semaphore) - }); -} - #[divan::bench] fn try_acquire_release(bencher: Bencher) { let semaphore = Semaphore::new(1); diff --git a/benchmarks/semaphore/mod.rs b/benchmarks/semaphore/mod.rs new file mode 100644 index 0000000..7a9c960 --- /dev/null +++ b/benchmarks/semaphore/mod.rs @@ -0,0 +1,19 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod acquire; +mod release; diff --git a/benchmarks/semaphore/release.rs b/benchmarks/semaphore/release.rs new file mode 100644 index 0000000..d5423f4 --- /dev/null +++ b/benchmarks/semaphore/release.rs @@ -0,0 +1,44 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use asyncband::semaphore::Semaphore; +use divan::Bencher; +use divan::black_box; + +#[divan::bench] +fn fulfill_debt_repeatedly(bencher: Bencher) { + const CYCLES: usize = 64; + + bencher.bench_local(|| { + let semaphore = Semaphore::new(0); + for _ in 0..CYCLES { + semaphore.reduce_permits(black_box(1)); + semaphore.release(black_box(1)); + } + black_box(semaphore.available_permits()) + }); +} + +#[divan::bench] +fn release(bencher: Bencher) { + bencher + .with_inputs(|| Semaphore::new(0)) + .bench_local_values(|semaphore| { + semaphore.release(black_box(1)); + black_box(semaphore) + }); +} diff --git a/benchmarks/shutdown/mod.rs b/benchmarks/shutdown/mod.rs new file mode 100644 index 0000000..d695450 --- /dev/null +++ b/benchmarks/shutdown/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod request_shutdown; diff --git a/benchmarks/shutdown.rs b/benchmarks/shutdown/request_shutdown.rs similarity index 92% rename from benchmarks/shutdown.rs rename to benchmarks/shutdown/request_shutdown.rs index 187981c..36f72d7 100644 --- a/benchmarks/shutdown.rs +++ b/benchmarks/shutdown/request_shutdown.rs @@ -19,10 +19,10 @@ use asyncband::shutdown; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; const RECEIVER_COUNTS: &[usize] = &[1, 8, 32]; diff --git a/benchmarks/singleflight/mod.rs b/benchmarks/singleflight/mod.rs new file mode 100644 index 0000000..426858b --- /dev/null +++ b/benchmarks/singleflight/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod work; diff --git a/benchmarks/singleflight.rs b/benchmarks/singleflight/work.rs similarity index 92% rename from benchmarks/singleflight.rs rename to benchmarks/singleflight/work.rs index 2e0b00f..62ffefb 100644 --- a/benchmarks/singleflight.rs +++ b/benchmarks/singleflight/work.rs @@ -21,15 +21,15 @@ use asyncband::singleflight::Group; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::defer_input_drop; -use super::support::poll_pending; -use super::support::poll_pinned_ready; -use super::support::poll_ready; -use super::support::spin_poll_ready; -use super::support::thread_slot_ticket; -use super::support::wait_until_open; -use super::support::yield_polls; +use crate::support::bench_context; +use crate::support::defer_input_drop; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; +use crate::support::poll_ready; +use crate::support::spin_poll_ready; +use crate::support::thread_slot_ticket; +use crate::support::wait_until_open; +use crate::support::yield_polls; const WAITER_COUNTS: &[usize] = &[1, 8, 32]; const THREAD_COUNTS: &[usize] = &[1, 2, 8, 32]; diff --git a/benchmarks/waitgroup/mod.rs b/benchmarks/waitgroup/mod.rs new file mode 100644 index 0000000..3554069 --- /dev/null +++ b/benchmarks/waitgroup/mod.rs @@ -0,0 +1,18 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +mod wait; diff --git a/benchmarks/waitgroup.rs b/benchmarks/waitgroup/wait.rs similarity index 96% rename from benchmarks/waitgroup.rs rename to benchmarks/waitgroup/wait.rs index 39c287a..ff4e764 100644 --- a/benchmarks/waitgroup.rs +++ b/benchmarks/waitgroup/wait.rs @@ -22,9 +22,9 @@ use asyncband::waitgroup::WaitGroup; use divan::Bencher; use divan::black_box; -use super::support::bench_context; -use super::support::poll_pending; -use super::support::poll_pinned_ready; +use crate::support::bench_context; +use crate::support::poll_pending; +use crate::support::poll_pinned_ready; const WORKER_COUNTS: &[usize] = &[1, 8, 32]; From 658e268b4d8d454cbe0f76ddf132894d60b8c82e Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 27 Aug 2026 01:08:37 +0800 Subject: [PATCH 5/5] refactor(benchmarks): separate asyncband suite --- benchmarks/Cargo.toml | 2 +- benchmarks/README.md | 60 ------------------- benchmarks/{ => asyncband}/barrier/mod.rs | 0 benchmarks/{ => asyncband}/barrier/wait.rs | 0 .../{ => asyncband}/blocking/block_on.rs | 0 benchmarks/{ => asyncband}/blocking/mod.rs | 0 .../{ => asyncband}/blocking/wait_timeout.rs | 0 benchmarks/{ => asyncband}/condvar/mod.rs | 0 benchmarks/{ => asyncband}/condvar/wait.rs | 0 benchmarks/{ => asyncband}/latch/mod.rs | 0 benchmarks/{ => asyncband}/latch/wait.rs | 0 benchmarks/{ => asyncband}/main.rs | 0 benchmarks/{ => asyncband}/mpsc/bounded.rs | 0 benchmarks/{ => asyncband}/mpsc/mod.rs | 0 benchmarks/{ => asyncband}/mpsc/unbounded.rs | 0 benchmarks/{ => asyncband}/mutex/lock.rs | 0 benchmarks/{ => asyncband}/mutex/mod.rs | 0 .../{ => asyncband}/once/get_or_init.rs | 0 benchmarks/{ => asyncband}/once/mod.rs | 0 benchmarks/{ => asyncband}/once/wait.rs | 0 .../{ => asyncband}/once_map/compute.rs | 0 benchmarks/{ => asyncband}/once_map/get.rs | 0 benchmarks/{ => asyncband}/once_map/mod.rs | 0 .../{ => asyncband}/once_map/support.rs | 0 benchmarks/{ => asyncband}/oneshot/mod.rs | 0 benchmarks/{ => asyncband}/oneshot/send.rs | 0 benchmarks/{ => asyncband}/pool/bounded.rs | 0 benchmarks/{ => asyncband}/pool/mod.rs | 0 benchmarks/{ => asyncband}/pool/unbounded.rs | 0 benchmarks/{ => asyncband}/rwlock/mod.rs | 0 benchmarks/{ => asyncband}/rwlock/read.rs | 0 benchmarks/{ => asyncband}/rwlock/write.rs | 0 .../{ => asyncband}/semaphore/acquire.rs | 0 benchmarks/{ => asyncband}/semaphore/mod.rs | 0 .../{ => asyncband}/semaphore/release.rs | 0 benchmarks/{ => asyncband}/shutdown/mod.rs | 0 .../shutdown/request_shutdown.rs | 0 .../{ => asyncband}/singleflight/mod.rs | 0 .../{ => asyncband}/singleflight/work.rs | 0 benchmarks/{ => asyncband}/support.rs | 0 benchmarks/{ => asyncband}/waitgroup/mod.rs | 0 benchmarks/{ => asyncband}/waitgroup/wait.rs | 0 benchmarks/ecosystem/main.rs | 2 +- 43 files changed, 2 insertions(+), 62 deletions(-) delete mode 100644 benchmarks/README.md rename benchmarks/{ => asyncband}/barrier/mod.rs (100%) rename benchmarks/{ => asyncband}/barrier/wait.rs (100%) rename benchmarks/{ => asyncband}/blocking/block_on.rs (100%) rename benchmarks/{ => asyncband}/blocking/mod.rs (100%) rename benchmarks/{ => asyncband}/blocking/wait_timeout.rs (100%) rename benchmarks/{ => asyncband}/condvar/mod.rs (100%) rename benchmarks/{ => asyncband}/condvar/wait.rs (100%) rename benchmarks/{ => asyncband}/latch/mod.rs (100%) rename benchmarks/{ => asyncband}/latch/wait.rs (100%) rename benchmarks/{ => asyncband}/main.rs (100%) rename benchmarks/{ => asyncband}/mpsc/bounded.rs (100%) rename benchmarks/{ => asyncband}/mpsc/mod.rs (100%) rename benchmarks/{ => asyncband}/mpsc/unbounded.rs (100%) rename benchmarks/{ => asyncband}/mutex/lock.rs (100%) rename benchmarks/{ => asyncband}/mutex/mod.rs (100%) rename benchmarks/{ => asyncband}/once/get_or_init.rs (100%) rename benchmarks/{ => asyncband}/once/mod.rs (100%) rename benchmarks/{ => asyncband}/once/wait.rs (100%) rename benchmarks/{ => asyncband}/once_map/compute.rs (100%) rename benchmarks/{ => asyncband}/once_map/get.rs (100%) rename benchmarks/{ => asyncband}/once_map/mod.rs (100%) rename benchmarks/{ => asyncband}/once_map/support.rs (100%) rename benchmarks/{ => asyncband}/oneshot/mod.rs (100%) rename benchmarks/{ => asyncband}/oneshot/send.rs (100%) rename benchmarks/{ => asyncband}/pool/bounded.rs (100%) rename benchmarks/{ => asyncband}/pool/mod.rs (100%) rename benchmarks/{ => asyncband}/pool/unbounded.rs (100%) rename benchmarks/{ => asyncband}/rwlock/mod.rs (100%) rename benchmarks/{ => asyncband}/rwlock/read.rs (100%) rename benchmarks/{ => asyncband}/rwlock/write.rs (100%) rename benchmarks/{ => asyncband}/semaphore/acquire.rs (100%) rename benchmarks/{ => asyncband}/semaphore/mod.rs (100%) rename benchmarks/{ => asyncband}/semaphore/release.rs (100%) rename benchmarks/{ => asyncband}/shutdown/mod.rs (100%) rename benchmarks/{ => asyncband}/shutdown/request_shutdown.rs (100%) rename benchmarks/{ => asyncband}/singleflight/mod.rs (100%) rename benchmarks/{ => asyncband}/singleflight/work.rs (100%) rename benchmarks/{ => asyncband}/support.rs (100%) rename benchmarks/{ => asyncband}/waitgroup/mod.rs (100%) rename benchmarks/{ => asyncband}/waitgroup/wait.rs (100%) diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index 0a90b54..c082f90 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -50,7 +50,7 @@ tokio = { workspace = true, features = ["sync"] } [[bench]] harness = false name = "benchmarks" -path = "main.rs" +path = "asyncband/main.rs" [[bench]] harness = false diff --git a/benchmarks/README.md b/benchmarks/README.md deleted file mode 100644 index e51f96e..0000000 --- a/benchmarks/README.md +++ /dev/null @@ -1,60 +0,0 @@ - - -# Benchmarks - -`benchmarks` measures Asyncband primitives in isolation. The `ecosystem` target compares channel operations with semantically similar Rust channels so a new implementation does not hide a large performance regression behind API differences. - -Source files use a primitive/operation layout such as `semaphore/acquire.rs` and `once_map/compute.rs`. Shared setup stays in the nearest `support.rs`. Ecosystem comparisons remain a separate target under `ecosystem/` and follow the same layout within each compared primitive. - -## Running - -Run the repository benchmark workflow, including the ecosystem target: - -```shell -cargo x bench -``` - -For a shorter development loop, select the companion target and optionally a Divan name filter: - -```shell -cargo bench -p benchmarks --bench ecosystem -cargo bench -p benchmarks --bench ecosystem -- mpsc::bounded::concurrent -``` - -Use a release build on an otherwise idle machine, record the CPU and operating system, and compare implementations in the same invocation. Absolute results from different machines are not directly comparable. - -## Channel methodology - -The current suite covers the MPSC API that Asyncband exposes today: - -- bounded capacity is 64 messages; -- ready-path cases reuse an empty channel and measure one send/receive round trip; -- concurrent cases move 16,384 messages from 1, 2, 4, or 8 producer threads to one consumer; -- channel construction, thread spawning, and thread joining stay outside the timed section; -- async operations are driven by a minimal standards-based executor or a benchmark waker, so no peer gets a dedicated runtime; -- every implementation receives the same `usize` values and the consumer computes a checksum to keep the work observable. - -The peer set is Asyncband from the current checkout, Tokio 1.53.1, async-channel 2.5.0, and flume 0.12.0. `Cargo.lock` records the exact resolved versions. Tokio has the same MPSC topology; async-channel and flume are MPMC implementations measured with one receiver, so their extra receiver capability is a documented semantic difference. async-channel exposes unbounded `send` as a future, so the unbounded cases use its non-waiting `try_send` path to match the other implementations' immediate sends. Benchmark-only peers are dev dependencies of the `benchmarks` package and do not become runtime dependencies of `asyncband`. - -The suite is a regression signal rather than a fastest-wins contest. Investigate a sustained result above 3x the closest semantic peer. Treat an order-of-magnitude gap as blocking unless a documented semantic or resource tradeoff explains it. - -## Extending the matrix - -Add comparable cases when Asyncband exposes another topology; do not publish peer-only rows as an Asyncband baseline. SPMC and MPMC queue work should add competing-consumer and balanced producer/consumer cases. Broadcast work should add fanout and producer-contention cases while documenting delivery, overwrite, and lag semantics. Watch work should compare latest-state notification rather than queue throughput. diff --git a/benchmarks/barrier/mod.rs b/benchmarks/asyncband/barrier/mod.rs similarity index 100% rename from benchmarks/barrier/mod.rs rename to benchmarks/asyncband/barrier/mod.rs diff --git a/benchmarks/barrier/wait.rs b/benchmarks/asyncband/barrier/wait.rs similarity index 100% rename from benchmarks/barrier/wait.rs rename to benchmarks/asyncband/barrier/wait.rs diff --git a/benchmarks/blocking/block_on.rs b/benchmarks/asyncband/blocking/block_on.rs similarity index 100% rename from benchmarks/blocking/block_on.rs rename to benchmarks/asyncband/blocking/block_on.rs diff --git a/benchmarks/blocking/mod.rs b/benchmarks/asyncband/blocking/mod.rs similarity index 100% rename from benchmarks/blocking/mod.rs rename to benchmarks/asyncband/blocking/mod.rs diff --git a/benchmarks/blocking/wait_timeout.rs b/benchmarks/asyncband/blocking/wait_timeout.rs similarity index 100% rename from benchmarks/blocking/wait_timeout.rs rename to benchmarks/asyncband/blocking/wait_timeout.rs diff --git a/benchmarks/condvar/mod.rs b/benchmarks/asyncband/condvar/mod.rs similarity index 100% rename from benchmarks/condvar/mod.rs rename to benchmarks/asyncband/condvar/mod.rs diff --git a/benchmarks/condvar/wait.rs b/benchmarks/asyncband/condvar/wait.rs similarity index 100% rename from benchmarks/condvar/wait.rs rename to benchmarks/asyncband/condvar/wait.rs diff --git a/benchmarks/latch/mod.rs b/benchmarks/asyncband/latch/mod.rs similarity index 100% rename from benchmarks/latch/mod.rs rename to benchmarks/asyncband/latch/mod.rs diff --git a/benchmarks/latch/wait.rs b/benchmarks/asyncband/latch/wait.rs similarity index 100% rename from benchmarks/latch/wait.rs rename to benchmarks/asyncband/latch/wait.rs diff --git a/benchmarks/main.rs b/benchmarks/asyncband/main.rs similarity index 100% rename from benchmarks/main.rs rename to benchmarks/asyncband/main.rs diff --git a/benchmarks/mpsc/bounded.rs b/benchmarks/asyncband/mpsc/bounded.rs similarity index 100% rename from benchmarks/mpsc/bounded.rs rename to benchmarks/asyncband/mpsc/bounded.rs diff --git a/benchmarks/mpsc/mod.rs b/benchmarks/asyncband/mpsc/mod.rs similarity index 100% rename from benchmarks/mpsc/mod.rs rename to benchmarks/asyncband/mpsc/mod.rs diff --git a/benchmarks/mpsc/unbounded.rs b/benchmarks/asyncband/mpsc/unbounded.rs similarity index 100% rename from benchmarks/mpsc/unbounded.rs rename to benchmarks/asyncband/mpsc/unbounded.rs diff --git a/benchmarks/mutex/lock.rs b/benchmarks/asyncband/mutex/lock.rs similarity index 100% rename from benchmarks/mutex/lock.rs rename to benchmarks/asyncband/mutex/lock.rs diff --git a/benchmarks/mutex/mod.rs b/benchmarks/asyncband/mutex/mod.rs similarity index 100% rename from benchmarks/mutex/mod.rs rename to benchmarks/asyncband/mutex/mod.rs diff --git a/benchmarks/once/get_or_init.rs b/benchmarks/asyncband/once/get_or_init.rs similarity index 100% rename from benchmarks/once/get_or_init.rs rename to benchmarks/asyncband/once/get_or_init.rs diff --git a/benchmarks/once/mod.rs b/benchmarks/asyncband/once/mod.rs similarity index 100% rename from benchmarks/once/mod.rs rename to benchmarks/asyncband/once/mod.rs diff --git a/benchmarks/once/wait.rs b/benchmarks/asyncband/once/wait.rs similarity index 100% rename from benchmarks/once/wait.rs rename to benchmarks/asyncband/once/wait.rs diff --git a/benchmarks/once_map/compute.rs b/benchmarks/asyncband/once_map/compute.rs similarity index 100% rename from benchmarks/once_map/compute.rs rename to benchmarks/asyncband/once_map/compute.rs diff --git a/benchmarks/once_map/get.rs b/benchmarks/asyncband/once_map/get.rs similarity index 100% rename from benchmarks/once_map/get.rs rename to benchmarks/asyncband/once_map/get.rs diff --git a/benchmarks/once_map/mod.rs b/benchmarks/asyncband/once_map/mod.rs similarity index 100% rename from benchmarks/once_map/mod.rs rename to benchmarks/asyncband/once_map/mod.rs diff --git a/benchmarks/once_map/support.rs b/benchmarks/asyncband/once_map/support.rs similarity index 100% rename from benchmarks/once_map/support.rs rename to benchmarks/asyncband/once_map/support.rs diff --git a/benchmarks/oneshot/mod.rs b/benchmarks/asyncband/oneshot/mod.rs similarity index 100% rename from benchmarks/oneshot/mod.rs rename to benchmarks/asyncband/oneshot/mod.rs diff --git a/benchmarks/oneshot/send.rs b/benchmarks/asyncband/oneshot/send.rs similarity index 100% rename from benchmarks/oneshot/send.rs rename to benchmarks/asyncband/oneshot/send.rs diff --git a/benchmarks/pool/bounded.rs b/benchmarks/asyncband/pool/bounded.rs similarity index 100% rename from benchmarks/pool/bounded.rs rename to benchmarks/asyncband/pool/bounded.rs diff --git a/benchmarks/pool/mod.rs b/benchmarks/asyncband/pool/mod.rs similarity index 100% rename from benchmarks/pool/mod.rs rename to benchmarks/asyncband/pool/mod.rs diff --git a/benchmarks/pool/unbounded.rs b/benchmarks/asyncband/pool/unbounded.rs similarity index 100% rename from benchmarks/pool/unbounded.rs rename to benchmarks/asyncband/pool/unbounded.rs diff --git a/benchmarks/rwlock/mod.rs b/benchmarks/asyncband/rwlock/mod.rs similarity index 100% rename from benchmarks/rwlock/mod.rs rename to benchmarks/asyncband/rwlock/mod.rs diff --git a/benchmarks/rwlock/read.rs b/benchmarks/asyncband/rwlock/read.rs similarity index 100% rename from benchmarks/rwlock/read.rs rename to benchmarks/asyncband/rwlock/read.rs diff --git a/benchmarks/rwlock/write.rs b/benchmarks/asyncband/rwlock/write.rs similarity index 100% rename from benchmarks/rwlock/write.rs rename to benchmarks/asyncband/rwlock/write.rs diff --git a/benchmarks/semaphore/acquire.rs b/benchmarks/asyncband/semaphore/acquire.rs similarity index 100% rename from benchmarks/semaphore/acquire.rs rename to benchmarks/asyncband/semaphore/acquire.rs diff --git a/benchmarks/semaphore/mod.rs b/benchmarks/asyncband/semaphore/mod.rs similarity index 100% rename from benchmarks/semaphore/mod.rs rename to benchmarks/asyncband/semaphore/mod.rs diff --git a/benchmarks/semaphore/release.rs b/benchmarks/asyncband/semaphore/release.rs similarity index 100% rename from benchmarks/semaphore/release.rs rename to benchmarks/asyncband/semaphore/release.rs diff --git a/benchmarks/shutdown/mod.rs b/benchmarks/asyncband/shutdown/mod.rs similarity index 100% rename from benchmarks/shutdown/mod.rs rename to benchmarks/asyncband/shutdown/mod.rs diff --git a/benchmarks/shutdown/request_shutdown.rs b/benchmarks/asyncband/shutdown/request_shutdown.rs similarity index 100% rename from benchmarks/shutdown/request_shutdown.rs rename to benchmarks/asyncband/shutdown/request_shutdown.rs diff --git a/benchmarks/singleflight/mod.rs b/benchmarks/asyncband/singleflight/mod.rs similarity index 100% rename from benchmarks/singleflight/mod.rs rename to benchmarks/asyncband/singleflight/mod.rs diff --git a/benchmarks/singleflight/work.rs b/benchmarks/asyncband/singleflight/work.rs similarity index 100% rename from benchmarks/singleflight/work.rs rename to benchmarks/asyncband/singleflight/work.rs diff --git a/benchmarks/support.rs b/benchmarks/asyncband/support.rs similarity index 100% rename from benchmarks/support.rs rename to benchmarks/asyncband/support.rs diff --git a/benchmarks/waitgroup/mod.rs b/benchmarks/asyncband/waitgroup/mod.rs similarity index 100% rename from benchmarks/waitgroup/mod.rs rename to benchmarks/asyncband/waitgroup/mod.rs diff --git a/benchmarks/waitgroup/wait.rs b/benchmarks/asyncband/waitgroup/wait.rs similarity index 100% rename from benchmarks/waitgroup/wait.rs rename to benchmarks/asyncband/waitgroup/wait.rs diff --git a/benchmarks/ecosystem/main.rs b/benchmarks/ecosystem/main.rs index e5cb0fc..ae6037b 100644 --- a/benchmarks/ecosystem/main.rs +++ b/benchmarks/ecosystem/main.rs @@ -18,7 +18,7 @@ mod mpsc; #[allow(dead_code)] -#[path = "../support.rs"] +#[path = "../asyncband/support.rs"] mod support; fn main() {