IF 的性能是否比 IF-ELSE 更好?

发布于 2024-12-09 14:02:52 字数 8004 浏览 0 评论 0原文

这些代码块中哪一个性能更好,哪一个更具可读性? 我猜增益可以忽略不计,特别是在第二个区块。我只是好奇。

块#1

string height;
string width;
if (myFlag == 1)
{
    height = "60%";
    width = "60%";
}
else
{
    height = "80%";
    width = "80%";
}

块#2

string height = "80%";
string width = "80%";
if (myFlag == 1)
{
    height = "60%";
    width = "60%";
}

更新

我测试上述代码时的结果是两个块执行相同的

块#1

myFlag = 1:   3 Milliseconds
myFlag = 0:   3 Milliseconds

块 #2

myFlag = 1:   3 Milliseconds
myFlag = 0:   3 Milliseconds

但我在这里注意到一件重要的事情(感谢 Matthew Steeples 在这里回答)是因为代码块我测试过的代码块 1 和 2 的 if-else 和 if 块中的赋值除外,没有使用变量 height 和 width,编译器已完全优化了 IL 代码删除有问题的 if 和 if-else 块,从而在此处显示我们的测试无效结果

我已经更新了两个代码块以将高度和宽度的值写入文件,从而再次使用它们并强制编译器运行我们的测试块(我希望),但是您可以从代码中观察到实际的文件写入部分不会影响我们的测试结果

这是更新后的结果,C# 和 IL 代码

结果

Block #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;
    }

块 #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

块 #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)运行得更快,正如本论坛中许多人指出的那样。

Which one of these blocks of code performs better, and which one of them is more readable?
I'd guess the gain would be negligible, particularly in the second block. I am just curious.

Block #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%";
}

Updated

The results when i tested the above code were that both the blocks performed the same

Block #1

myFlag = 1:   3 Milliseconds
myFlag = 0:   3 Milliseconds

Block #2

myFlag = 1:   3 Milliseconds
myFlag = 0:   3 Milliseconds

But one important thing i noticed here(thanks to Matthew Steeples answer here) is that since the block of code that i have tested has not used the variables height and width except for assignment in the if-else and if blocks of Code Block-1 and 2 respectively, the compiler has optimized the IL code by completely removing the if and if-else blocks in question thus showing invalid results for our test here.

I have updated both the code blocks to write the values of both height and width to a file thus using them again and forcing the compiler to run our test blocks(I hope), but you can observe from the code that the actual file writing part does not effect our test results

This is the updated results, C# and IL Code

Results

Block #1

myFlag = 1:   1688 Milliseconds
myFlag = 0:   1664 Milliseconds

Block #2

myFlag = 1:   1700 Milliseconds
myFlag = 0:   1677 Milliseconds

C#.net Code

Block #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;
    }

IL Code Generated By ildasm.exe

Block #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

