哪个对于初始化 属性、if 或合并中的空检查更有效?
Which is more efficient for initialising a property, null checking in an if or coalescence?
我一直在努力使一些代码更高效,现在我想知道哪种模式更高效。由于遗留原因,该解决方案在 VB.NET 和 C# 中都有代码。
我已经输入了我们拥有的两种方法的 VB 和 C# 版本。
的想法是,如果 Foo
属性 被访问并且 _foo
为空,那么它将被设置为一个新对象,并且任何后续请求都将访问同一个对象对象,而不是每次都创建一个新对象。
我知道编译器和 JIT 在幕后做了一些聪明的事情,但我不确定哪种做事的方式更有效。
选项 1:合并值。 Visual Studio 一直建议在某些地方合并,所以这让我觉得该操作非常优化。但是,每次我们得到 Foo
时都会分配给 _foo
选项 2:比较 _foo
和 null
,然后仅在需要时分配给 _foo
。
我确定两者在速度上几乎没有任何区别,但我很好奇它们在低得多的水平上是如何处理的
Private _foo As List(Of Fubar)
Private _bar As String
Private _foo2 As List(Of Fubar)
Private _bar2 As String
Public Property Foo As List(Of Fubar)
Get
_foo = If(_foo, New List(Of Fubar))
Return _foo
End Get
Set (value As List(Of Fubar))
_foo = value
End Set
End Property
Public Property Bar As String
Get
_bar = If(_bar, String.Empty)
Return _bar
End Get
Set (value As String)
_bar = value
End Set
End Property
Public Property Foo2 As List(Of Fubar)
Get
If _foo2 Is Nothing Then _foo2 = New List(Of Fubar)
Return _foo2
End Get
Set (value As List(Of Fubar))
_foo2 = value
End Set
End Property
Public Property Bar2 As String
Get
If _bar2 Is Nothing Then _bar2 = String.Empty
Return _bar2
End Get
Set (value As String)
_bar2 = value
End Set
End Property
private List<Fubar> _foo;
private string _bar;
private List<Fubar> _foo2;
private string _bar2;
public List<Fubar> Foo
{
get
{
_foo = (_foo ?? new List<Fubar>());
return _foo;
}
set
{
_foo = value;
}
}
public string Bar
{
get
{
_bar = (_bar ?? string.Empty);
return _bar;
}
set
{
_bar = value;
}
}
public List<Fubar> Foo2
{
get
{
if (_foo2 == null) { _foo2 = new List<Fubar>(); }
return _foo2;
}
set
{
_foo2 = value;
}
}
public string Bar2
{
get
{
if (_bar2 == null) { _bar2 = string.Empty; }
return _bar2;
}
set
{
_bar2 = value;
}
}
我们开始了;这里的 IsEmpty
告诉我们在 get
操作之前是否是 null
,即它采取了哪个分支。
我的结论:
- 如果值通常会有值,
*2
方法更好
- 如果该值通常不会但有一个值,则没有真正的区别
- 但这些时间不太可能影响您的申请;都非常快
就个人而言,在 C# 中我更喜欢:
get => x ?? (x = val);
我会为此添加一个时间...(编辑:即 Foo3
/Bar3
看起来比 Foo2
/[=17 略有改进=]).
| Method | Job | Runtime | IsEmpty | Mean | Error | StdDev | Median |
|------- |----- |-------- |-------- |---------:|----------:|----------:|---------:|
| Foo | Clr | Clr | False | 1.764 ns | 0.0106 ns | 0.0094 ns | 1.760 ns |
| Foo2 | Clr | Clr | False | 1.175 ns | 0.0235 ns | 0.0305 ns | 1.185 ns |
| Foo3 | Clr | Clr | False | 1.165 ns | 0.0227 ns | 0.0347 ns | 1.180 ns |
| Bar | Clr | Clr | False | 1.957 ns | 0.0350 ns | 0.0293 ns | 1.940 ns |
| Bar2 | Clr | Clr | False | 1.197 ns | 0.0313 ns | 0.0348 ns | 1.190 ns |
| Bar3 | Clr | Clr | False | 1.165 ns | 0.0156 ns | 0.0146 ns | 1.170 ns |
| Foo | Core | Core | False | 2.142 ns | 0.0237 ns | 0.0185 ns | 2.135 ns |
| Foo2 | Core | Core | False | 1.172 ns | 0.0232 ns | 0.0524 ns | 1.170 ns |
| Foo3 | Core | Core | False | 1.168 ns | 0.0221 ns | 0.0237 ns | 1.170 ns |
| Bar | Core | Core | False | 2.063 ns | 0.0414 ns | 0.0580 ns | 2.040 ns |
| Bar2 | Core | Core | False | 1.169 ns | 0.0235 ns | 0.0392 ns | 1.170 ns |
| Bar3 | Core | Core | False | 1.151 ns | 0.0230 ns | 0.0379 ns | 1.150 ns |
| | | | | | | | |
| Foo | Clr | Clr | True | 1.767 ns | 0.0174 ns | 0.0154 ns | 1.760 ns |
| Foo2 | Clr | Clr | True | 1.791 ns | 0.0150 ns | 0.0141 ns | 1.790 ns |
| Foo3 | Clr | Clr | True | 1.784 ns | 0.0196 ns | 0.0174 ns | 1.780 ns |
| Bar | Clr | Clr | True | 1.767 ns | 0.0075 ns | 0.0063 ns | 1.770 ns |
| Bar2 | Clr | Clr | True | 1.784 ns | 0.0086 ns | 0.0067 ns | 1.780 ns |
| Bar3 | Clr | Clr | True | 1.775 ns | 0.0211 ns | 0.0176 ns | 1.780 ns |
| Foo | Core | Core | True | 2.360 ns | 0.0650 ns | 0.1400 ns | 2.290 ns |
| Foo2 | Core | Core | True | 2.553 ns | 0.0987 ns | 0.1754 ns | 2.450 ns |
| Foo3 | Core | Core | True | 2.464 ns | 0.0649 ns | 0.1894 ns | 2.345 ns |
| Bar | Core | Core | True | 1.697 ns | 0.0234 ns | 0.0183 ns | 1.690 ns |
| Bar2 | Core | Core | True | 1.717 ns | 0.0349 ns | 0.0621 ns | 1.695 ns |
| Bar3 | Core | Core | True | 1.647 ns | 0.0223 ns | 0.0198 ns | 1.640 ns |
注意我删除了实际的 List<T>
创建以避免开销 - 它现在分配给静态。
代码:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using System.Collections.Generic;
using System.Linq;
public static class Program
{
static void Main() => BenchmarkRunner.Run<MyTest>();
}
[ClrJob, CoreJob]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams)]
public class MyTest
{
[Params(false, true)]
public bool IsEmpty { get; set; }
const int OperationsPerInvoke = 10000;
private readonly Blab[] blabs = Enumerable.Range(0, OperationsPerInvoke).Select(XmlExporterAttribute => new Blab()).ToArray();
[IterationSetup]
public void Reset()
{
if (IsEmpty)
{
foreach (var blab in blabs) blab.Reset();
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Foo()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Foo;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Foo2()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Foo2;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Foo3()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Foo3;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Bar()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Bar;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Bar2()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Bar2;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Bar3()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Bar3;
}
}
public class Fubar { }
public class Blab
{
static readonly List<Fubar> s_SharedList = new List<Fubar>();
public void Reset()
{
_foo = _foo2 = _foo3 = null;
_bar = _bar2 = _bar3 = null;
}
private List<Fubar> _foo, _foo2, _foo3;
private string _bar, _bar2, _bar3;
public List<Fubar> Foo
{
get
{
_foo = (_foo ?? s_SharedList);
return _foo;
}
set
{
_foo = value;
}
}
public string Bar
{
get
{
_bar = (_bar ?? string.Empty);
return _bar;
}
set
{
_bar = value;
}
}
public List<Fubar> Foo2
{
get
{
if (_foo2 == null) { _foo2 = s_SharedList; }
return _foo2;
}
set
{
_foo2 = value;
}
}
public List<Fubar> Foo3
{
get => _foo3 ?? (_foo3 = s_SharedList);
set { _foo3 = value; }
}
public string Bar2
{
get
{
if (_bar2 == null) { _bar2 = string.Empty; }
return _bar2;
}
set
{
_bar2 = value;
}
}
public string Bar3
{
get => _bar3 ?? (_bar3 = string.Empty);
set { _bar3 = value; }
}
}
我一直在努力使一些代码更高效,现在我想知道哪种模式更高效。由于遗留原因,该解决方案在 VB.NET 和 C# 中都有代码。
我已经输入了我们拥有的两种方法的 VB 和 C# 版本。
的想法是,如果 Foo
属性 被访问并且 _foo
为空,那么它将被设置为一个新对象,并且任何后续请求都将访问同一个对象对象,而不是每次都创建一个新对象。
我知道编译器和 JIT 在幕后做了一些聪明的事情,但我不确定哪种做事的方式更有效。
选项 1:合并值。 Visual Studio 一直建议在某些地方合并,所以这让我觉得该操作非常优化。但是,每次我们得到 Foo
时都会分配给 _foo
选项 2:比较 _foo
和 null
,然后仅在需要时分配给 _foo
。
我确定两者在速度上几乎没有任何区别,但我很好奇它们在低得多的水平上是如何处理的
Private _foo As List(Of Fubar)
Private _bar As String
Private _foo2 As List(Of Fubar)
Private _bar2 As String
Public Property Foo As List(Of Fubar)
Get
_foo = If(_foo, New List(Of Fubar))
Return _foo
End Get
Set (value As List(Of Fubar))
_foo = value
End Set
End Property
Public Property Bar As String
Get
_bar = If(_bar, String.Empty)
Return _bar
End Get
Set (value As String)
_bar = value
End Set
End Property
Public Property Foo2 As List(Of Fubar)
Get
If _foo2 Is Nothing Then _foo2 = New List(Of Fubar)
Return _foo2
End Get
Set (value As List(Of Fubar))
_foo2 = value
End Set
End Property
Public Property Bar2 As String
Get
If _bar2 Is Nothing Then _bar2 = String.Empty
Return _bar2
End Get
Set (value As String)
_bar2 = value
End Set
End Property
private List<Fubar> _foo;
private string _bar;
private List<Fubar> _foo2;
private string _bar2;
public List<Fubar> Foo
{
get
{
_foo = (_foo ?? new List<Fubar>());
return _foo;
}
set
{
_foo = value;
}
}
public string Bar
{
get
{
_bar = (_bar ?? string.Empty);
return _bar;
}
set
{
_bar = value;
}
}
public List<Fubar> Foo2
{
get
{
if (_foo2 == null) { _foo2 = new List<Fubar>(); }
return _foo2;
}
set
{
_foo2 = value;
}
}
public string Bar2
{
get
{
if (_bar2 == null) { _bar2 = string.Empty; }
return _bar2;
}
set
{
_bar2 = value;
}
}
我们开始了;这里的 IsEmpty
告诉我们在 get
操作之前是否是 null
,即它采取了哪个分支。
我的结论:
- 如果值通常会有值,
*2
方法更好 - 如果该值通常不会但有一个值,则没有真正的区别
- 但这些时间不太可能影响您的申请;都非常快
就个人而言,在 C# 中我更喜欢:
get => x ?? (x = val);
我会为此添加一个时间...(编辑:即 Foo3
/Bar3
看起来比 Foo2
/[=17 略有改进=]).
| Method | Job | Runtime | IsEmpty | Mean | Error | StdDev | Median |
|------- |----- |-------- |-------- |---------:|----------:|----------:|---------:|
| Foo | Clr | Clr | False | 1.764 ns | 0.0106 ns | 0.0094 ns | 1.760 ns |
| Foo2 | Clr | Clr | False | 1.175 ns | 0.0235 ns | 0.0305 ns | 1.185 ns |
| Foo3 | Clr | Clr | False | 1.165 ns | 0.0227 ns | 0.0347 ns | 1.180 ns |
| Bar | Clr | Clr | False | 1.957 ns | 0.0350 ns | 0.0293 ns | 1.940 ns |
| Bar2 | Clr | Clr | False | 1.197 ns | 0.0313 ns | 0.0348 ns | 1.190 ns |
| Bar3 | Clr | Clr | False | 1.165 ns | 0.0156 ns | 0.0146 ns | 1.170 ns |
| Foo | Core | Core | False | 2.142 ns | 0.0237 ns | 0.0185 ns | 2.135 ns |
| Foo2 | Core | Core | False | 1.172 ns | 0.0232 ns | 0.0524 ns | 1.170 ns |
| Foo3 | Core | Core | False | 1.168 ns | 0.0221 ns | 0.0237 ns | 1.170 ns |
| Bar | Core | Core | False | 2.063 ns | 0.0414 ns | 0.0580 ns | 2.040 ns |
| Bar2 | Core | Core | False | 1.169 ns | 0.0235 ns | 0.0392 ns | 1.170 ns |
| Bar3 | Core | Core | False | 1.151 ns | 0.0230 ns | 0.0379 ns | 1.150 ns |
| | | | | | | | |
| Foo | Clr | Clr | True | 1.767 ns | 0.0174 ns | 0.0154 ns | 1.760 ns |
| Foo2 | Clr | Clr | True | 1.791 ns | 0.0150 ns | 0.0141 ns | 1.790 ns |
| Foo3 | Clr | Clr | True | 1.784 ns | 0.0196 ns | 0.0174 ns | 1.780 ns |
| Bar | Clr | Clr | True | 1.767 ns | 0.0075 ns | 0.0063 ns | 1.770 ns |
| Bar2 | Clr | Clr | True | 1.784 ns | 0.0086 ns | 0.0067 ns | 1.780 ns |
| Bar3 | Clr | Clr | True | 1.775 ns | 0.0211 ns | 0.0176 ns | 1.780 ns |
| Foo | Core | Core | True | 2.360 ns | 0.0650 ns | 0.1400 ns | 2.290 ns |
| Foo2 | Core | Core | True | 2.553 ns | 0.0987 ns | 0.1754 ns | 2.450 ns |
| Foo3 | Core | Core | True | 2.464 ns | 0.0649 ns | 0.1894 ns | 2.345 ns |
| Bar | Core | Core | True | 1.697 ns | 0.0234 ns | 0.0183 ns | 1.690 ns |
| Bar2 | Core | Core | True | 1.717 ns | 0.0349 ns | 0.0621 ns | 1.695 ns |
| Bar3 | Core | Core | True | 1.647 ns | 0.0223 ns | 0.0198 ns | 1.640 ns |
注意我删除了实际的 List<T>
创建以避免开销 - 它现在分配给静态。
代码:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using System.Collections.Generic;
using System.Linq;
public static class Program
{
static void Main() => BenchmarkRunner.Run<MyTest>();
}
[ClrJob, CoreJob]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams)]
public class MyTest
{
[Params(false, true)]
public bool IsEmpty { get; set; }
const int OperationsPerInvoke = 10000;
private readonly Blab[] blabs = Enumerable.Range(0, OperationsPerInvoke).Select(XmlExporterAttribute => new Blab()).ToArray();
[IterationSetup]
public void Reset()
{
if (IsEmpty)
{
foreach (var blab in blabs) blab.Reset();
}
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Foo()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Foo;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Foo2()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Foo2;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Foo3()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Foo3;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Bar()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Bar;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Bar2()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Bar2;
}
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
public void Bar3()
{
for (int i = 0; i < blabs.Length; i++) _ = blabs[i].Bar3;
}
}
public class Fubar { }
public class Blab
{
static readonly List<Fubar> s_SharedList = new List<Fubar>();
public void Reset()
{
_foo = _foo2 = _foo3 = null;
_bar = _bar2 = _bar3 = null;
}
private List<Fubar> _foo, _foo2, _foo3;
private string _bar, _bar2, _bar3;
public List<Fubar> Foo
{
get
{
_foo = (_foo ?? s_SharedList);
return _foo;
}
set
{
_foo = value;
}
}
public string Bar
{
get
{
_bar = (_bar ?? string.Empty);
return _bar;
}
set
{
_bar = value;
}
}
public List<Fubar> Foo2
{
get
{
if (_foo2 == null) { _foo2 = s_SharedList; }
return _foo2;
}
set
{
_foo2 = value;
}
}
public List<Fubar> Foo3
{
get => _foo3 ?? (_foo3 = s_SharedList);
set { _foo3 = value; }
}
public string Bar2
{
get
{
if (_bar2 == null) { _bar2 = string.Empty; }
return _bar2;
}
set
{
_bar2 = value;
}
}
public string Bar3
{
get => _bar3 ?? (_bar3 = string.Empty);
set { _bar3 = value; }
}
}