BenchmarkDotNet icon indicating copy to clipboard operation
BenchmarkDotNet copied to clipboard

Simple API for path customization of BenchmarkDotNet.Artifact and job folders

Open AndreyAkinshin opened this issue 7 years ago • 15 comments

Sometimes we have troubles with UnauthorizedAccessException (we don't have access to the current directories) or long paths. I think, it would be great to provide a nice API which allows to customzie BenchmarkDotNet working directory (which will contain all the generated files and folders).

AndreyAkinshin avatar Mar 01 '17 12:03 AndreyAkinshin

which will contain all the generated files and folders

@AndreyAkinshin do you mean only the results (.csc, .html and etc) and logs (.log)? then such change is easy

but if you also mean (.exe, .notcs and .config) then not so easy because today we use benchmark.Target.Type.GetTypeInfo().Assembly.Location where something (like msbuild) has produced the exe which makes assembly loading very easy for us (everything is in the current directory). If we want to produce the .exe somewhere else then most probably we would need to copy some files there or play with the CurrentDirectory of the benchmark process

adamsitnik avatar Apr 19 '17 20:04 adamsitnik

Hmm. In the perfect world, I want to have an ability to specify a folder for all generated files. But we can start with the artifacts folder.

AndreyAkinshin avatar Apr 22 '17 06:04 AndreyAkinshin

I hit this trying to set up some benchmarks in LINQPad. Specifically, it's trying to write to "C:\Program Files (x86)\LINQPad5\BenchmarkDotNet.Artifacts" which is locked down since it's in the "Program Files" folder. I can work around it, but a simple API to direct file writes elsewhere would be helpful.

daveaglick avatar Dec 08 '17 18:12 daveaglick

I hit upon this problem in LINQPad. In lieu of a BenchmarkDotNet API for configuring, I found that setting System.Environment.CurrentDirectory to another existing directory will change the benchmark output location. Below is some general code for setting this to the same location and name as your LINQPad query:

void Main()
{
	Environment.CurrentDirectory = Directory.CreateDirectory(GetBenchmarkOutputPath()).FullName;
	// ...
}

static string GetBenchmarkOutputPath()
{
	var currentQueryDirectory = Path.GetDirectoryName(LINQPad.Util.CurrentQueryPath);
	var currentQueryName = Path.GetFileNameWithoutExtension(LINQPad.Util.CurrentQueryPath);
	var result = Path.Combine(currentQueryDirectory, currentQueryName);
	result += "_" + DateTime.Now.ToString("yyyyMMdd-HHmmss");
	return result;
}

jhorv-scs avatar Dec 30 '17 19:12 jhorv-scs

I am hitting something similar trying to execute a benchmark from a C# script where the underlying generated assembly for the script has no location.

System.AggregateException: One or more errors occurred. (One or more errors occurred. (Object reference not set to an instance of an object.)) (One or more errors occurred. (Object reference not set to an instance of an object.)) (O
bject reference not set to an instance of an object.) (Object reference not set to an instance of an object.) ---> System.NullReferenceException: Object reference not set to an instance of an object.                                 
   at BenchmarkDotNet.Toolchains.DotNetCli.DotNetCliGenerator.GetArtifactsToCleanup(Benchmark benchmark, ArtifactsPaths artifactsPaths)                                                                                                 
   at BenchmarkDotNet.Toolchains.GeneratorBase.GenerateProject(Benchmark benchmark, ILogger logger, String rootArtifactsFolderPath, IConfig config, IResolver resolver)                                                                 
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.Build(Benchmark benchmark, ReadOnlyConfig config, String rootArtifactsFolderPath, Func`2 toolchainProvider, IResolver resolver)                                                       
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.<>c__DisplayClass7_0.<BuildInParallel>b__2(Benchmark benchmark)                                                                                                                       
   at System.Linq.Parallel.PartitionedDataSource`1.ListContiguousIndexRangeEnumerator.MoveNext(T& currentElement, Int32& currentKey)                                                                                                    
   at System.Linq.Parallel.StopAndGoSpoolingTask`2.SpoolingWork()                                                                                                                                                                       
   at System.Linq.Parallel.SpoolingTaskBase.Work()                                                                                                                                                                                      
   at System.Linq.Parallel.QueryTask.BaseWork(Object unused)                                                                                                                                                                            
   at System.Linq.Parallel.QueryTask.<>c.<.cctor>b__10_0(Object o)                                                                                                                                                                      
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)                                                                                                                  
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)                                                                                                                                                         
   --- End of inner exception stack trace ---                                                                                                                                                                                           
   at System.Linq.Parallel.QueryTaskGroupState.QueryEnd(Boolean userInitiatedDispose)                                                                                                                                                   
   at System.Linq.Parallel.SpoolingTask.SpoolStopAndGo[TInputOutput,TIgnoreKey](QueryTaskGroupState groupState, PartitionedStream`2 partitions, SynchronousChannel`1[] channels, TaskScheduler taskScheduler)                           
   at System.Linq.Parallel.DefaultMergeHelper`2.System.Linq.Parallel.IMergeHelper<TInputOutput>.Execute()                                                                                                                               
   at System.Linq.Parallel.MergeExecutor`1.Execute[TKey](PartitionedStream`2 partitions, Boolean ignoreOutput, ParallelMergeOptions options, TaskScheduler taskScheduler, Boolean isOrdered, CancellationState cancellationState, Int32 
queryId)                                                                                                                                                                                                                                
   at System.Linq.Parallel.PartitionedStreamMerger`1.Receive[TKey](PartitionedStream`2 partitionedStream)                                                                                                                               
   at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.GivePartitionedStream(IPartitionedStreamRecipient`1 recipient)                                                                                                
   at System.Linq.Parallel.QueryOperator`1.GetOpenedEnumerator(Nullable`1 mergeOptions, Boolean suppressOrder, Boolean forEffect, QuerySettings querySettings)                                                                          
   at System.Linq.Parallel.QueryOpeningEnumerator`1.OpenQuery()                                                                                                                                                                         
   at System.Linq.Parallel.QueryOpeningEnumerator`1.MoveNext()                                                                                                                                                                          
   at System.Linq.ParallelEnumerable.ToDictionary[TSource,TKey,TElement](ParallelQuery`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)                                                              
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.BuildInParallel(ILogger logger, String rootArtifactsFolderPath, Func`2 toolchainProvider, IResolver resolver, Benchmark[] benchmarks, ReadOnlyConfig config, StartedClock& globalChron
ometer)                                                                                                                                                                                                                                 
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.Run(BenchmarkRunInfo benchmarkRunInfo, ILogger logger, String title, String rootArtifactsFolderPath, Func`2 toolchainProvider, IResolver resolver, List`1 artifactsToCleanup)         
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.Run(BenchmarkRunInfo benchmarkRunInfo, Func`2 toolchainProvider)                                                                                                                      
   at Submission#0.<<Initialize>>d__0.MoveNext() in C:\Github\fresh\dotnet-script\src\Dotnet.Script.Tests\TestFixtures\Issue221\Issue221.csx:line 6                                                                                     
--- End of stack trace from previous location where exception was thrown ---                                                                                                                                                            
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()                                                                                                                                                                    
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)                                                                                                                                    
   at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.<RunSubmissionsAsync>d__9`1.MoveNext()                                                                                                                                      
---> (Inner Exception #0) System.NullReferenceException: Object reference not set to an instance of an object.                                                                                                                          
   at BenchmarkDotNet.Toolchains.DotNetCli.DotNetCliGenerator.GetArtifactsToCleanup(Benchmark benchmark, ArtifactsPaths artifactsPaths)                                                                                                 
   at BenchmarkDotNet.Toolchains.GeneratorBase.GenerateProject(Benchmark benchmark, ILogger logger, String rootArtifactsFolderPath, IConfig config, IResolver resolver)                                                                 
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.Build(Benchmark benchmark, ReadOnlyConfig config, String rootArtifactsFolderPath, Func`2 toolchainProvider, IResolver resolver)                                                       
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.<>c__DisplayClass7_0.<BuildInParallel>b__2(Benchmark benchmark)                                                                                                                       
   at System.Linq.Parallel.PartitionedDataSource`1.ListContiguousIndexRangeEnumerator.MoveNext(T& currentElement, Int32& currentKey)                                                                                                    
   at System.Linq.Parallel.StopAndGoSpoolingTask`2.SpoolingWork()                                                                                                                                                                       
   at System.Linq.Parallel.SpoolingTaskBase.Work()                                                                                                                                                                                      
   at System.Linq.Parallel.QueryTask.BaseWork(Object unused)                                                                                                                                                                            
   at System.Linq.Parallel.QueryTask.<>c.<.cctor>b__10_0(Object o)                                                                                                                                                                      
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)                                                                                                                  
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)<---                                                                                                                                                     
                                                                                                                                                                                                                                        
