Skip to content

Instantly share code, notes, and snippets.

@ufcpp
Created March 31, 2018 03:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ufcpp/79283511d2a10afb34f8c5c837dce1a6 to your computer and use it in GitHub Desktop.
Save ufcpp/79283511d2a10afb34f8c5c837dce1a6 to your computer and use it in GitHub Desktop.
Enum.HasFlag(Enum) のパフォーマンスの測定
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Toolchains.CsProj;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using System;
[Flags]
enum X
{
A = 1,
B = 2,
C = 3,
}
/// <summary>
/// <see cref="System.Enum.HasFlag(Enum)"/> のパフォーマンスの測定。
///
/// .NET では、「すべての enum は System.Enum クラスから派生している “かのようにふるまう”」という仕様があるものの。
/// このために、単なる整数である enum が、Enum クラスにボックス化してる。
/// <see cref="Enum.HasFlag(Enum)"/> も、x.HasFlag(y) って書くと、ボックス化が発生するし、
/// 内部的にも実行型チェックが走ってるみたいでものすごい遅い。
/// 遅かった。
///
/// .NET Core 2.1 では、これを、JIT 時特殊対応したらしくて、速くなってる。
/// 「<see cref="Enum.HasFlag(Enum)"/> が呼ばれてたらそこを単なる比較演算に置き換える」っていう処理を入れたらしい。
/// ボックス化とかの実行時処理が一切消えるので、余裕で1桁速くなる。
/// 参考: https://github.com/dotnet/coreclr/blob/master/tests/src/JIT/opt/Enum/hasflag.cs#L7
///
/// ベンチマーク実行結果の一例:
/// Method | Toolchain | Mean | Error | StdDev | Gen 0 | Allocated |
/// -------- |-------------- |-----------:|----------:|----------:|-------:|----------:|
/// HasFlag | .NET Core 2.0 | 208.555 ns | 0.8816 ns | 0.7815 ns | 0.1371 | 576 B |
/// HasFlag | .NET Core 2.1 | 7.781 ns | 0.0831 ns | 0.0777 ns | - | 0 B |
///
/// .NET Core 2.0 と 2.1 で20倍以上差がある。
/// </summary>
public class HasFlagBenchmark
{
[Benchmark]
public void HasFlag()
{
Count(X.A);
Count(X.A | X.B);
Count(X.A | X.C);
Count(X.A | X.B | X.C);
}
private int Count(X x)
{
var count = 0;
if (x.HasFlag(X.A)) count++;
if (x.HasFlag(X.B)) count++;
if (x.HasFlag(X.C)) count++;
return count;
}
}
class Program
{
static void Main()
{
BenchmarkRunner.Run<HasFlagBenchmark>(new MultipleRuntimesConfig());
}
}
public class MultipleRuntimesConfig : ManualConfig
{
public MultipleRuntimesConfig()
{
Add(Job.Default.With(CsProjCoreToolchain.NetCoreApp20));
Add(Job.Default.With(CsProjCoreToolchain.NetCoreApp21));
Add(DefaultColumnProviders.Instance);
Add(MarkdownExporter.GitHub);
Add(new ConsoleLogger());
Add(new HtmlExporter());
Add(MemoryDiagnoser.Default);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment