私はパフォーマンスの低下を調査し、それを追跡してHashSetを遅くしました。
私は主キーとして使用されるnull可能な値を持つ構造体を持っています。たとえば:なぜnull可能な値を持つ構造体のHashSetが非常に遅いのですか?
public struct NullableLongWrapper
{
private readonly long? _value;
public NullableLongWrapper(long? value)
{
_value = value;
}
}
私はHashSet<NullableLongWrapper>
の作成は非常に緩慢であることに気づきました。ここで
はBenchmarkDotNetを使った例です:(Install-Package BenchmarkDotNet
)
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
public class Program
{
static void Main()
{
BenchmarkRunner.Run<HashSets>();
}
}
public class Config : ManualConfig
{
public Config()
{
Add(Job.Dry.WithWarmupCount(1).WithLaunchCount(3).WithTargetCount(20));
}
}
public struct NullableLongWrapper
{
private readonly long? _value;
public NullableLongWrapper(long? value)
{
_value = value;
}
public long? Value => _value;
}
public struct LongWrapper
{
private readonly long _value;
public LongWrapper(long value)
{
_value = value;
}
public long Value => _value;
}
[Config(typeof (Config))]
public class HashSets
{
private const int ListSize = 1000;
private readonly List<long?> _nullables;
private readonly List<long> _longs;
private readonly List<NullableLongWrapper> _nullableWrappers;
private readonly List<LongWrapper> _wrappers;
public HashSets()
{
_nullables = Enumerable.Range(1, ListSize).Select(i => (long?) i).ToList();
_longs = Enumerable.Range(1, ListSize).Select(i => (long) i).ToList();
_nullableWrappers = Enumerable.Range(1, ListSize).Select(i => new NullableLongWrapper(i)).ToList();
_wrappers = Enumerable.Range(1, ListSize).Select(i => new LongWrapper(i)).ToList();
}
[Benchmark]
public void Longs() => new HashSet<long>(_longs);
[Benchmark]
public void NullableLongs() => new HashSet<long?>(_nullables);
[Benchmark(Baseline = true)]
public void Wrappers() => new HashSet<LongWrapper>(_wrappers);
[Benchmark]
public void NullableWrappers() => new HashSet<NullableLongWrapper>(_nullableWrappers);
}
結果:long
を持つ構造体に比べNullable<long>
を持つ構造体を使用して
Method | Median | Scaled ----------------- |---------------- |--------- Longs | 22.8682 us | 0.42 NullableLongs | 39.0337 us | 0.62 Wrappers | 62.8877 us | 1.00 NullableWrappers | 231,993.7278 us | 3,540.34
が3540倍遅いです!
私の場合は、800msと<の差が1msです。ここで
はBenchmarkDotNetからの環境情報です:
OS =のMicrosoft Windows NT 6.1.7601 Service Pack 1を
プロセッサ=インテル(R)Core(TM)i7-5600U CPU 2.60GHz、ProcessorCount = 4
周波数= 2536269ティック、分解能= 394.2799 NS、タイマ= TSC
CLR = MS.NET 4.0.30319.42000、アーチ= 64ビットRELEASE [RyuJIT]
GC =同時ワークステーション
JitModules = clrjit-V4。 6.1076.0
なぜパフォーマンスが悪いのですか?
私も[フィールドを読んでいないようにしました](https://codeblog.jonskeet.uk/2014/07/16 /マイクロ最適化 - 驚くほど非効率的な読み取り専用フィールド/)、それは役に立たない。 – Kobi
構造体に 'GetHashCode'と' Equals'を実装していますか?デフォルトの実装ではリフレクションを使用します。また、ボクシングを防ぐために 'IEquatableを実装する必要があります。 –
Lee
@Lee - いいえ - これは競合する例です。 'GetHashCode'と' Equals'の実装はありません。それは良い解決策ですが、私はそれを試していませんでした。 – Kobi