As we know, the Rust compiler has two (or more?) modes for compilation: one for development, which compiles faster, and one for release, which takes longer but is more optimized and typically runs much faster.
Can the code itself detect how it has been compiled?
In general, no. There are a number of different knobs at a lower level that determine the optimization level of code (opt-level goes from 0-3, and it's not the only factor).
There is, for some purposes, the cfg(debug_assertions) which is by default active in the dev profile but not the release profile. This is intended to control optional (not for safety purposes) runtime checks. It's controllable separately from opt-level though.
A just for fun, but should totally not be used in anything real, answer: maybe?
fn is_release_mode() -> bool {
let start = std::time::Instant::now();
for _ in 0..10000000 {
()
}
start.elapsed().as_millis() < 10
}
fn main() {
if is_release_mode() {
println!("release");
} else {
println!("debug");
}
}
$ cargo run
Compiling whichmode v0.1.0 (/home/tristan/projects/whiteboard/whichmode)
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
Running `target/debug/whichmode`
debug
$ cargo run --release
Compiling whichmode v0.1.0 (/home/tristan/projects/whiteboard/whichmode)
Finished release [optimized] target(s) in 0.17s
Running `target/release/whichmode`
release
Works because the optimized version will remove the loop completely so it takes no time, but in debug mode the loop is run completely. Obviously the number of zeros in the loop might need to be adjusted based on the system you're running on so that it both doesn't take too long but also takes long enough in debug mode. Please don't use this!
I was thinking of something like that. The obvious drawback is that this behavior is not guaranteed anywhere. Maybe some future version of the compiler will optimize the loop away even in development mode (I was actually expecting it would already).
Maybe I can integrate some vague benchmarks into my app and make it complain if the benchmarks look unusually bad, citing lack of optimization as one possible reason.
You can use a different opt-level for your dev profile. For example, we use it in RustCrypto since execution of some tests is too slow without optimizations.
You can use a build script to halt compilation if the OPT_LEVEL environment variable is 0. This way at least nobody could accidentally compile it without optimizations. A build script can also output environment variables that the program being compiled can access with the env! macro and store in a static/const that could then be checked at runtime.
OK, so this is an XY problem and you don't actually want to detect whether it's compiled in one way or another but whether its running time seems reasonable.
I suggest instead of trying to account for every possible constellation of compiler flags ever™, you should just build something like a watchdog mechanism into the application, which alerts the user if it's been running for some amount of time without making enough progress.
I don't know what your program is specifically doing, but to give you an idea: you could e.g. measure how long it takes to chew through, say, 1GB of data. If that takes e.g. more than twice more than warranted on mainstream modern hardware (or whatever you expect people to run the executable on), then you could issue a warning.
An additional benefit of this approach is that pretty much by construction, it accounts for all sources of slowness. You could print "this looks too slow – maybe try running in release mode and/or buy more silicon", for example.
Incidentally, you should distribute your program as a proper cargo package and simply instruct your users to cargo install my_program (as always). That always builds in release mode without the need for any explicit manual flags, so people will not accidentally compile the code without optimizations, then.