J'ai étudié la dégradation des performances et le suivi qu'il est vers le bas pour ralentir HashSets.
J'ai structs avec nullable valeurs qui sont utilisés comme clé primaire. Par exemple:
public struct NullableLongWrapper
{
private readonly long? _value;
public NullableLongWrapper(long? value)
{
_value = value;
}
}
J'ai remarqué que la création d'un HashSet<NullableLongWrapper>
est extrêmement lente.
Voici un exemple d'utilisation 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);
}
Résultat:
Méthode | Médian | Mises ----------------- |---------------- |--------- 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
À l'aide d'une structure avec un Nullable<long>
par rapport à une structure avec un long
est 3540 fois plus lent!
Dans mon cas, il fait la différence entre la 800ms et <1ms.
Voici les informations sur l'environnement de BenchmarkDotNet:
OS=Microsoft Windows NT 6.1.7601 Service Pack 1
Processeur=Intel(R) Core(TM) i7-5600U CPU 2.60 GHz, ProcessorCount=4
Fréquence=2536269 tiques, Résolution=394.2799 ns, Timer=TSC
CLR=MS.NET 4.0.30319.42000, Arch=64 bits VERSION [RyuJIT]
GC=Concurrent poste de travail
JitModules=clrjit-v4.6.1076.0
Quelle est la raison de la performance est-ce mauvais?