How to benchmark two different versions of the same non-NuGet library in BenchmarkDotNet? - benchmarkdotnet

I'm trying to use BenchmarkDotNet to prepare performance regression testing for a library. This requires that I compare the same tests with an older (stable) version of the library. Now, there's an option to give different versions of a NuGet package to a job. There doesn't seem to be an option to reference different assemblies instead.
I've tried custom build configurations:
<ItemGroup Condition="'$(Configuration)' == 'Baseline'">
<Reference Include="MyAssembly">
<HintPath>lib\baseline\MyAssembly.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' != 'Baseline'">
<Reference Include="MyAssembly">
<HintPath>lib\current\MyAssembly.dll</HintPath>
</Reference>
</ItemGroup>
but when trying to use those configurations from BenchmarkDotNet via
public static void Main(string[] args) {
var summary = BenchmarkRunner.Run(typeof(Program).Assembly,
DefaultConfig.Instance
.With(Job.Default.WithCustomBuildConfiguration("Baseline"))
.With(Job.Default.WithCustomBuildConfiguration("Current")));
}
I get build errors that indicate that the assembly is not referenced at all. BenchmarkDotNet also helpfully clears up any temporary artifacts it creates so I can't even look at the generated project file to figure out how that looked.
Would the only workaround here be to wrap the library in a NuGet package? Or is there something I'm overlooking in the (for this case seemingly sparse) docs?
This issue seems to be vaguely related to the build errors I'm getting.

I was able to get something similar working, which may also work for you. In my case, I wanted to reference both the NuGet and the local project from the same build of the benchmark project.
After adding both the PackageReference and the ProjectReference, I (manually) added the Aliases to the ProjectReference:
<ItemGroup>
<ProjectReference Include="..\src\Combinatorics\Combinatorics.csproj" Aliases="localbuild" />
</ItemGroup>
The dll names do need to be different (since both dlls end up in the output directory). So I changed the dll name for the referenced project (but only when building locally and not when doing a continuous integration build):
<PropertyGroup>
<AssemblyName Condition="'$(CI)'!='true'">local.Combinatorics</AssemblyName>
</PropertyGroup>
Then I could use extern alias and using to use both assemblies in the same app:
extern alias localbuild;
using NugetCombinatorics = Combinatorics.Collections;
using LocalCombinatorics = localbuild::Combinatorics.Collections;
...
[BenchmarkCategory("Enumerate"), Benchmark(Baseline = true)]
public void EnumerateOld()
{
var permutations = new NugetCombinatorics.Permutations<int>(_source);
foreach (var p in permutations)
;
}
[BenchmarkCategory("Enumerate"), Benchmark]
public void EnumerateNew()
{
var permutations = new LocalCombinatorics.Permutations<int>(_source);
foreach (var p in permutations)
;
}
The duplicated code is not ideal, and I'm kinda abusing categories instead of using proper runs, but it gets the job done.

Related

How to use command-line options for F# within a .NET Core project?

I need to use one of the reserved words, sig, in my F# code, in a .NET Core project. As the manual states:
If you use the --mlcompatibility compiler option, the above keywords are available for use as identifiers.
The compilation environment can also be controlled by setting the project properties. For projects targeting .NET Core, the "Other flags" property, ... in .fsproj, is used for specifying extra command-line options.
In trying to do so, I successfully crashed my project.
How does one put command-line options into the .NET Core project?
TIA.
On the part where you had trouble with the Other flags - it needs to go into a/the PropertyGroup. Here is an example for the default console template:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<OtherFlags>$(OtherFlags) --mlcompatibility</OtherFlags>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
Sadly this will not resolve your main point - using sig as an identifier will still be an error (the option did just remove warnings)
See the discussion in How do I compile F# code with ML compatibility?
if you really want to use the identifier you could try this little trick:
let ``sig`` = "F#"
printfn "Hello world %s" ``sig``

