N00b question: how do I let benchmark files know my functions?

I was trying to use the "bencher" framework to write benchmarks for my own codebase. However, I couldn't quite get the benchmark file to recognize functions defined in my src folder. For example, I've a file in src/lib.rs and defined a struct pub struct Foo there. Then, how do I use the struct Foo in benches/example.rs? I tried with extern crate with my package name and use lib::..., but neither works...

Edit: actually the above is not all correct: I don't have a lib.rs file and my struct was defined in src/lib/core.rs. I provided a minimal example project called bench_example below. The file tree looks like:

bench_example

  • src
    • main.rs
    • lib
      • core.rs
      • mod.rs
  • benches
    • example.rs

In "main.rs" I simply have a mod lib; statement. Similarly, in "mod.rs" I just have mod core; Then, in "core.rs" I have a definition struct Foo{}

The problem is when I try to use the Foo struct in the benchmark file below:

"benches/example.rs":

use bencher::{benchmark_group, benchmark_main, Bencher};

use bench_example::lib::core::Bar;

pub fn a(b: &mut Bencher){
    let _ = Bar{};
}

benchmark_group!(benches, a);

benchmark_main!(benches);

lib.rs contains the root of your library's namespace, so items defined there are available directly under the package name:

use my_package_name::Foo;
1 Like

Somehow that does not work for me. When I do use my_package_name::Foo it says "unresolved import". Do I need to publish the package or do something before it can be used?

No, you shouldn't need to publish it. We probably need more details. Could you create a minimal reproducible example of your file structure and the command you're running?

Good idea! I've edited the question to provide a minimal example.

Ok, I've gotten your code running with the following modifications:

  1. You seem to be running a library + binary system, but I don't think you have it set up properly. This SO answer shows how to set it up, but basically your library and binary are different things, where the binary can use the library as an extern crate bench_example, not as mod lib.
  2. Similarly, I added an extern crate bench_example; to benches/example.rs.
  3. In benches/example.rs, change the use bench_example::lib::core::Bar to just use bench_example::core::Bar. The lib isn't part of the path.
  4. Also, in src/lib/mod.rs, mod core; had to be changed to pub mod core;.

I'm not sure why you're using a main.rs file at all here, and I'm skeptical that you need it, but I don't know what your actual use case is. Also, I would probably get rid of the src/lib directory and just have a src/lib.rs file--you appear to be copying the pattern used for modules, but as I understand, lib is not a module, it's a library, and it works differently (for eg. as I mentioned earlier it's not a part of your path).

Also, if you're using a main.rs for a binary, you're going to have to have a fn main() in it, or it'll complain.

My directory structure ended up looking like:

  • Cargo.toml

    [package]
    name = "bench_example"
    version = "0.1.0"
    authors = ["Phoenix Kahlo <kahlo.phoenix@gmail.com>"]
    edition = "2018"
    
    [lib]
    name = "bench_example"
    path = "src/lib/mod.rs"
    
    [[bin]]
    name = "bench_example_bin"
    path = "src/main.rs"
    
    [dependencies]
    bencher = "0.1.5"
    
    [[bench]]
    name = "example"
    harness = false
    
  • src/

    • main.rs

      mod lib;
      
    • lib/

      • mod.rs

        pub mod core;
        
      • core.rs

        struct Foo {}
        
2 Likes

lib.rs and main.rs are special filenames, and should not be included as modules in other files:

  • If it exists, lib.rs (including its submodules) is automatically compiled into a library called your_package_name.

  • If it exists, main.rs (including its submodules) is automatically compiled into a binary. This binary automatically links to your project's library if there is one.

Your project can contain either or both of these files. Code inside your library can be accessed outside of the library using paths that look like your_project_name::foo::bar::baz.

So in your example project, all you need is:

src/lib.rs

pub mod core;

src/core.rs

pub struct Bar {}

benches/example.rs

use bench_example::core::Bar;
// ...

If your project also had a main binary, it would be src/main.rs and it would access Bar in the same way, as bench_example::core::Bar.

1 Like

Thanks a bunch! This works. So basically if I need to benchmark my code, then I need to create a "library" via specifying the [lib] section of the Cargo.toml file. Out of curiosity, then what's the difference between a library and a crate?

A crate is a single compilation unit (that is, the result of a single rustc invocation). Each crate is either a library or a binary.

One cargo package can contain any number of binary crates, and one or zero library crates. Benchmarks and integration tests are special types of binary crates. Any code that is used in multiple crates can go in the library crate, where it will be available to all other crates in the package.

2 Likes

Thanks for the explanation! One last question: then where do we ever use the package name in the Cargo.toml file? Seems like everywhere we just need to use the name of the library crate?

If you have a lib.rs or main.rs file but no corresponding [lib] or [[bin]] section in the manifest, then Cargo will default to using the package name as the crate name for both of those crates.

1 Like