Answer the question
In order to leave comments, you need to log in
What is the fastest null check?
Hello. In my project, checks for null often occur, tell me which one is the fastest? I am aware of several options: foo == null, foo is null, foo == false, Object.Equals(foo, null)
Answer the question
In order to leave comments, you need to log in
I checked for 10 million objects and it turned out that foo is null is 20 times faster than foo == null. Therefore, I wonder which way is the fastest.
public class Benchmark
{
private static readonly object? Obj = new();
[Benchmark]
public bool EqualityOperator() => Obj == null;
[Benchmark]
public bool PatternMatching() => Obj is null;
[Benchmark] public bool ComplexPatterMatching() => Obj is not { };
[Benchmark] public bool ConstantReturn() => false;
[Benchmark] public bool EqualsCall() => Obj!.Equals(null);
[Benchmark] public bool ReferenceEqualsCall() => ReferenceEquals(Obj, null);
}
.method public hidebysig instance bool
EqualityOperator() cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [10 39 - 10 50]
IL_0000: ldsfld object Benchmark::Obj
IL_0005: ldnull
IL_0006: ceq
IL_0008: ret
} // end of method Benchmark::EqualityOperator
.method public hidebysig instance bool
PatternMatching() cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [12 38 - 12 49]
IL_0000: ldsfld object Benchmark::Obj
IL_0005: ldnull
IL_0006: ceq
IL_0008: ret
} // end of method Benchmark::PatternMatching
.method public hidebysig instance bool
ComplexPatterMatching() cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [13 56 - 13 70]
IL_0000: ldsfld object Benchmark::Obj
IL_0005: ldnull
IL_0006: cgt.un
IL_0008: ldc.i4.0
IL_0009: ceq
IL_000b: ret
} // end of method Benchmark::ComplexPatterMatching
.method public hidebysig instance bool
EqualsCall() cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [15 45 - 15 62]
IL_0000: ldsfld object Benchmark::Obj
IL_0005: ldnull
IL_0006: callvirt instance bool [System.Runtime]System.Object::Equals(object)
IL_000b: ret
} // end of method Benchmark::EqualsCall
.method public hidebysig instance bool
ReferenceEqualsCall() cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor()
= (01 00 00 00 )
.maxstack 8
// [16 54 - 16 80]
IL_0000: ldsfld object Benchmark::Obj
IL_0005: ldnull
IL_0006: ceq
IL_0008: ret
} // end of method Benchmark::ReferenceEqualsCall
| Method | Mean | Error | StdDev | Median |
|---------------------- |----------:|----------:|----------:|----------:|
| EqualityOperator | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
| PatternMatching | 0.0300 ns | 0.0164 ns | 0.0145 ns | 0.0271 ns |
| ComplexPatterMatching | 0.0401 ns | 0.0327 ns | 0.0376 ns | 0.0267 ns |
| ConstantReturn | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
| EqualsCall | 1.3787 ns | 0.0437 ns | 0.0409 ns | 1.3770 ns |
| ReferenceEqualsCall | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
| Method | Mean | Error | StdDev |
|---------------------- |----------:|----------:|----------:|
| EqualityOperator | 2.5751 ns | 0.0062 ns | 0.0049 ns |
| PatternMatching | 2.5682 ns | 0.0073 ns | 0.0065 ns |
| ComplexPatterMatching | 2.6456 ns | 0.0744 ns | 0.0696 ns |
| ConstantReturn | 0.0065 ns | 0.0044 ns | 0.0035 ns |
| EqualsCall | 4.6958 ns | 0.0337 ns | 0.0282 ns |
| ReferenceEqualsCall | 2.9525 ns | 0.0667 ns | 0.0557 ns |
All checks for null after compilation are two assembler instructions. So they are all extremely fast.
Well, except for Object.Equals(foo, null), since you need to compare Object.ReferenceEquals(foo, null)
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question