Ich untersuchte die Leistungseinbußen und verfolgte sie, um HashSets zu verlangsamen.
Ich habe Strukturen mit Nullable-Werten, die als Primärschlüssel verwendet werden. Zum Beispiel:Warum sind HashSets von Strukturen mit Nullwerten unglaublich langsam?
public struct NullableLongWrapper
{
private readonly long? _value;
public NullableLongWrapper(long? value)
{
_value = value;
}
}
Ich bemerkte, dass ein HashSet<NullableLongWrapper>
Schaffung außergewöhnlich langsam ist.
Hier ist ein Beispiel unter Verwendung von 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);
}
Ergebnis:
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
eine Struktur mit einem Nullable<long>
Verwendung im Vergleich zu einer Struktur mit einem long
ist 3540-mal langsamer!
In meinem Fall machte es den Unterschied zwischen 800ms und < 1ms.
Hier ist die Umgebung Informationen aus BenchmarkDotNet:
OS = Microsoft Windows NT 6.1.7601 Service Pack 1
Processor = Intel (R) Core (TM) i7-5600U CPU 2.60GHz, ProcessorCount = Frequency 4
= 2.536.269 Zecken, Auflösung = 394,2799 ns, Timer = TSC
CLR = MS.NET 4.0.30319.42000, Arch = 64-Bit-RELEASE [RyuJIT]
GC = Concurrent Workstation
JitModules = clrjit-v4. 6.1076.0
Aus welchem Grund ist die Leistung so schlecht?
Ich habe auch versucht [machen das Feld nicht readonly] (https://codeblog.jonskeet.uk/2014/07/16/Mikro-Optimierung-die-überraschend-Ineffizienz-von-Nur-Lese-Feldern /), hilft es nicht. – Kobi
Implementieren Sie 'GetHashCode' und' Equals' in Ihrer Struktur? Die Standardimplementierungen verwenden Reflektion. Sie sollten auch 'IEquatable' implementieren, um Boxen zu verhindern. –
Lee
@Lee - nein - das ist ein konkurrierendes Beispiel. Keine Implementierung von 'GetHashCode' und' Equals'. Das ist ein guter Workaround, ich habe es nicht versucht. – Kobi