c# if簡寫 - IF的表現是否優於IF-ELSE?




6 Answers

檢測結果

塊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%。 有道理,因為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

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在這裡回答 )是因為我測試的代碼塊沒有使用變量高度和寬度,除了if-else中的賦值和if代碼塊1的塊和編譯器分別通過完全刪除問題中的if和if-else塊來優化IL代碼,從而在此處顯示我們測試的無效結果

我已經更新了兩個代碼塊,將高度和寬度的值寫入文件,因此再次使用它們並強制編譯器運行我們的測試塊(我希望),但您可以從代碼中觀察到實際的文件寫入部分不影響我們的測試結果

這是更新的結果,C#和IL代碼

結果

塊#1

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

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

IL代碼由ildasm.exe生成

塊#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)運行得更快,正如本論壇中許多人所指出的那樣。




更新

根據Matthew Steeples根據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;
    }

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

IL代碼由ildasm.exe生成

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



如前所述,如果您對可讀性感興趣,那麼性能可能不會成為問題,儘管您可能想嘗試以下方法:

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

並將std和受限大小定義為其他地方的consts或readonlys(或從config中讀取)。




警告:自從我使用特定的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

注意事項:

  • 並非所有指令都是平等的,當我學習裝配時,特別是跳躍往往更加昂貴...現代處理器基本上已經完成了這種趨勢。
  • 現代編譯器進行了大量的優化,並且可能會將代碼的結構從這些結構中的任何一個更改為另一個結構,或完全不同的方法。 (我已經看到一些相當有創意的數組索引用法,可以在這樣的情況下使用)

結論:一般來說,即使在機器代碼級別,這兩個流程之間的差異也很小。 選擇最適合您的方法,讓編譯器找出優化它的最佳方法。 像這樣的所有相對較小的案件應該或多或少地以這種方式對待。 應該積極地實現宏優化,例如改變計算次數或減少昂貴的函數調用。 像這樣的次循環優化不太可能在實踐中產生真正的差異,特別是在編譯器通過它之後。




看看IL我認為你有一個比if語句更快的問題。 因為您的方法沒有副作用,所以編譯器實際上是在調試模式下完全刪除if語句的內容,並在發布模式下完全刪除if語句。

ILSpy東西中打開.exe文件將驗證這一點。

在找到這個問題的答案之前,你必須重新開始需要花費已知且恆定時間的事情。




塊#2更具可讀性。 但是,貴公司是否有編碼標準? 如果是這樣,我會盡可能地關注它們或建議持續改進。

根據塊#1中的性能,使用空值初始化高度和寬度,但無論如何都要分配(無論條件如何)。 接近零性能差異。

另外,你用ILDASM檢查了IL嗎?




Related