Expand description
§loom-rs
Weaving multiple threads together
A bespoke thread pool runtime combining tokio and rayon with CPU pinning capabilities.
§Features
- Hybrid Runtime: Combines tokio for async I/O with rayon for CPU-bound parallel work
- CPU Pinning: Automatically pins threads to specific CPUs for consistent performance
- Zero Allocation:
spawn_compute()uses per-type pools for zero allocation after warmup - Flexible Configuration: Configure via files (TOML/YAML/JSON), environment variables, or code
- CLI Integration: Built-in clap support for command-line overrides
- CUDA NUMA Awareness: Optional feature for selecting CPUs local to a GPU (Linux only)
§Quick Start
ⓘ
use loom_rs::LoomBuilder;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = LoomBuilder::new()
.prefix("myapp")
.tokio_threads(2)
.rayon_threads(6)
.build()?;
runtime.block_on(async {
// Spawn tracked async I/O task
let io_handle = runtime.spawn_async(async {
// Async I/O work
42
});
// Spawn tracked compute task and await result (zero alloc after warmup)
let result = runtime.spawn_compute(|| {
// CPU-bound work on rayon
(0..1000000).sum::<i64>()
}).await;
println!("Compute result: {}", result);
// Zero-overhead parallel iterators
let _sum = runtime.install(|| {
use rayon::prelude::*;
(0..1000).into_par_iter().sum::<i64>()
});
// Wait for async task
let io_result = io_handle.await.unwrap();
println!("I/O result: {}", io_result);
});
// Graceful shutdown
runtime.block_until_idle();
Ok(())
}§Ergonomic Access
Use current_runtime() or spawn_compute() from anywhere in the runtime:
ⓘ
use loom_rs::LoomBuilder;
let runtime = LoomBuilder::new().build()?;
runtime.block_on(async {
// No need to pass &runtime around
let result = loom_rs::spawn_compute(|| expensive_work()).await;
// Or get the runtime explicitly
let rt = loom_rs::current_runtime().unwrap();
rt.spawn_async(async { /* ... */ });
});§Configuration
Configuration sources are merged in order (later sources override earlier):
- Default values
- Config files (via
.file()) - Environment variables (via
.env_prefix()) - Programmatic overrides
- CLI arguments (via
.with_cli_args())
§Config File Example (TOML)
prefix = "myapp"
cpuset = "0-7,16-23"
tokio_threads = 2
rayon_threads = 14
compute_pool_size = 64§Environment Variables
With .env_prefix("LOOM"):
LOOM_PREFIX=myappLOOM_CPUSET=0-7LOOM_TOKIO_THREADS=2LOOM_RAYON_THREADS=6
§CLI Arguments
ⓘ
use clap::Parser;
use loom_rs::{LoomBuilder, LoomArgs};
#[derive(Parser)]
struct MyArgs {
#[command(flatten)]
loom: LoomArgs,
}
let args = MyArgs::parse();
let runtime = LoomBuilder::new()
.file("config.toml")
.env_prefix("LOOM")
.with_cli_args(&args.loom)
.build()?;§CPU Set Format
The cpuset option accepts a string in Linux taskset/numactl format:
- Single CPUs:
"0","5" - Ranges:
"0-7","16-23" - Mixed:
"0-3,8-11","0,2,4,6-8"
§CUDA Support
With the cuda feature enabled (Linux only), you can configure the runtime
to use CPUs local to a specific CUDA GPU:
ⓘ
let runtime = LoomBuilder::new()
.cuda_device_id(0) // Use CPUs near GPU 0
.build()?;§Thread Naming
Threads are named with the configured prefix:
- Tokio threads:
{prefix}-tokio-0000,{prefix}-tokio-0001, … - Rayon threads:
{prefix}-rayon-0000,{prefix}-rayon-0001, …
Re-exports§
pub use builder::LoomArgs;pub use builder::LoomBuilder;pub use config::LoomConfig;pub use error::LoomError;pub use error::Result;pub use runtime::LoomRuntime;pub use runtime::LoomRuntimeInner;pub use stream::ComputeStreamExt;
Modules§
- builder
- Builder pattern for constructing Loom runtimes.
- config
- Configuration types for loom-rs runtime.
- cpuset
- CPU set parsing and validation utilities.
- error
- Error types for loom-rs.
- runtime
- Loom runtime implementation.
- stream
- Stream combinators for processing items via rayon compute threads.
Functions§
- current_
runtime - Get the current loom runtime from thread-local storage.
- spawn_
compute - Spawn compute work using the current runtime.
- try_
spawn_ compute - Try to spawn compute work using the current runtime.