在最近的代码审查期间,一位同事建议,对于具有4个int属性的类,在构造函数中将每个属性赋值为零将导致性能损失。
例如,
public Example()
{
this.major = 0;
this.minor = 0;
this.revision = 0;
this.build = 0;
}
他的观点是,这是多余的,因为默认情况下它们将被设置为零,并且您通过两次基本上执行相同的任务来引入开销。我的观点是,如果一个存在,并且这更可读(有几个构造函数),性能命中可以忽略不计,因为调用此构造函数后对象状态的意图非常清楚。
你怎么看?这里有值得关注的性能提升吗?答案 0 :(得分:6)
不,没有。编译器将优化这些操作;相同的任务不会执行两次。你的同事错了。
[根据总是优秀的Jon Skeet的输入进行编辑]
编译器应该优化操作,但显然它们没有完全优化;然而,优化增益完全可以忽略不计,并且赋值具有如此明确的好处是好的。你的同事可能不是完全错误的,但他们专注于一个完全无关紧要的优化。
答案 1 :(得分:5)
我不相信它们是相同的操作,并且 是性能差异。这是一个显示它的微基准测试:
using System;
using System.Diagnostics;
class With
{
int x;
public With()
{
x = 0;
}
}
class Without
{
int x;
public Without()
{
}
}
class Test
{
static void Main(string[] args)
{
int iterations = int.Parse(args[0]);
Stopwatch sw = Stopwatch.StartNew();
if (args[1] == "with")
{
for (int i = 0; i < iterations; i++)
{
new With();
}
}
else
{
for (int i = 0; i < iterations; i++)
{
new Without();
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
结果:
c:\Users\Jon\Test>test 1000000000 with
8427
c:\Users\Jon\Test>test 1000000000 without
881
c:\Users\Jon\Test>test 1000000000 with
7568
c:\Users\Jon\Test>test 1000000000 without
819
现在,这会让我改变代码吗?绝对不。首先编写最易读的代码。 如果,通过作业更具可读性,请将作业保留在那里。即使微基准测试显示它有成本,但在执行任何真正的工作的情况下,这仍然是一个小成本。即使比例差异很大,它仍然会在“慢”路线中在8秒内创建十亿个实例。我的猜测是,对于完全空的构造函数实际上存在某种优化,直接链接到完全空的object()
构造函数。分配给两个字段和仅分配给一个字段之间的区别要小得多。
就编译器无法优化它而言,请记住基础构造函数可以通过反射或虚拟方法调用来修改值。编译器可能可能注意到这些,但这似乎是一种奇怪的优化。
答案 2 :(得分:4)
我的理解是通过简单且非常快速的内存擦除将对象内存清零。但是,这些明确的分配将需要额外的IL。实际上,有些工具会发现您分配了默认值(在字段初始化程序中)并建议不要使用它。
所以我会说:不要这样做 - 它可能略微更慢。但不是很多。总之,我认为你的朋友是正确的。
不幸的是,我现在正在移动设备上,没有正确的工具来证明它。
答案 3 :(得分:3)
您应该专注于代码清晰度,这是最重要的事情。如果性能成为问题,那么衡量性能,看看你的瓶颈是什么,并改进它们。当理解代码更容易理解时,花费这么多时间来担心性能是不值得的。
答案 4 :(得分:1)
您可以直接将它们初始化为字段:
public int number = 0;
并且也很清楚。
答案 5 :(得分:1)
更重要的问题是:是否真的有可读性?如果维护代码的人已经知道int被分配为零,那么这只是他们需要解析的一些代码。也许代码会更清晰,没有什么都不做的行。
答案 6 :(得分:1)
事实上,我在构造函数中使用赋值,只是为了可读性,并标记'我不忘记初始化那些'意图。依赖默认行为往往会混淆其他开发人员。
答案 7 :(得分:0)
我认为你不应该关心性能损失,通常还有很多其他地方可以优化程序。另一方面,我没有看到在构造函数中指定这些值有任何好处,因为无论如何它们都将设置为0.