Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to get benchmarks running from LINQPad? #580

Closed
michael-wolfenden opened this issue Nov 14, 2017 · 15 comments
Closed

How to get benchmarks running from LINQPad? #580

michael-wolfenden opened this issue Nov 14, 2017 · 15 comments

Comments

@michael-wolfenden
Copy link

Running the following code in LINQPad5 v5.25.00 referencing BenchmarkDotNet 0.10.10.339

void Main()
{
    var summary = BenchmarkRunner.Run<Md5VsSha256>();
}

public class Md5VsSha256
{
    private const int N = 10000;
    private readonly byte[] data;

    private readonly SHA256 sha256 = SHA256.Create();
    private readonly MD5 md5 = MD5.Create();

    public Md5VsSha256()
    {
        data = new byte[N];
        new Random(42).NextBytes(data);
    }

    [Benchmark]
    public byte[] Sha256() => sha256.ComputeHash(data);

    [Benchmark]
    public byte[] Md5() => md5.ComputeHash(data);
}

Outputs the following and does not run

// ***** BenchmarkRunner: Start   *****
// Found benchmarks:
//   Md5VsSha256.Sha256: DefaultJob
//   Md5VsSha256.Md5: DefaultJob

// Validating benchmarks:
Assembly LINQPadQuery which defines benchmarks references non-optimized LINQPad
Assembly LINQPadQuery which defines benchmarks is non-optimized
Benchmark was built without optimization enabled (most probably a DEBUG configuration). Please, build it in RELEASE.
Assembly LINQPadQuery, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null is located in temp. If you are running benchmarks from xUnit you need to disable shadow copy. It's not supported by design.
// * Artifacts cleanup *

This appears to be a regression as the same script referencing BenchmarkDotNet 0.10.8.246 outputs

// ***** BenchmarkRunner: Start   *****
// Found benchmarks:
//   Md5VsSha256.Sha256: DefaultJob
//   Md5VsSha256.Md5: DefaultJob

// Validating benchmarks:
Assembly LINQPadQuery which defines benchmarks references non-optimized LINQPad
Assembly LINQPadQuery which defines benchmarks is non-optimized
// **************************
// Benchmark: Md5VsSha256.Sha256: DefaultJob
// *** Generate *** 
// Result = Success
// BinariesDirectoryPath = C:\Users\WOLFENDEN-PC\AppData\Local\Temp\LINQPad5\_vhnggtqv\txnrcv

// *** Build ***
BuildScript: C:\Users\WOLFENDEN-PC\AppData\Local\Temp\LINQPad5\_vhnggtqv\txnrcv\67c72de2-c073-4a54-9515-f24267bef6e7.bat
// Result = Success

// *** Execute ***
// Launch: 1 / 1
// Execute: C:\Users\WOLFENDEN-PC\AppData\Local\Temp\LINQPad5\_vhnggtqv\txnrcv\67c72de2-c073-4a54-9515-f24267bef6e7.exe 
.....
.....
.....
@AndreyAkinshin
Copy link
Member

It's not a bug, it's a feature. We have decided that BenchmarkDotNet shouldn't allow running benchmarks in DEBUG. Why do you want to run your benchmark without optimizations?
See also: #561
/cc @adamsitnik

@michael-wolfenden
Copy link
Author

So I have set the "compile with /optimize+" setting in linqpad, which from my understanding is the equivalent of release mode, and it still doesn't run

// ***** BenchmarkRunner: Start   *****
// Found benchmarks:
//   Md5VsSha256.Sha256: DefaultJob
//   Md5VsSha256.Md5: DefaultJob

// Validating benchmarks:
Assembly LINQPadQuery which defines benchmarks references non-optimized LINQPad
Assembly LINQPadQuery, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null is located in temp. If you are running benchmarks from xUnit you need to disable shadow copy. It's not supported by design.
// * Artifacts cleanup *

Or am I misunderstanding and the issue is that linqpad itself is not optimized?

@adamsitnik
Copy link
Member

Hello @michael-wolfenden

Thanks for reporting your issue. I have just pushed a commit with the fix (we detect that user is using LINQPad and has optimizations disabled and give nice error message).

However as you have noticed LINQPad itself is non-optimized. To overcome this problem please use custom config with our policy disabled:

public class AllowNonOptimized : ManualConfig
{
    public AllowNonOptimized()
    {
        Add(JitOptimizationsValidator.DontFailOnError); // ALLOW NON-OPTIMIZED DLLS

        Add(DefaultConfig.Instance.GetLoggers().ToArray()); // manual config has no loggers by default
        Add(DefaultConfig.Instance.GetExporters().ToArray()); // manual config has no exporters by default
        Add(DefaultConfig.Instance.GetColumnProviders().ToArray()); // manual config has no columns by default
    }
}

BenchmarkRunner<YourType>(new AllowNonOptimized());

@adamsitnik
Copy link
Member

adamsitnik commented Nov 18, 2017

@albahari is there any reason why LINQPad is non-optimized? It would make our user experience much better if it was optimized. You can use following settings in your csproj to have nice symbol information in pdb and optimizations enabled for Release:

<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>

@adamsitnik adamsitnik self-assigned this Nov 18, 2017
@adamsitnik adamsitnik added this to the v0.10.11 milestone Nov 18, 2017
@adamsitnik adamsitnik changed the title Linqpad support no longer works in the latest version (0.10.10.339) How to get benchmarks running from LINQPad? Nov 18, 2017
@albahari
Copy link
Contributor