Running MSBuild custom tasks and MSBuildRuntimeType difficulties

Main strategy:
Here we have a library NugLibEnforceCore, which contains a custom MSBuild task and we will put it in a folder: The solution folder.libs
First step is to build this library and put it in the proper folder.
Then we may try to build the Consumer project, which has build.targets and will try to run that custom tasks.
Projects and tasks are the bare minimum needed things for what expected and they don't have anything special.
The .targets file in the Consumer project:
<PropertyGroup>
<LibFolder>$(SolutionDir).libs</LibFolder>
<!-- <LibFolderPart2 Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.1\NugLibEnforceCore.dll</LibFolderPart2> -->
<LibFolderPart2 Condition="'$(MSBuildRuntimeType)' == 'Core'">NugLibEnforceCore.dll</LibFolderPart2>
<LibFolderPart2 Condition="'$(MSBuildRuntimeType)' != 'Core'">net472\NugLibEnforceCore.dll</LibFolderPart2>
</PropertyGroup>
<UsingTask
TaskName="TestTask"
AssemblyFile="$(LibFolder)\$(LibFolderPart2)"
/>
<Target Name="DoTheTask" AfterTargets="Build">
<Message Text="=== Trying TestTask ===" Importance="high" />
<TestTask />
<Message Text=" == TestTask is finished" Importance="high" />
</Target>
Here I share the issues and some difficulties I experienced and the approaches I tried related to the topic.
Tried to reproduce an issue which was irritating and happened time to time for me.
I was able to use custom tasks, but want to find the reasons and solve this issue forever.
1. Approach 1: ALL based on .net core - Build via Visual Studio Build Menu
get this error:
Error MSB4062
The "TestTask" task could not be loaded from the assembly
...\MSBuild13 Enforce Core\.libs\net472\WeRTheBest.NugLibEnforceCore.dll.
Could not load file or assembly 'file:///...\MSBuild13 Enforce Core\.libs\net472\WeRTheBest.NugLibEnforceCore.dll' or one of its dependencies.
The system cannot find the file specified.
Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask. Consumer ...\MSBuild13 Enforce Core\Consumer\build\build.targets 27
2. Approach 2: ALL based on .net core - Build via dotnet msbuild command
dotnet msbuild NugLibEnforceCore\NugLibEnforceCore.csproj
get this error:
Error:
=== Trying TestTask ===
...\MSBuild13 Enforce Core\Consumer\build\build.targets(27,5):
error MSB4062: The "TestTask" task could not be loaded from the assembly
...\MSBuild13 Enforce Core\Consumer\build\.libs\netstandard2.1\NugLibEnforceCore.dll.
Could not load file or assembly '...\MSBuild13 Enforce Core\Consumer\build\.libs\netstandard2.1\NugLibEnforceCore.dll'.
The system cannot find the path specified.
Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
...\MSBuild13 Enforce Core\Consumer\Consumer.csproj]
3. Approach 3: Multi-target (.Net Core & .Net Framework) - Build via Visual Studio Build Menu:
will cause other difficulties, which I may bring more details on the next steps here if needed or as we continue this discussion.
Brief:
In the first approach when the project is based on ".net standard" why MSBuildRuntimeType is trying to take net472 library?!
In the second approach, it can't understand the same path used in the first approach and is looking for:
...\[Solution]\Consumer\build\.libs\netstandard2.1\NugLibEnforceCore.dll
instead of
...\[Solution]\.libs\netstandard2.1\NugLibEnforceCore.dll'
In the last build I commented/removed "netstandard2.1" for easier builds, cause it is currently a single-target assembly.
We know that the files are in the proper location, you can also put the library manually and take a test if needed, the only thing that matters is be able to run the consumer project's tasks without any issues and worry.
I can share videos about it if needed.
The answers briefly are:
Build and run custom tasks under visual studio doesn't support .net core, so it is looking for .net framework versions of your libraries.
There is no support for getting solution folder in MSBuild.
Some references:
ref 1
ref 2
ref 3

