Skip to content

Commit fb3e29f

Browse files
eliothedemanfacebook-github-bot
authored andcommitted
Channel benchmarks (pytorch-labs#627)
Summary: Pull Request resolved: pytorch-labs#627 Reviewed By: vidhyav Differential Revision: D78755894
1 parent 354b66c commit fb3e29f

File tree

3 files changed

+187
-1
lines changed

3 files changed

+187
-1
lines changed

hyperactor/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# @generated by autocargo from //monarch/hyperactor:[hyperactor,hyperactor-example-derive]
1+
# @generated by autocargo from //monarch/hyperactor:[channel_benchmarks,hyperactor,hyperactor-example-derive]
22

33
[package]
44
name = "hyperactor"
@@ -9,6 +9,10 @@ description = "a high-performance, scalable actor framework for cluster computin
99
repository = "https://github.com/pytorch-labs/monarch/"
1010
license = "BSD-3-Clause"
1111

12+
[[bin]]
13+
name = "channel_benchmarks"
14+
path = "benches/channel_benchmarks.rs"
15+
1216
[[bin]]
1317
name = "hyperactor_example_derive"
1418
path = "example/derive.rs"
@@ -21,6 +25,7 @@ bincode = "1.3.3"
2125
bytes = { version = "1.9.0", features = ["serde"] }
2226
cityhasher = "0.1.0"
2327
crc32fast = "1.4"
28+
criterion = { version = "0.5.1", features = ["async_tokio", "csv_output"] }
2429
dashmap = { version = "5.5.3", features = ["rayon", "serde"] }
2530
derivative = "2.2"
2631
dns-lookup = "1.0"

hyperactor/benches/README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Hyperactor Channel Benchmarks
2+
3+
This directory contains performance benchmarks for the Hyperactor channel system, specifically testing message passing throughput across different transport types and message sizes.
4+
5+
## Overview
6+
7+
The benchmark suite measures the performance of Hyperactor's channel communication system by testing:
8+
9+
- **Transport Types**: Local, TCP, MetaTLS, and Unix socket transports
10+
- **Message Sizes**: Ranging from 10 bytes to 1 GB (10^1 to 10^9 bytes)
11+
- **Metrics**: Throughput measured in bytes per second
12+
13+
## Benchmark Details
14+
15+
### Transport Types Tested
16+
17+
1. **Local**: In-memory transport for same-process communication
18+
2. **TCP**: Network transport using TCP sockets
19+
3. **MetaTLS**: Secure transport using Meta's TLS implementation
20+
4. **Unix**: Unix domain socket transport for inter-process communication
21+
22+
### Message Size Range
23+
24+
The benchmark tests message sizes across multiple orders of magnitude:
25+
- Small messages: 10B, 100B, 1KB
26+
- Medium messages: 10KB, 100KB, 1MB
27+
- Large messages: 10MB, 100MB, 1GB
28+
29+
Each message contains:
30+
- An ID field (`u64`)
31+
- A payload of the specified size (filled with zeros)
32+
33+
## Running the Benchmarks
34+
35+
### Prerequisites
36+
37+
Ensure you have the Rust toolchain and required dependencies installed.
38+
39+
### Running All Benchmarks
40+
41+
```bash
42+
# From the hyperactor directory
43+
buck run @//mode/opt //monarch/hyperactor/benches:channel_benchmarks -- --bench
44+
```
45+
46+
### Running with Cargo (if available)
47+
48+
```bash
49+
# From the hyperactor directory
50+
cargo bench --bench channel_benchmarks
51+
```
52+
53+
## Understanding the Results
54+
55+
The benchmark output will show results in the format:
56+
```
57+
message_sizes/message_<transport>_<size><unit>
58+
```
59+
60+
For example:
61+
- `message_sizes/message_local_1kb`: Local transport with 1KB messages
62+
- `message_sizes/message_tcp_10mb`: TCP transport with 10MB messages
63+
- `message_sizes/message_metatls_100b`: MetaTLS transport with 100 byte messages
64+
65+
### Interpreting Performance
66+
67+
- **Higher throughput** (bytes/sec) indicates better performance
68+
- **Local transport** typically shows the highest throughput
69+
- **Larger messages** generally achieve higher throughput due to amortized overhead
70+
- **Secure transports** (MetaTLS) may show lower throughput due to encryption overhead
71+
72+
## Benchmark Implementation
73+
74+
The benchmark uses the [Criterion](https://docs.rs/criterion/) framework for statistical analysis and uses Tokio's async runtime for handling the asynchronous channel operations.
75+
76+
Each benchmark iteration:
77+
1. Sets up a server listening on the specified transport
78+
2. Creates a client connection to the server
79+
3. Sends messages of the specified size
80+
4. Measures the time taken for round-trip communication
81+
5. Calculates throughput based on bytes transferred and time elapsed
82+
83+
## Use Cases
84+
85+
These benchmarks are useful for:
86+
- **Performance regression testing**: Ensuring changes don't degrade performance
87+
- **Transport selection**: Choosing the appropriate transport for your use case
88+
- **Capacity planning**: Understanding throughput limits for different message sizes
89+
- **Optimization validation**: Verifying that performance improvements are effective
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
use std::time::Instant;
10+
11+
use criterion::Criterion;
12+
use criterion::Throughput;
13+
use criterion::criterion_group;
14+
use criterion::criterion_main;
15+
use hyperactor::Named;
16+
use hyperactor::channel::ChannelAddr;
17+
use hyperactor::channel::ChannelTransport;
18+
use hyperactor::channel::Rx;
19+
use hyperactor::channel::Tx;
20+
use hyperactor::channel::dial;
21+
use hyperactor::channel::serve;
22+
use serde::Deserialize;
23+
use serde::Serialize;
24+
use tokio::runtime::Runtime;
25+
26+
#[derive(Debug, Clone, Serialize, Deserialize, Named, PartialEq)]
27+
struct Message {
28+
id: u64,
29+
payload: Vec<u8>,
30+
}
31+
32+
impl Message {
33+
fn new(id: u64, size: usize) -> Self {
34+
Self {
35+
id,
36+
payload: vec![0; size],
37+
}
38+
}
39+
}
40+
41+
// Benchmark message sizes
42+
fn bench_message_sizes(c: &mut Criterion) {
43+
let mut group = c.benchmark_group("message_sizes");
44+
45+
let transports = vec![
46+
("local", ChannelTransport::Local),
47+
("tcp", ChannelTransport::Tcp),
48+
("metatls", ChannelTransport::MetaTls),
49+
("unix", ChannelTransport::Unix),
50+
];
51+
52+
for size_exp in 5..10 {
53+
let size = 10_usize.pow(size_exp);
54+
let fsize = size as f64;
55+
let (nice_size, postfix) = match size {
56+
1_000..=999_999 => (fsize / 1000f64, "kb"),
57+
1_000_000..=999_999_999 => (fsize / 1_000_000f64, "mb"),
58+
1_000_000_000..=999_999_999_999 => (fsize / 1_000_000_000f64, "gb"),
59+
_ => (fsize, "b"),
60+
};
61+
62+
for (transport_name, transport) in &transports {
63+
let transport = transport.clone();
64+
group.throughput(Throughput::Bytes(size as u64));
65+
group.bench_function(
66+
format!("message_{}_{}{}", transport_name, nice_size, postfix),
67+
move |b| {
68+
let mut b = b.to_async(Runtime::new().unwrap());
69+
let tt = &transport;
70+
b.iter_custom(|iters| async move {
71+
let addr = ChannelAddr::any(tt.clone());
72+
let (listen_addr, mut rx) = serve::<Message>(addr).await.unwrap();
73+
let tx = dial::<Message>(listen_addr).unwrap();
74+
let msg = Message::new(0, size);
75+
let start = Instant::now();
76+
for _ in 0..iters {
77+
tx.post(msg.clone());
78+
rx.recv().await.unwrap();
79+
}
80+
start.elapsed()
81+
});
82+
},
83+
);
84+
}
85+
}
86+
87+
group.finish();
88+
}
89+
90+
criterion_group!(benches, bench_message_sizes);
91+
92+
criterion_main!(benches);

0 commit comments

Comments
 (0)