So we can say that the IF-Else block(Block #1) runs faster than the if block(Block #2) as pointed by many in this forum.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

[旋木] 2024-12-16 14:02:52

测试结果

块 1 迭代 10,000,000 次

myFlag = 0:    23.8ns per iteration
myFlag = 1:    23.8ns per iteration

块 2 迭代 10,000,000 次

myFlag = 0:    23.8ns per iteration
myFlag = 1:    46.8ns per iteration

块 2块 1 慢 96%。这是有道理的,因为块 2 在悲观情况下做了两倍的工作。

我更喜欢这两种情况,具体取决于具体情况。如果myFlag很少为1,那么它希望它作为我们必须处理的边缘情况脱颖而出。如果两者的可能性相同,我需要 if-else 语法。但这只是偏好,而不是事实。


几十年前,如果进行条件跳转,intel 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 好。


在此处输入图像描述

Test Results

10,000,000 iterations of Block 1

myFlag = 0:    23.8ns per iteration
myFlag = 1:    23.8ns per iteration

10,000,000 iterations of Block 2

myFlag = 0:    23.8ns per iteration
myFlag = 1:    46.8ns per iteration

Block 2 is 96% slower than Block 1. Makes sense, since Block 2 does twice the work in the pessimistic case.

i prefer either case, depending on the situation. If myFlag is rarely ever 1, then it want it to stand out as the edge case that we have to handle. If both are equally likely, i want the if-else syntax. But that's preference, not fact.


Decades ago, the intel 80286 dual pipeline would stall if a conditional jump was taken, rather than falling through to the next instruction. By the time of the Pentium that went away; the CPU pre-fetches both branch paths. But in the back of my mind i still have a twinge of fear whenever i write code that has the most common outcome in the else clause. Every time i have to remind myself that it doesn't matter anymore.


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 makes IF 96% slower
  • GetPercentageString(0.60) makes IF 96% slower

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;

Doing twice the amount of work takes roughly twice the amount of time.

Answer: IF does not perform better than IF-ELSE.


enter image description here

美羊羊 2024-12-16 14:02:52

对于我称之为微-微-微优化的调整来说,这里的性能增益可以忽略不计。除非您打算这样做几百万次,否则请注意这里的可读性。

编辑:(回复:评论中的问题)

在我看来,第一个更具可读性。它以现成的格式明确显示每种情况下的字符串应该是什么。第二个省略了一个案例,因此审阅者必须查看代码的其他区域才能确定默认值。为了更直观地理解,想象一下原始声明/初始化和这个特定代码块之间有 50 行代码。如果在这种情况下变得不清楚,那么这将为我决定。

The performance gain here is negligible to the tune I'd call this micro-micro-micro-optimization. Go for readability here unless you plan to do this a couple of million times.

Edit: (re: question in comments)

In my opinion the first one is the more readable. It explicitly shows in a ready format what the strings should be for each case. The second one omits a case, so a reviewer would have to look through other areas of the code to determine the default value. To put it into perspective, imagine 50 lines of code between the original declaration/initialization and this particular code block. If it becomes unclear in that case then that would decide it for me.

梦幻的心爱 2024-12-16 14:02:52

已更新

根据更新代码后Matthew Steeples 回答 并根据 Lou Franco,我发现 If-Else 块执行比 if 块更好,尽管

我在测试应用程序中使用了以下代码块

C#.net 代码

Block #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;
    }

区块#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

块 #2

myFlag = 1:   1700 Milliseconds
myFlag = 0:   1677 Milliseconds

由 ildasm.exe 生成的 IL 代码< /strong>

块#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

块#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

Updated

After updating the code according to Matthew Steeples answer and testing the code in Release build according to Lou Franco, i found that the If-Else blcoks perfrom better than the if block although marginally

I used the following code blocks in my test application

C#.net Code

Block #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;
    }

The following are the results for Release build

Results for 1000000 Iterations

Block #1

myFlag = 1:   1688 Milliseconds
myFlag = 0:   1664 Milliseconds

Block #2

myFlag = 1:   1700 Milliseconds
myFlag = 0:   1677 Milliseconds

IL Code Generated By ildasm.exe

Block #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
ま昔日黯然 2024-12-16 14:02:52

您可以使用性能分析器自行回答这个问题,或者只是对其进行计时(将函数放入您多次调用的循环中)。据您所知,编译器将其转换为相同的代码(您可以检查)

也许您不应该担心这些类型的微优化。编写最具可读性的代码,直到您的工具告诉您要优化哪些内容。

You can answer this question yourself with a performance analyzer or just timing it (put the function in a loop that you call many times). For all you know, the compiler turns this into the same code (you can check)

Probably you shouldn't worry about these kinds of micro-optimizations. Write the most readable code until your tools tell you what to optimize.

脸赞 2024-12-16 14:02:52

正如之前所说,性能在这里不太可能成为问题,如果您对可读性感兴趣,尽管您可能想尝试以下操作:

string height = StdHeight;
string width = StdWidth;
if (restrictDimensionsFlag)
{
    height = RestrictedHeight;
    width = RestrictedWidth;
}

并将 std 和限制大小定义为其他地方的 const 或 readonly(或从配置中读取)。

As said before performance isn't likely to be an issue here, if you're interested in readability though you might want to try something like:

string height = StdHeight;
string width = StdWidth;
if (restrictDimensionsFlag)
{
    height = RestrictedHeight;
    width = RestrictedWidth;
}

and define your std and restricted sizes as consts or readonlys elsewhere (or read in from config).

被你宠の有点坏 2024-12-16 14:02:52

修正结果。我做了自己的测试,发现这个值我认为更准确。迭代次数:100,000,000

Flag = 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);

Correcting the result. I did my own test and I found this value which I think is more accurate. No of iterations : 100,000,000

Flag = 1

enter image description here

Flag = 0 .

enter image description here

The fact that the worse case time should be twice the best case is wrong.

The code that was used

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);
半衬遮猫 2024-12-16 14:02:52

警告:我已经有一段时间没有进行特定的 CPU 优化了。

也就是说,如果我用汇编语言对其进行编码,则块 1 每个循环的指令数将少于块 2。在汇编/机器代码级别,与 if 相比,if/else 本质上是免费的,因为任何一种情况都扩展到本质上相同的指令(加载、比较、条件跳转)。

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

注意事项:

  • 并非所有指令都是一样的,尤其是当我学习汇编时,跳转的成本往往更高...现代处理器基本上消除了这种趋势。
  • 现代编译器进行了大量的优化,并且可能将代码的结构从其中一种构造更改为另一种构造的等效结构,或者完全不同的方法。 (我已经看到了一些相当有创意的数组索引的使用,也可以在这样的情况下使用)

结论:一般来说,即使在机器代码级别,这些之间的差异也很小两个流。选择最适合您的一种,然后让编译器找出优化它的最佳方法。像这样的所有相对较小的案件或多或少都应该这样对待。例如,应该积极实施改变计算数量或减少昂贵的函数调用的宏观优化。像这样的小循环优化在实践中不太可能产生真正的影响,特别是在编译器完成它之后。

Warning: It's been a while since I worked with specific CPU optimizations.

That said, if I were to code this in assembly language, Block 1 would have fewer instructions per loop than block 2. At the assembly/machine code level, if/else is essentially free when compared to if, because either case expands to essentially the same instructions (load, compare, conditional-jump).

Block1: best case: 5, worst: 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: best case: 6, worst: 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

Caveats:

  • Not all instructions are created equal, and jumps in particular tended to be more expensive when I was studying assembly... modern processors have basically done away with this tendency.
  • Modern compilers do a HUGE amount of optimization, and may change the structure of your code from either one of these constructs to the equivalent of the other, or a completely different method entirely. (I've seen some rather creative use of array indices that could be used in cases like this as well)

Conclusion: In general, the difference, even at the machine code level will be quite minimal between these two flows. Chose the one that works best for you, and let the compiler figure out the best way to optimize it. All relatively minor cases like this should more or less be treated that way. Macro-optimizations which change the number of computations for example, or reduce expensive function calls should be aggressively implemented. Minor loop optimizations like this are unlikely to make a real difference in practice, especially after the compiler is through with it.

一身骄傲 2024-12-16 14:02:52

更快的方法可能是将高度和宽度视为整数/浮点数,并在最后一秒将它们转换为字符串......假设您经常执行此操作以远程影响(提示:您不是)。

The faster way to do this is probably treat height and width as ints/floats and cast them to strings at the last second ... assuming you're doing this operation frequently enough to remotely matter (hint: you're not).

简单 2024-12-16 14:02:52

看看 IL,我认为你遇到了一个比 if 语句更快的更大问题。因为您的方法没有副作用,所以编译器实际上是在调试模式下完全删除 if 语句的内容,并在发布模式下完全删除 if 语句。

在类似 ILSpy 中打开 .exe 文件将验证这一点。

在找到这个问题的答案之前,您必须重新开始一些需要花费已知且恒定时间的事情。

Looking at the IL I think you've got a bigger problem than which if statement is quicker. Because your methods have no side effects the compiler is actually removing the contents of the if statements entirely in debug mode, and removing the if statements entirely in release mode.

Opening up the .exe file in something like ILSpy will verify this.

You'll have to start again with something that takes a known and constant amount of time before finding out the answer to this question.

听风念你 2024-12-16 14:02:52

我会使用块#2。正如您所说,对性能的影响可以忽略不计,但它肯定更短且更易于阅读。本质上,除非满足特定条件,否则您将为变量设置默认值。

I would use Block #2. The performance hit is negligible, as you said, but it's definitely shorter and easier to read. Essentially, you are setting default values to your variables unless a specific condition is met.

冷…雨湿花 2024-12-16 14:02:52

块#2 更具可读性。但是,你们公司有编码标准吗?如果是这样,我会尽可能地关注他们或提出持续改进的建议。

根据块 #1 中的性能,高度和宽度使用空值进行初始化,但随后仍进行分配(无论条件如何)。性能差异接近于零。

另外,你用ILDASM检查过IL吗?

Block #2 is more readable. However, does your company have coding standards? If so, I would following them as much as possible or suggest consistent improvements.

As per performance in Block #1 height and width are initialized with null values, but then assigned anyway (regardless of condition). Near zero performance difference.

Also, did you check the IL with ILDASM?

把梦留给海 2024-12-16 14:02:52

我通常使用 Block #2 方法,只是因为我知道相关变量最初设置为其默认值

I usually use the Block #2 method just because I know the variables in question are initially set to their defaults

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文