Why is Visual Studio telling me I need to reference System.Private.CoreLib?

I'm trying out EF Core for the first time and have coded a very simple MVC app to get my feet wet. I am using a method for seeding the database found in the UnicornStore project where they write some code in Startup.cs to migrate the database and then run a seed method.
Before they call the seed method, they run this DbContext extension method to check if all migrations have been applied:
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace UnicornStore.Models
{
public static class DbContextExtensions
{
public static bool AllMigrationsApplied(this DbContext context)
{
var applied = context.GetService<IHistoryRepository>()
.GetAppliedMigrations()
.Select(m => m.MigrationId);
var total = context.GetService<IMigrationsAssembly>()
.Migrations
.Select(m => m.Key);
return !total.Except(applied).Any();
}
}
}
I've put this same method in my application and everything works -- the code compiles and the database is migrated and seeded. However, Visual Studio (2017 Enterprise) is red underlining this line:
context.GetService<IMigrationsAssembly>()
.Migrations
.Select(m => m.Key);
If I hover over the red line, it tells me:
Module 'System.Private.CoreLib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=foo' should be referenced
Can anyone tell me why I am getting this message? I actually tried to add a reference to System.Private.CoreLib to see what would happen, and that caused a ton of errors (undefined System.Object, etc). I'm never comfortable leaving things like this unresolved in case they come back to bite me later, so any resolution (or a confirmation that I can leave this be and ignore the message) would be appreciated!
Do you have R# installed? You might have been hitting this issue: RSRP-464676
If so, try suspending R# and see if the issues are not shown anymore.
Just as an alternate response, it seems as if my happy-go-lucky alt-enter spam led me to import an assembly reference to System.Private.CoreLib, which I think was from a threading namespace import fix. Check to see whether this has been referenced.
I had the same problem. Upgrading to the latest version of Resharper fixed the issue.
Had a similar problem in VS Code (vscode) with SAFE-Stack's server template code. The issue appears resolved by adding <Reference include="netstandard" /> to the associated fsproj project file sugggested on github here as follows:
<Project Sdk="Microsoft.NET.Sdk.Web">
..
<ItemGroup>
..
<Reference include="netstandard" />
..
</ItemGroup>
</Project>

Premake directive to embed a static library into a static library

First, I'm using premake5 and VisualStudio 2013. Please don't jump on me for that one (since premake5 is still alpha). I have already searched and can't find an answer, not one that actually works.
I need to embed a static library into a custom static library using premake. I'm converting some older code after updating the libraries and only want to link the custom library with the application. I don't want to have to link the custom library and all the other libraries it deals with to the application configuration. Everything else with premake5 is working swimmingly.
I can get it to link to the application fine using the link directive, example:
links { "SDL2", "SDL2main", "SDL2_image" }
This works fine for normal inclusion of static libraries but I need a way to embed say these example libraries into a custom static library.
I can take the resulting project files that premake creates into visual studio and manually modify the project to add the libraries the way I need to, but I don't want to have to do that every time I need to regenerate the project.
The result I need is a way for premake to generate the following XML into the project xml files:
<ItemGroup>
<Library Include="..\deps\SDL2\lib\x86\SDL2.lib">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Win64|x64'">true</ExcludedFromBuild>
</Library>
<Library Include="..\deps\SDL2\lib\x86\SDL2main.lib">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Win64|x64'">true</ExcludedFromBuild>
</Library>
<Library Include="..\deps\SDL2_image\lib\x86\SDL2_image.lib" />
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Win64|x64'">true</ExcludedFromBuild>
</Library>
</ItemGroup>
This is the result of manually editing the project in visual studio. I added the exclusion of the libraries from an x64 build because ultimately, I have to use x64 versions of these libraries as well. It would be great if I have seriously overlooked something but I need to be able to get this to work.
Did I overlook something really simple here?
Premake doesn't support linking static libraries together out of the box, as that behavior isn't portable to other toolsets. However, you can install a small extension in your project script to make it work:
---
-- Allow static library projects to link in their dependencies. Visual Studio
-- doesn't support this out of the box, so I need to manually add items to
-- the list of additional dependencies.
---
local p = premake
function myAdditionalStaticDependencies(cfg)
local links = p.config.getlinks(cfg, "siblings", "fullpath")
if #links > 0 then
links = path.translate(table.concat(links, ";"))
p.x('<AdditionalDependencies>%s;%%(AdditionalDependencies)</AdditionalDependencies>', links)
end
end
p.override(p.vstudio.vc2010.elements, "lib", function(base, cfg, explicit)
local calls = base(cfg, explicit)
if cfg.kind == p.STATICLIB then
table.insert(calls, myAdditionalStaticDependencies)
end
return calls
end)

