Skip to content

ArgumentsSource method is executed before GlobalSetup method #1408

Open
@drieseng

Description

@drieseng

In BenchmarkDotNet version 0.12, the method that provided arguments for a parameterized benchmark is executed before the GlobalSetup method of the benchmark.

Having the designated GlobalSetup method execute before the ArgumentsSource method is useful when the arguments are costly to create, and - for example - need to be disposed once the benchmark has completed.

Example:

using BenchmarkDotNet.Attributes;
using System;
using System.Collections.Generic;
using System.IO;

public class ArgumentsSourceBenchmarks
{
    private List<DownloadFile> _downloadFiles;
    private StreamProvider _streamProvider;

    [GlobalSetup]
    public void GlobalSetup()
    {
        _streamProvider = new StreamProvider();
        _downloadFiles = new List<DownloadFile>
            {
                new DownloadFile("A", _streamProvider.Create(2048)),
                new DownloadFile("B", _streamProvider.Create(4096))
            };
    }

    [GlobalCleanup]
    public void GlobalCleanup()
    {
        _streamProvider?.Dispose();

        foreach (var downloadFile in _downloadFiles)
        {
            downloadFile.DownloadStream.Dispose();
        }
    }

    [Benchmark]
    [ArgumentsSource(nameof(DownloadFileArguments))]
    public void DownloadFileBenchmark(string path, Stream stream)
    {
        // Do something
    }

    public IEnumerable<object[]> DownloadFileArguments()
    {
        foreach (var downloadFile in _downloadFiles)
        {
            yield return new object[] { downloadFile.Path, downloadFile.DownloadStream };
        }
    }

    public class DownloadFile
    {
        public DownloadFile(string path, Stream downloadStream)
        {
            Path = path;
            DownloadStream = downloadStream;
        }

        public string Path { get; }
        public Stream DownloadStream { get; }
    }

    public class StreamProvider : IDisposable
    {
        public void Dispose()
        {
        }

        public Stream Create(int initialCapacity)
        {
            return new MemoryStream(initialCapacity);
        }
    }
}

Running this benchmark class will current result in an NRE:

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at ArgumentsSourceBenchmarks.<DownloadFileArguments>d__4.MoveNext() in D:\Development\Repos\ArgumentsSource\ArgumentsSourceBenchmarks.cs:line 32
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at BenchmarkDotNet.Running.BenchmarkConverter.GetValidValuesForParamsSource(Type parentType, String sourceName)
   at BenchmarkDotNet.Running.BenchmarkConverter.<GetArgumentsDefinitions>d__9.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at BenchmarkDotNet.Running.BenchmarkConverter.MethodsToBenchmarksWithFullConfig(Type containingType, MethodInfo[] benchmarkMethods, ImmutableConfig immutableConfig)
   at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at BenchmarkDotNet.Running.BenchmarkSwitcher.RunWithDirtyAssemblyResolveHelper(String[] args, IConfig config)
   at BenchmarkDotNet.Running.BenchmarkSwitcher.Run(String[] args, IConfig config)
   at Program.Main(String[] args) in D:\Development\Repos\ArgumentsSource\Program.cs:line 13

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions