这些代码块中哪一个表现更好,哪一个更易读? 我猜这个增益可以忽略不计,特别是在第二个区块。我很好奇。
第1块
string height;
string width;
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
else
{
height = "80%";
width = "80%";
}
Block#2
string height = "80%";
string width = "80%";
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
更新
我测试上述代码时的结果是两个块执行相同的
第1块
myFlag = 1: 3 Milliseconds
myFlag = 0: 3 Milliseconds
Block#2
myFlag = 1: 3 Milliseconds
myFlag = 0: 3 Milliseconds
但是我在这里注意到的一件重要的事情(感谢Matthew Steeples answer here)是因为我测试的代码块没有使用变量高度和宽度,除了在if-else和if块中的赋值代码块1和分别为2,编译器通过完全删除有问题的if和if-else块来优化IL代码,从而在此处显示无效的结果。
我更新了两个代码块,将高度和宽度的值写入文件,从而再次使用它们并强制编译器运行我们的测试块(我希望),但是你可以从代码中观察实际的文件写入部分不会影响我们的测试结果
这是更新的结果,C#和IL代码
结果
第1块
myFlag = 1: 1688 Milliseconds
myFlag = 0: 1664 Milliseconds
Block#2
myFlag = 1: 1700 Milliseconds
myFlag = 0: 1677 Milliseconds
C#.net代码
第1块
public long WithIfAndElse(int myFlag)
{
Stopwatch myTimer = new Stopwatch();
string someString = "";
myTimer.Start();
for (int i = 0; i < 1000000; i++)
{
string height;
string width;
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
else
{
height = "80%";
width = "80%";
}
someString = "Height: " + height + Environment.NewLine + "Width: " + width;
}
myTimer.Stop();
File.WriteAllText("testifelse.txt", someString);
return myTimer.ElapsedMilliseconds;
}
Block#2
public long WithOnlyIf(int myFlag)
{
Stopwatch myTimer = new Stopwatch();
string someString = "";
myTimer.Start();
for (int i = 0; i < 1000000; i++)
{
string height = "80%";
string width = "80%";
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
someString = "Height: " + height + Environment.NewLine + "Width: " + width;
}
myTimer.Stop();
File.WriteAllText("testif.txt", someString);
return myTimer.ElapsedMilliseconds;
}
ildasm.exe生成的IL代码
第1块
.method public hidebysig instance int64 WithIfAndElse(int32 myFlag) cil managed
{
// Code size 144 (0x90)
.maxstack 3
.locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer,
[1] string someString,
[2] int32 i,
[3] string height,
[4] string width,
[5] string[] CS$0$0000)
IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.0
IL_0006: ldstr ""
IL_000b: stloc.1
IL_000c: ldloc.0
IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0012: ldc.i4.0
IL_0013: stloc.2
IL_0014: br.s IL_0070
IL_0016: ldarg.1
IL_0017: ldc.i4.1
IL_0018: bne.un.s IL_0029
IL_001a: ldstr "60%"
IL_001f: stloc.3
IL_0020: ldstr "60%"
IL_0025: stloc.s width
IL_0027: br.s IL_0036
IL_0029: ldstr "80%"
IL_002e: stloc.3
IL_002f: ldstr "80%"
IL_0034: stloc.s width
IL_0036: ldc.i4.5
IL_0037: newarr [mscorlib]System.String
IL_003c: stloc.s CS$0$0000
IL_003e: ldloc.s CS$0$0000
IL_0040: ldc.i4.0
IL_0041: ldstr "Height: "
IL_0046: stelem.ref
IL_0047: ldloc.s CS$0$0000
IL_0049: ldc.i4.1
IL_004a: ldloc.3
IL_004b: stelem.ref
IL_004c: ldloc.s CS$0$0000
IL_004e: ldc.i4.2
IL_004f: call string [mscorlib]System.Environment::get_NewLine()
IL_0054: stelem.ref
IL_0055: ldloc.s CS$0$0000
IL_0057: ldc.i4.3
IL_0058: ldstr "Width: "
IL_005d: stelem.ref
IL_005e: ldloc.s CS$0$0000
IL_0060: ldc.i4.4
IL_0061: ldloc.s width
IL_0063: stelem.ref
IL_0064: ldloc.s CS$0$0000
IL_0066: call string [mscorlib]System.String::Concat(string[])
IL_006b: stloc.1
IL_006c: ldloc.2
IL_006d: ldc.i4.1
IL_006e: add
IL_006f: stloc.2
IL_0070: ldloc.2
IL_0071: ldc.i4 0xf4240
IL_0076: blt.s IL_0016
IL_0078: ldloc.0
IL_0079: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_007e: ldstr "testifelse.txt"
IL_0083: ldloc.1
IL_0084: call void [mscorlib]System.IO.File::WriteAllText(string,
string)
IL_0089: ldloc.0
IL_008a: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_008f: ret
} // end of method frmResearch::WithIfAndElse
Block#2
.method public hidebysig instance int64 WithOnlyIf(int32 myFlag) cil managed
{
// Code size 142 (0x8e)
.maxstack 3
.locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer,
[1] string someString,
[2] int32 i,
[3] string height,
[4] string width,
[5] string[] CS$0$0000)
IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.0
IL_0006: ldstr ""
IL_000b: stloc.1
IL_000c: ldloc.0
IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0012: ldc.i4.0
IL_0013: stloc.2
IL_0014: br.s IL_006e
IL_0016: ldstr "80%"
IL_001b: stloc.3
IL_001c: ldstr "80%"
IL_0021: stloc.s width
IL_0023: ldarg.1
IL_0024: ldc.i4.1
IL_0025: bne.un.s IL_0034
IL_0027: ldstr "60%"
IL_002c: stloc.3
IL_002d: ldstr "60%"
IL_0032: stloc.s width
IL_0034: ldc.i4.5
IL_0035: newarr [mscorlib]System.String
IL_003a: stloc.s CS$0$0000
IL_003c: ldloc.s CS$0$0000
IL_003e: ldc.i4.0
IL_003f: ldstr "Height: "
IL_0044: stelem.ref
IL_0045: ldloc.s CS$0$0000
IL_0047: ldc.i4.1
IL_0048: ldloc.3
IL_0049: stelem.ref
IL_004a: ldloc.s CS$0$0000
IL_004c: ldc.i4.2
IL_004d: call string [mscorlib]System.Environment::get_NewLine()
IL_0052: stelem.ref
IL_0053: ldloc.s CS$0$0000
IL_0055: ldc.i4.3
IL_0056: ldstr "Width: "
IL_005b: stelem.ref
IL_005c: ldloc.s CS$0$0000
IL_005e: ldc.i4.4
IL_005f: ldloc.s width
IL_0061: stelem.ref
IL_0062: ldloc.s CS$0$0000
IL_0064: call string [mscorlib]System.String::Concat(string[])
IL_0069: stloc.1
IL_006a: ldloc.2
IL_006b: ldc.i4.1
IL_006c: add
IL_006d: stloc.2
IL_006e: ldloc.2
IL_006f: ldc.i4 0xf4240
IL_0074: blt.s IL_0016
IL_0076: ldloc.0
IL_0077: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_007c: ldstr "testif.txt"
IL_0081: ldloc.1
IL_0082: call void [mscorlib]System.IO.File::WriteAllText(string,
string)
IL_0087: ldloc.0
IL_0088: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_008d: ret
} // end of method frmResearch::WithOnlyIf
所以我们可以说 IF-Else块(块#1)的运行速度比if块(块#2)的速度快,正如本论坛中许多人指出的那样。
答案 0 :(得分:93)
10,000,000次 Block 1
的迭代myFlag = 0: 23.8ns per iteration
myFlag = 1: 23.8ns per iteration
10,000,000次 Block 2
的迭代myFlag = 0: 23.8ns per iteration
myFlag = 1: 46.8ns per iteration
Block 2 比 Block 1 慢96%。有道理,因为 Block 2 在悲观情况下做了两倍的工作。
我更喜欢这两种情况,视情况而定。如果
myFlag
很少 1,那么它希望它作为我们必须处理的边缘情况脱颖而出。如果两者同样可能,我想要if-else
语法。但这是偏好,而不是事实。
几十年前,如果采取条件跳转,英特尔80286双管道将停止运行,而不是落入下一条指令。到奔腾的时候消失了; CPU预取两个分支路径。但是在我的脑海里,每当我编写在else
子句中具有最常见结果的代码时,我仍然会有一丝恐惧。每当我不得不提醒自己不再重要。
Int32 reps = 10000000;
private void Block1(int myFlag)
{
String width;
String height;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < reps; i++)
{
if (myFlag == 1)
{
width = String.Format("{0:g}%", 60);
height = String.Format("{0:g}%", 60);
}
else
{
width = String.Format("{0:g}%", 80);
height = String.Format("{0:g}%", 80);
}
}
sw.Stop();
Double time = (Double)sw.Elapsed.Ticks / Stopwatch.Frequency * 1000000000.0 / reps;
MessageBox.Show(time.ToString() + " ns");
}
private void Block2(int myFlag)
{
String width;
String height;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < reps; i++)
{
width = String.Format("{0:g}%", 80);
height = String.Format("{0:g}%", 80);
if (myFlag == 1)
{
width = String.Format("{0:g}%", 60);
height = String.Format("{0:g}%", 60);
}
}
sw.Stop();
Double time = (Double)sw.Elapsed.Ticks / Stopwatch.Frequency * 1000000000.0 / reps;
MessageBox.Show(time.ToString() + " ns");
}
String.Format
使IF
慢了96%GetPercentageString(0.60)
使IF
慢了96%const
reps = 10000000;
procedure Block1(myflag: Integer);
var
width, height: string;
i: Integer;
t1, t2: Int64;
time: Extended;
freq: Int64;
begin
QueryPerformanceCounter(t1);
for i := 1 to reps do
begin
if myFlag = 1 then
begin
width := '60%';
height := '60%';
end
else
begin
width := '80%';
height := '80%';
end;
end;
QueryPerformanceCounter(t2);
QueryPerformanceFrequency(freq);
time := (t2-t1) / freq * 1000000000 / reps;
ShowMessage(FloatToStr(time)+ 'ns');
end;
procedure Block2(myflag: Integer);
var
width, height: string;
i: Integer;
t1, t2: Int64;
time: Extended;
freq: Int64;
begin
QueryPerformanceCounter(t1);
for i := 1 to reps do
begin
width := '80%';
height := '80%';
if myFlag = 1 then
begin
width := '60%';
height := '60%';
end;
end;
QueryPerformanceCounter(t2);
QueryPerformanceFrequency(freq);
time := (t2-t1) / freq * 1000000000 / reps;
ShowMessage(FloatToStr(time)+ 'ns');
end;
做两倍的工作量大约是时间的两倍。
答案:IF的表现不如IF-ELSE。
答案 1 :(得分:45)
这里的性能提升可以忽略不计,我称之为微微观微优化。除非你计划这样做几百万次,否则请在这里阅读。
修改(重新:评论中的问题)
在我看来,第一个更具可读性。它以准备好的格式明确地显示了每种情况下字符串应该是什么。第二个省略了一个案例,因此审阅者必须查看代码的其他区域以确定默认值。为了正确看待它,想象一下原始声明/初始化和这个特定代码块之间的50行代码。如果在那种情况下变得不清楚那么那将由我决定。
答案 2 :(得分:13)
<强>更新强>
根据Matthew Steeples answer更新代码并根据Lou Franco测试Release版本中的代码后,我发现If-Else blcoks的性能优于if块,但略有不足
我在测试应用程序中使用了以下代码块
C#.net代码
第1块
public long WithIfAndElse(int myFlag)
{
Stopwatch myTimer = new Stopwatch();
string someString = "";
myTimer.Start();
for (int i = 0; i < 1000000; i++)
{
string height;
string width;
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
else
{
height = "80%";
width = "80%";
}
someString = "Height: " + height + Environment.NewLine + "Width: " + width;
}
myTimer.Stop();
File.WriteAllText("testifelse.txt", someString);
return myTimer.ElapsedMilliseconds;
}
Block#2
public long WithOnlyIf(int myFlag)
{
Stopwatch myTimer = new Stopwatch();
string someString = "";
myTimer.Start();
for (int i = 0; i < 1000000; i++)
{
string height = "80%";
string width = "80%";
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
someString = "Height: " + height + Environment.NewLine + "Width: " + width;
}
myTimer.Stop();
File.WriteAllText("testif.txt", someString);
return myTimer.ElapsedMilliseconds;
}
以下是发布版本
的结果
1000000次迭代的结果
第1块
myFlag = 1: 1688 Milliseconds
myFlag = 0: 1664 Milliseconds
Block#2
myFlag = 1: 1700 Milliseconds
myFlag = 0: 1677 Milliseconds
ildasm.exe生成的IL代码
第1块
.method public hidebysig instance int64 WithIfAndElse(int32 myFlag) cil managed
{
// Code size 144 (0x90)
.maxstack 3
.locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer,
[1] string someString,
[2] int32 i,
[3] string height,
[4] string width,
[5] string[] CS$0$0000)
IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.0
IL_0006: ldstr ""
IL_000b: stloc.1
IL_000c: ldloc.0
IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0012: ldc.i4.0
IL_0013: stloc.2
IL_0014: br.s IL_0070
IL_0016: ldarg.1
IL_0017: ldc.i4.1
IL_0018: bne.un.s IL_0029
IL_001a: ldstr "60%"
IL_001f: stloc.3
IL_0020: ldstr "60%"
IL_0025: stloc.s width
IL_0027: br.s IL_0036
IL_0029: ldstr "80%"
IL_002e: stloc.3
IL_002f: ldstr "80%"
IL_0034: stloc.s width
IL_0036: ldc.i4.5
IL_0037: newarr [mscorlib]System.String
IL_003c: stloc.s CS$0$0000
IL_003e: ldloc.s CS$0$0000
IL_0040: ldc.i4.0
IL_0041: ldstr "Height: "
IL_0046: stelem.ref
IL_0047: ldloc.s CS$0$0000
IL_0049: ldc.i4.1
IL_004a: ldloc.3
IL_004b: stelem.ref
IL_004c: ldloc.s CS$0$0000
IL_004e: ldc.i4.2
IL_004f: call string [mscorlib]System.Environment::get_NewLine()
IL_0054: stelem.ref
IL_0055: ldloc.s CS$0$0000
IL_0057: ldc.i4.3
IL_0058: ldstr "Width: "
IL_005d: stelem.ref
IL_005e: ldloc.s CS$0$0000
IL_0060: ldc.i4.4
IL_0061: ldloc.s width
IL_0063: stelem.ref
IL_0064: ldloc.s CS$0$0000
IL_0066: call string [mscorlib]System.String::Concat(string[])
IL_006b: stloc.1
IL_006c: ldloc.2
IL_006d: ldc.i4.1
IL_006e: add
IL_006f: stloc.2
IL_0070: ldloc.2
IL_0071: ldc.i4 0xf4240
IL_0076: blt.s IL_0016
IL_0078: ldloc.0
IL_0079: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_007e: ldstr "testifelse.txt"
IL_0083: ldloc.1
IL_0084: call void [mscorlib]System.IO.File::WriteAllText(string,
string)
IL_0089: ldloc.0
IL_008a: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_008f: ret
} // end of method frmResearch::WithIfAndElse
Block#2
.method public hidebysig instance int64 WithOnlyIf(int32 myFlag) cil managed
{
// Code size 142 (0x8e)
.maxstack 3
.locals init ([0] class [System]System.Diagnostics.Stopwatch myTimer,
[1] string someString,
[2] int32 i,
[3] string height,
[4] string width,
[5] string[] CS$0$0000)
IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor()
IL_0005: stloc.0
IL_0006: ldstr ""
IL_000b: stloc.1
IL_000c: ldloc.0
IL_000d: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()
IL_0012: ldc.i4.0
IL_0013: stloc.2
IL_0014: br.s IL_006e
IL_0016: ldstr "80%"
IL_001b: stloc.3
IL_001c: ldstr "80%"
IL_0021: stloc.s width
IL_0023: ldarg.1
IL_0024: ldc.i4.1
IL_0025: bne.un.s IL_0034
IL_0027: ldstr "60%"
IL_002c: stloc.3
IL_002d: ldstr "60%"
IL_0032: stloc.s width
IL_0034: ldc.i4.5
IL_0035: newarr [mscorlib]System.String
IL_003a: stloc.s CS$0$0000
IL_003c: ldloc.s CS$0$0000
IL_003e: ldc.i4.0
IL_003f: ldstr "Height: "
IL_0044: stelem.ref
IL_0045: ldloc.s CS$0$0000
IL_0047: ldc.i4.1
IL_0048: ldloc.3
IL_0049: stelem.ref
IL_004a: ldloc.s CS$0$0000
IL_004c: ldc.i4.2
IL_004d: call string [mscorlib]System.Environment::get_NewLine()
IL_0052: stelem.ref
IL_0053: ldloc.s CS$0$0000
IL_0055: ldc.i4.3
IL_0056: ldstr "Width: "
IL_005b: stelem.ref
IL_005c: ldloc.s CS$0$0000
IL_005e: ldc.i4.4
IL_005f: ldloc.s width
IL_0061: stelem.ref
IL_0062: ldloc.s CS$0$0000
IL_0064: call string [mscorlib]System.String::Concat(string[])
IL_0069: stloc.1
IL_006a: ldloc.2
IL_006b: ldc.i4.1
IL_006c: add
IL_006d: stloc.2
IL_006e: ldloc.2
IL_006f: ldc.i4 0xf4240
IL_0074: blt.s IL_0016
IL_0076: ldloc.0
IL_0077: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()
IL_007c: ldstr "testif.txt"
IL_0081: ldloc.1
IL_0082: call void [mscorlib]System.IO.File::WriteAllText(string,
string)
IL_0087: ldloc.0
IL_0088: callvirt instance int64 [System]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds()
IL_008d: ret
} // end of method frmResearch::WithOnlyIf
答案 3 :(得分:11)
您可以使用性能分析器自己回答这个问题,或者只是计时(将函数置于您多次调用的循环中)。如您所知,编译器将其转换为相同的代码(您可以检查)
可能你不应该担心这些微观优化。编写最易读的代码,直到您的工具告诉您要优化的内容。
答案 4 :(得分:5)
如前所述,如果您对可读性感兴趣,那么在此之前表现不太可能是一个问题,但您可能想要尝试以下内容:
string height = StdHeight;
string width = StdWidth;
if (restrictDimensionsFlag)
{
height = RestrictedHeight;
width = RestrictedWidth;
}
并将std和受限制的大小定义为其他地方的consts或readonlys(或从config中读取)。
答案 5 :(得分:3)
更正结果。我做了自己的测试,我发现这个值我觉得更准确。迭代次数:100,000,000
旗帜= 1
Flag = 0。
事实上,更糟糕的案件时间应该是最佳案件的两倍是错误的。
使用的代码
string height;
string width;
int myFlag = 1;
Console.WriteLine(" ----------- case 1 ---------------");
DateTime Start = DateTime.Now;
for (int Lp = 0; Lp < 100000000; Lp++)
{
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
else
{
height = "80%";
width = "80%";
}
}
TimeSpan Elapsed = DateTime.Now - Start;
Console.WriteLine("Time Elapsed: {0} ms",Elapsed.Milliseconds);
Console.WriteLine(" ----------- case 2 ---------------");
DateTime Start2 = DateTime.Now;
for (int Lp = 0; Lp < 100000000; Lp++)
{
height = "80%";
width = "80%";
if (myFlag == 1)
{
height = "60%";
width = "60%";
}
}
Elapsed = DateTime.Now - Start2;
Console.WriteLine("Time Elapsed: {0} ms", Elapsed.Milliseconds);
答案 6 :(得分:3)
警告:自从我使用特定的CPU优化以来已经有一段时间了。
也就是说,如果我用汇编语言对其进行编码,则块1的每个循环的指令将少于块2.在汇编/机器代码级别,如果/ else与if相比基本上是免费的,因为任何一种情况扩展到基本相同的指令(加载,比较,条件跳转)。
Block1:最佳案例:5,最差:6
Load value of myFlag
Compare to const 1
Jump if zero (equal) :t1
height = "80%";
width = "80%";
Jump :t2
:t1
height = "60%";
width = "60%";
:t2
Block2:最好的情况:6,最差:7
height = "80%";
width = "80%";
Load value of myFlag
Compare to const 1
Jump if non-zero (not-equal) :t1
height = "60%";
width = "60%";
:t1
警告:
结论:通常情况下,即使在机器代码级别,这两个流程之间的差异也非常小。选择最适合您的方法,让编译器找出优化它的最佳方法。像这样的所有相对较小的案件应该或多或少地以这种方式对待。应该积极地实现宏优化,例如改变计算次数或减少昂贵的函数调用。像这样的次循环优化不太可能在实践中产生真正的差异,特别是在编译器完成之后。
答案 7 :(得分:2)
执行此操作的更快方法可能是将高度和宽度视为整数/浮点数,并在最后一秒将它们转换为字符串...假设您经常执行此操作以远程重要(提示:您不是)。
答案 8 :(得分:2)
看看IL我认为你有一个比if语句更快的问题。因为您的方法没有副作用,所以编译器实际上是在调试模式下完全删除if语句的内容,并在发布模式下完全删除if语句。
以ILSpy之类的方式打开.exe文件将验证这一点。
在找到这个问题的答案之前,你必须重新开始需要已知且持续时间的事情。
答案 9 :(得分:0)
我会使用 Block#2 。正如你所说,性能影响可以忽略不计,但它肯定更短,更容易阅读。实际上,除非满足特定条件,否则您将为变量设置默认值。
答案 10 :(得分:0)
块#2更具可读性。但是,贵公司是否有编码标准?如果是这样,我会尽可能地关注它们或建议持续改进。
根据Block#1中的性能,使用空值初始化高度和宽度,但无论如何都要分配(无论条件如何)。接近于零的性能差异。
另外,您是否使用ILDASM检查IL?
答案 11 :(得分:0)
我通常使用Block#2方法,因为我知道有问题的变量最初设置为默认值