Help with inventory crate

Hello all. I'm trying to prototype a benchmarking library where benchmarks are collected using the inventory crate, but it's not working as I expect.

What I have now looks like this: I have a workspace with the main library benchpress and an example consumer of the library benchpress-example. benchpress defines a struct Benchmark that's collected with inventory::collect!(), and benchpress-example adds a benchmark using inventory::submit!(). Then, the added benchmarks are iterated in benchpress-example/benches/bench.rs.

exa -T

.
├── benchpress
│  ├── Cargo.toml
│  └── src
│     └── lib.rs
├── benchpress-example
│  ├── benches
│  │  └── bench.rs
│  ├── Cargo.toml
│  └── src
│     └── lib.rs
├── Cargo.lock
├── Cargo.toml

Cargo.toml:

[workspace]
members = [
    "benchpress",
    "benchpress-example",
]

benchpress/Cargo.toml

[package]
name = "benchpress"
version = "0.1.0"
authors = ["Billy Rieger <wrieger@protonmail.com>"]
edition = "2018"

[dependencies]
inventory = "0.1"

benchpress-example/Cargo.toml

[package]
name = "benchpress-example"
version = "0.1.0"
authors = ["Billy Rieger <wrieger@protonmail.com>"]
edition = "2018"

[dependencies]
benchpress = { path = "../benchpress" }
inventory = "0.1"

[[bench]]
name = "bench"
harness = false

benchpress/src/lib.rs

#[derive(Debug)]
pub struct Benchmark {
    name: &'static str,
}

inventory::collect!(Benchmark);

impl Benchmark {
    pub fn new(name: &'static str) -> Self {
        Self { name }
    }
}

pub fn bench() {
    for benchmark in inventory::iter::<Benchmark> {
        println!("{:?}", benchmark);
    }
}

inventory::submit!(Benchmark::new("hello from benchpress crate"));

benchpress-example/src/lib.rs

inventory::submit!(benchpress::Benchmark::new(
    "hello from benchpress-example crate"
));

benchpress-example/benches/bench.rs

fn main() {
    benchpress::bench();
}

When I run cargo bench -- --nocapture, I would expect to see both "hello from benchpress crate" and "hello from benchpress-example crate". However, the actual output only shows the former:

$ cargo bench -- --nocapture
    Finished release [optimized] target(s) in 0.04s
     ...
     Running target/release/deps/bench-138439ced4f75337
Benchmark { name: "hello from benchpress crate" }

Is this a limitation of the inventory crate or am I doing something incorrectly?

Edit: fixed a typo.

1 Like

Have you used cargo test when you were developing your project?
Edit1: 'cause when you are prototyping in Rust you need to do tests before benchmarking.
Edit2: For benchmarking you need the #[bench] attribute and then you can improve the code's performance and do stuff, here's a link to the Unstable book.

Try adding #[allow(unused_imports)] use benchpress_example; to benchpress-example/benches/bench.rs, if nothing from the crate is referenced then the crate will not be linked in the final output (and I think inventory uses some linker magic to do its work).

(In the next release this will be able to be written as use benchpress_example as _; instead of needing to use the allow, but that is not stable yet).

2 Likes

Interesting. Adding use benchpress_example doesn't work, but if I define a dummy function pub fn dummy() {} in benchpress-example and then call that function in benchpress-example/benches/bench.rs, it works! Thanks for the suggestion.

I wonder if there's a way to get around having to actually use an imported item in order to get inventory to collect properly.

1 Like

Oh, weird, with panic-abort I found just adding a use for it was enough to ensure it was linked in, I guess that's related to it using lang items rather than actual linker stuff like inventory is.

Might be worth opening an issue on inventory to see if there's some other workaround (and have this one documented if not).

1 Like