Hi!
It's very easy to run benchmark on an assembly built as debug. Sometimes it is good and sometimes definitely not. I've learned that hard way as I just spent some time just to figure out our CI server uses debug builds for our perftests:)
So, I propose the following feature to be included as opt-in:
To define what "debug" assembly mean: it's an assembly with
[assembly:Debuggable(DebuggableAttribute.DebuggingModes.DisableOptimizations)]
attribute applied.
The API for the feature may look like this (names are preliminary)
// What to do on debug build
enum DebugBuildBehavior
{
Run = 0, // Run as usual
Default = Run, // The same, used by default
FailsWithException = 1, // BenchmarkRunner.Run() throws an exception
DoesNotRun = 2 // Warning is logged, benchmark exits
}
Also, if DebugBuildBehavior is set to FailsWithException or DoesNotRun and the benchmark assembly is not debug one additional check is performed:
all assemblies being loaded during benchmark run should be checked too (using the AppDomain.AssemblyLoad event to hook it up).
The DebugBuildBehavior enum should be exposed as a property of the IConfig interface so it can be setup individually for each benchmark.
Thanks!:)
Very good point! I agree with you, we should not let the users use non-optimized dlls.
We already preload all dlls to check their target .NET version and build references for csproj so perhaps we could just extend this with the optimization check?
But I am not sure for the default behaviour. Personally I think that we should throw exception and stop. @AndreyAkinshin @mattwarren what do you think?
I think, we should throw an exception with a message like "if you really want to run it in DEBUG, do the following ..."
@AndreyAkinshin
Also, should the runner throw the exception if the code is running under debugger?
*If you wonder how do you debug the benchmark, there's VS extension for child process debugging.
Don't sure about an exception. It would be cool to add a warning.
I think, if somebody try to debug a child benchmark process, then he should understand what he is doing.
@AndreyAkinshin
Looks like I've asked it wrong:)
Ok, one more time (sorry:))
You're proposing that the exception for debug builds should be thrown by default.
My question is, should the exception be thrown when the code is being run under debugger?
The question really should be whether JIT optimizations are enabled, because that's what makes most of the difference between optimized and non-optimized builds. And, by default, there won't be JIT optimizations when running under a debugger.
@goldshtn
Agreed. Failing even if debugger attached makes the debugging slightly harder, but I don't think that this is a real issue.
All in all, the code from benchmark always can be run (and debugged) directly, without using the BenchmarkRunner.
Ok. I have implemented this feature. How it works:
Add(Analyzers.JitOptimizationsAnalyser.Instance);@ig-sinicyn could please let me know if this satisfies you as a user?
@adamsitnik
If the flag is set to false I display warning
Thanks!
There're two drawbacks, however.
Summary class. And xUnit (AFAIK) does not support warning assertions at all.@ig-sinicyn Thanks for very usefull comments. I will add Warnings to summary and somehow extend IAnalyser to allow prevalidation and post verification
@adamsitnik great news!
And thanks for exposing the warnings! I'll try to pass these as NUnit warnings when done. There's no documented way to do this, however the NUnit's IgnoreException is threated as a warning.
@ig-sinicyn @AndreyAkinshin @mattwarren
I have extended our current features with new thing: Validators
public interface IValidator
{
IEnumerable<IValidationError> Validate(IList<Benchmark> benchmarks);
}
public interface IValidationError
{
bool IsCritical { get; }
string Message { get; }
Benchmark Benchmark { get; }
}
Validators are executed before running benchmarks. There is only one mandatory validator: BaselineValidator (checks wether class does not have more than one Baseline = true method). Summary has been extended and contains list of found errors. If any of the errors is critical, then none of the Benchmarks is executed and export is also ommited.
@ig-sinicyn In your case if you would like to fail on error when any of the dlls that defines benchmark was not optimized you can use Add(JitOptimizationsValidator.FailOnError); in your config. Please let me know if it fixes the problem
@adamsitnik Wow! That's much better than my original proposal, thumbs up.
Please let me know if it fixes the problem
It definitely does:)