---> (Inner Exception #1) System.NullReferenceException: Object reference not set to an instance of an object.                                                                                                                          
   at BenchmarkDotNet.Toolchains.DotNetCli.DotNetCliGenerator.GetArtifactsToCleanup(Benchmark benchmark, ArtifactsPaths artifactsPaths)                                                                                                 
   at BenchmarkDotNet.Toolchains.GeneratorBase.GenerateProject(Benchmark benchmark, ILogger logger, String rootArtifactsFolderPath, IConfig config, IResolver resolver)                                                                 
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.Build(Benchmark benchmark, ReadOnlyConfig config, String rootArtifactsFolderPath, Func`2 toolchainProvider, IResolver resolver)                                                       
   at BenchmarkDotNet.Running.BenchmarkRunnerCore.<>c__DisplayClass7_0.<BuildInParallel>b__2(Benchmark benchmark)                                                                                                                       
   at System.Linq.Parallel.PartitionedDataSource`1.ListContiguousIndexRangeEnumerator.MoveNext(T& currentElement, Int32& currentKey)                                                                                                    
   at System.Linq.Parallel.StopAndGoSpoolingTask`2.SpoolingWork()                                                                                                                                                                       
   at System.Linq.Parallel.SpoolingTaskBase.Work()                                                                                                                                                                                      
   at System.Linq.Parallel.QueryTask.BaseWork(Object unused)                                                                                                                                                                            
   at System.Linq.Parallel.QueryTask.<>c.<.cctor>b__10_0(Object o)                                                                                                                                                                      
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)                                                                                                                  
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)<---                                                                                                                                                     

seesharper avatar Feb 02 '18 13:02 seesharper

I would love to be able to customize the BenchmarkDotNet.Artifacts path not just by an API but as a CLI option. Something like:

dotnet run -c Release benchmark1.dll artifacts=./master-regex-list

That would allow me to run a benchmark multiple times without overriding the results.

ViktorHofer avatar Feb 13 '18 23:02 ViktorHofer

@ViktorHofer Done! As soon as this build is over, you should be able to download 0.10.12.430 version with this new feature from our CI feed.

<packageSources>
  <add key="bdn-nightly" value="https://ci.appveyor.com/nuget/benchmarkdotnet" />
</packageSources>
[ArtifactsPath(@"C:\here")]
[DryJob]
public class Simple
{
    [Benchmark]public void Nothig() { }
}

or as suggested via the command line: dotnet run -c Release -f net46 -- --artifacts="C:\AdamTest" --class=Cpu_Atomics

or via the ConfigExtensions: config.WithArtifactsPath(thePath)

adamsitnik avatar Feb 14 '18 15:02 adamsitnik

Adam you are fast!! Thanks a lot! Happy to have you on our team 👍

ViktorHofer avatar Feb 14 '18 15:02 ViktorHofer

@ViktorHofer thanks! I will contact you soon about local Core CLR/FX builds

adamsitnik avatar Feb 14 '18 15:02 adamsitnik

@adamsitnik is there a way to run against in-memory assemblies though?

filipw avatar Feb 16 '18 07:02 filipw

@filipw hi Filip! You should try our InProcessToolchain which does not compile and spawn new processes but instead generates new types using IL Emit. Here is a good description of it

adamsitnik avatar Feb 16 '18 16:02 adamsitnik

Hi @adamsitnik , I am using BenchmarkDotNet in Azure Function. When I am triggering this function I am getting an error that tells me "Access to the path '........\BenchmarkDotNet.Artifacts\results' is denied" Can I disable the writing to the directory?

isabr85 avatar Nov 02 '20 10:11 isabr85

Hi @isabr85

To disable all exporters you need to define a config with no exporters. This should work:

var noExporters = ManualConfig.CreateEmpty()
                .AddColumnProvider(DefaultColumnProviders.Instance)
                .AddLogger(ConsoleLogger.Default);

and pass it to BenchmarkRunner|Switcher

adamsitnik avatar Nov 02 '20 11:11 adamsitnik

Is this complete, or am I missing something? It looks like the API was added a long time ago. [Edit] It's the working directory that we're missing?

timcassell avatar Jul 24 '23 08:07 timcassell

Should we try to place all generated files in the artifacts directory? So we'd have

> BenchmarkDotNet.Artifacts
  > build
  > logs
  > results

timcassell avatar Jul 24 '23 11:07 timcassell