Use attribute to omit code from coverage analysis in Visual Studio

I have some classes that, for one reason or another, cannot be or need not be unit tested. I'd like to exclude these classes from my coverage metrics so that I can get a better feel for the coverage on the classes I actually care about. Right now I have to exclude the results after the fact. What I would like to do is use an attribute to mark those classes as excluded so that they aren't included to begin with. Is there any way to decorate a class with an attribute that will automatically exclude it from coverage analysis? Either VS coverage analysis or nCover will work.
FWIW, these are classes that I can assure myself by inspection that the code is correct. Mostly they are wrapper classes around existing framework classes that I've introduced in order to be able to mock the framework class out. Since the wrapper's get mocked out they don't get tested. That's ok because all they do is wrap the framework class' methods that I care about.
Starting with VS2010 we have ExcludeFromCodeCoverageAttribute. Commenters have noted this to work in NCover as well as DotCover + NUnit. Example usage:
[ExcludeFromCodeCoverage]
public class myUntestableClass
{ }
Also see this link. They suggest using VSInstr as command line tool, it have /EXCLUDE options (it's not as handy).
I've found some information on a couple of Diagnostics attributes DebuggerNonUserCodeAttribute and DebuggerHiddenAttribute that indicates that using these attributes will cause the coverage analyzer in VS to leave these out of its results. I've tried it with the DebuggerNonUserCodeAttribute and it seems to work. I can probably live with this for most of the classes that I'm thinking of, though I don't like the side effect of not being able to step into these classes. That shouldn't be a problem for the wrapper classes, but it may end up being so with classes that are inherently hard to test and I need debugger access to.
I'm still looking for alternatives, though.
With NCover you can create an attribute and then tell NCover to ignore that attribute.
In our projects, we have defined this attribute (no namespace, so it is easy to use):
public class CoverageExcludeAttribute : Attribute { }
We use NAnt, so we have a target that looks like this:
<target name="unittests" description="run nunit tests" >
<ncover
....
excludeAttributes="CoverageExcludeAttribute"
/>
</target>
Question 9 in the NCover FAQ describes this method. We based our solution on this.
Alternatively, you can use the exclude feature of the NCoverExplorer to exclude namespaces and assemblies from the final report. This merely removes the data from the report, but the end result is the same.
We use both techniques.
This work for me! 👍
use in .csproj: sonar keys
<ItemGroup>
<SonarQubeSetting Include="sonar.issue.ignore.allfile">
<Value>ExcludeFromCodeCoverage</Value>
</SonarQubeSetting>
</ItemGroup>
or
<ItemGroup>
<SonarQubeSetting Include="sonar.coverage.exclusions">
<Value>**/FileClassToExclude.cs</Value>
</SonarQubeSetting>
</ItemGroup>
And then use in your class file .cs by ExcludeFromCodeCoverage from microsoft
[ExcludeFromCodeCoverage]
public class ExcludeMeFromSonarCoverage
{
public ExcludeMeFromSonarCoverage()
{
}
}

Resources