The*_*exx 52 .net c# if-statement
这是示例:
if(value != ageValue) {
ageValue = value;
}
Run Code Online (Sandbox Code Playgroud)
我的意思是,如果我们将一个变量的值分配给另一个变量,为什么还要检查它们是否具有相同的值?
这让我感到困惑。这里是更广泛的上下文:
private double ageValue;
public double Age {
get {
return ageValue;
}
set {
if(value != ageValue) {
ageValue = value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Dmi*_*nko 50
这是检查非常有用的代码示例:
public class MyClass {
...
int ageValue = 0;
public int AgeValue {
get {
return ageValue
}
protected set {
... // value validation here
// your code starts
if (value != ageValue) {
ageValue = value;
}
// your code ends
else
return; // do nothing since value == ageValue
// ageValue has been changed
// Time (or / and memory) consuming process
SaveToRDBMS();
InvalidateCache();
...
}
}
...
Run Code Online (Sandbox Code Playgroud)
但是,更自然的实现是从一开始就进行检入,以避免不必要的计算。
protected set {
if (ageValue == value)
return;
... // value validation here
ageValue = value;
// ageValue has been changed
// Time (or / and memory) consuming process
SaveToRDBMS();
InvalidateCache();
...
}
Run Code Online (Sandbox Code Playgroud)
Oli*_*ver 39
在winforms控件中,我们已将BackgroundColor设置为特定颜色:
myControl.BackgroundColor = Color.White
Run Code Online (Sandbox Code Playgroud)
在特定情况下,这可能会发生紧密循环,并导致UI冻结。经过性能分析后,我们发现此调用是UI冻结的原因,因此我们将其更改为:
if (myControl.BackgroundColor != Color.White)
myControl.BackgroundColor = Color.White
Run Code Online (Sandbox Code Playgroud)
我们的工具的性能又回到了正轨(然后我们消除了紧密循环的原因)。
因此,此检查并不总是多余的。特别是如果目标是在设置器中执行更多操作的属性,则只需将值应用于后备存储即可。
Cod*_*dor 20
经if检查,这不是多余的。这取决于其余的实现。请注意,在C#中,!=可以重载,这意味着评估可能会有副作用。此外,检查的变量可以实现为属性,这也可能对评估产生副作用。
Alo*_*aus 18
这个问题已经获得了很多评论,但是到目前为止,所有答案都试图重新构造该问题,以解决操作员超载或设置器副作用的问题。
如果setter被多个线程使用,则确实可以有所作为。如果要使用多个更改数据的线程迭代相同的数据,则设置前检查模式可能(应该测量)很有用。这种现象的教科书名称称为虚假共享。如果您读取数据并确认它已经与目标值匹配,则可以忽略写入。
如果忽略该写操作,则CPU无需刷新高速缓存行(Intel CPU上为64字节的块),以确保其他内核看到更改后的值。如果另一个内核要从该64字节块中读取其他数据,则您只是减慢了内核速度,增加了跨内核流量,以在CPU缓存之间同步内存内容。
以下示例应用程序显示了这种效果,其中还包含写前检查条件:
if (tmp1 != checkValue) // set only if not equal to checkvalue
{
values[i] = checkValue;
}
Run Code Online (Sandbox Code Playgroud)
这是完整的代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
const int N = 500_000_000;
int[] values = new int[N]; // 2 GB
for (int nThreads = 1; nThreads < Environment.ProcessorCount; nThreads++)
{
SetArray(values, checkValue: 1, nTimes: 10, nThreads: nThreads);
SetArray(values, checkValue: 2, nTimes: 10, nThreads: nThreads);
SetArrayNoCheck(values, checkValue: 2, nTimes: 10, nThreads: nThreads);
}
}
private static void SetArray(int[] values, int checkValue, int nTimes, int nThreads)
{
List<double> ms = new List<double>();
for (int k = 0; k < nTimes; k++) // set array values to 1
{
for (int i = 0; i < values.Length; i++)
{
values[i] = 1;
}
var sw = Stopwatch.StartNew();
Action acc = () =>
{
int tmp1 = 0;
for (int i = 0; i < values.Length; i++)
{
tmp1 = values[i];
if (tmp1 != checkValue) // set only if not equal to checkvalue
{
values[i] = checkValue;
}
}
};
Parallel.Invoke(Enumerable.Repeat(acc, nThreads).ToArray()); // Let this run on 3 cores
sw.Stop();
ms.Add(sw.Elapsed.TotalMilliseconds);
// Console.WriteLine($"Set {values.Length * 4 / (1_000_000_000.0f):F1} GB of Memory in {sw.Elapsed.TotalMilliseconds:F0} ms. Initial Value 1. Set Value {checkValue}");
}
string descr = checkValue == 1 ? "Conditional Not Set" : "Conditional Set";
Console.WriteLine($"{descr}, {ms.Average():F0}, ms, nThreads, {nThreads}");
}
private static void SetArrayNoCheck(int[] values, int checkValue, int nTimes, int nThreads)
{
List<double> ms = new List<double>();
for (int k = 0; k < nTimes; k++) // set array values to 1
{
for (int i = 0; i < values.Length; i++)
{
values[i] = 1;
}
var sw = Stopwatch.StartNew();
Action acc = () =>
{
for (int i = 0; i < values.Length; i++)
{
values[i] = checkValue;
}
};
Parallel.Invoke(Enumerable.Repeat(acc, nThreads).ToArray()); // Let this run on 3 cores
sw.Stop();
ms.Add(sw.Elapsed.TotalMilliseconds);
//Console.WriteLine($"Unconditional Set {values.Length * 4 / (1_000_000_000.0f):F1} GB of Memory in {sw.Elapsed.TotalMilliseconds:F0} ms. Initial Value 1. Set Value {checkValue}");
}
Console.WriteLine($"Unconditional Set, {ms.Average():F0}, ms, nThreads, {nThreads}");
}
}
Run Code Online (Sandbox Code Playgroud)
如果运行该命令,则将获得如下值:
// Value not set
Set 2.0 GB of Memory in 439 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 420 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 429 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 393 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 404 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 395 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 419 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 421 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 442 ms. Initial Value 1. Set Value 1
Set 2.0 GB of Memory in 422 ms. Initial Value 1. Set Value 1
// Value written
Set 2.0 GB of Memory in 519 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 582 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 543 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 484 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 523 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 540 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 552 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 527 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 535 ms. Initial Value 1. Set Value 2
Set 2.0 GB of Memory in 581 ms. Initial Value 1. Set Value 2
Run Code Online (Sandbox Code Playgroud)
这样可以使性能提高22%,这在高性能数字处理场景中非常重要。
要回答所写的问题:
如果仅单线程访问内存,则可以删除if语句。如果多个线程在同一个或附近的数据上工作,则可能发生错误共享,这可能使您损失多达约200美元。内存访问性能的20%。
更新1 我进行了更多测试,并创建了一个图表来显示跨核心聊天记录。这显示了一个简单的集合(无条件集合),评论者Frank Hopkins指出了这一点。条件未设置包含从不设置值的if。最后但并非最不重要的是,条件集将在if条件中设置值。
| 归档时间: |
|
| 查看次数: |
4822 次 |
| 最近记录: |