Because the performance-critical libraries that LINQPad uses are optimized (libraries such as Roslyn & ActiPro's syntax editor), there's not much to gain in optimizing LINQPad itself. And there's a lot to lose - namely, the quality of the diagnostic information when an error occurs. Just recently, I've diagnosed an intermittent hang based on a stack trace which might have been compromised with optimizations enabled. I think users would rather have better reliability and quicker bug fixes than the small perf gain that optimizations would yield in this situation.

In the case of BenchmarkDotNet, the message about LINQPad not being optimized is a false alarm, because LINQPad's non-optimized code should not find its way into the code paths being tested (unless you're performance-testing features of LINQPad itself - such as LINQPad's Dump or Dif methods). LINQPad is like the IDE, so it shouldn't really be counted in this scenario.

Could I can add an assembly attribute to LINQPad to signal this? Obviously I can't take a dependency on BenchmarkDotNet, but I could define an attribute class like the following in LINQPad, and you could detect it by looking at the full type name:

namespace BenchmarkDotNet {
   [Serializable, AttributeUsageAttribute (AttributeTargets.Assembly)]
   class AllowNonoptimized : System.Attribute { }
}

@adamsitnik
Copy link
Member

@albahari thanks for the explanation!

I have added LINQPad to our "white list", so everything should work fine now.

@ZodmanPerth
Copy link

@adamsitnik Will this require a new release of BenchmarkDotNet or has it already made it into a release? I've just moved to a new device and had to employ the custom config workaround (which in my case is not a big deal; this is more of a notification type question).

@adamsitnik
Copy link
Member

@ZodmanPerth hi! It will be a part of 0.10.11.

@AndreyAkinshin could you trigger our CI build? It has failed for some magical reason

@ZodmanPerth as soon as our CI builds the package, it should be available at

<packageSources>
  <add key="bdn-nightly" value="https://ci.appveyor.com/nuget/benchmarkdotnet" />
</packageSources>

@AndreyAkinshin
Copy link
Member

@adamsitnik, done.

@ZodmanPerth
Copy link

Thanks.
There was a minor update at that address (0.10.10.343) dated November 18. It doesn't appear to be the build I'm after, but just in case here's part of the report I get:
Assembly LINQPadQuery which defines benchmarks references non-optimized LINQPad If you own this dependency, please, build it in RELEASE. If you don't, you can create custom config with DontFailOnError to disable our custom policy and allow this benchmark to run. Assembly LINQPadQuery, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null is located in temp. If you are running benchmarks from xUnit you need to disable shadow copy. It's not supported by design.
I'll check back later for 0.10.11.

@adamsitnik
Copy link
Member

Hi @ZodmanPerth

Our CI had some issues. Now we publish NuGet package again ;) You can try 0.10.10.350 from our CI feed:

<packageSources>
  <add key="bdn-nightly" value="https://ci.appveyor.com/nuget/benchmarkdotnet" />
</packageSources>

@ZodmanPerth
Copy link

Thanks @adamsitnik . By the time I was able to take action on this the nightly build was up to 0.10.10.353, which I confirm does have the fix in it. Good job!

@adamsitnik
Copy link
Member

@ZodmanPerth thanks for the confirmation!

@ImaginaryDevelopment
Copy link

Hello @michael-wolfenden

Thanks for reporting your issue. I have just pushed a commit with the fix (we detect that user is using LINQPad and has optimizations disabled and give nice error message).

However as you have noticed LINQPad itself is non-optimized. To overcome this problem please use custom config with our policy disabled:

public class AllowNonOptimized : ManualConfig
{
    public AllowNonOptimized()
    {
        Add(JitOptimizationsValidator.DontFailOnError); // ALLOW NON-OPTIMIZED DLLS

        Add(DefaultConfig.Instance.GetLoggers().ToArray()); // manual config has no loggers by default
        Add(DefaultConfig.Instance.GetExporters().ToArray()); // manual config has no exporters by default
        Add(DefaultConfig.Instance.GetColumnProviders().ToArray()); // manual config has no columns by default
    }
}

BenchmarkRunner<YourType>(new AllowNonOptimized());

Please include usings/opens in examples.
where is JitOptimizationsValidator? I can't seem to find it, is any of this manual config still necessary or useful?

@albahari
Copy link
Contributor

albahari commented Oct 29, 2022

JitOptimizationsValidator is in BenchmarkDotNet.Validators; however it's not necessary for LINQPad.

No special configuration is required to run BenchmarkDotNet in LINQPad other than to ensure that your query itself is optimized, which you can do either via the LINQPad UI or with the following directive:

#LINQPad optimize+

(If you forget, you'll get a message telling you specifically to add this directive.)

Also note that recent LINQPad betas (7.5.x) have direct support for BenchmarkDotNet. You no longer need to reference BenchmarkDotNet, nor add any directives or benchmarking attributes, nor call the Benchmark runner (although these things are still supported). Instead, just highlight the statements you wish to benchmark (or a group of methods) and press Ctrl+Shift+B. A realtime visualization appears as benchmarking takes place:

image

More info is in LINQPad's built-in samples. Press Ctrl+F1 to search the samples and type 'benchmark'. LINQPad 7.5 will go RTM within the next few weeks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants