条件运算符慢吗?

发布于 2024-08-21 15:02:57 字数 3214 浏览 10 评论 0原文

我正在查看一些代码,每个案例都有一个巨大的 switch 语句和 if-else 语句,并立即感受到优化的冲动。作为一名优秀的开发人员,我始终应该着手获取一些硬时序事实,并从三个变体开始:

  1. 原始代码如下所示:

    public static bool SwitchIfElse(Key inKey, out char key, bool shift)
    {
        开关(inKey)
        {
           case Key.A: if (shift) { key = 'A'; } else { key = 'a';返回真;
           case Key.B: if (shift) { key = 'B'; } else { key = 'b';返回真;
           case Key.C: if (shift) { key = 'C'; } else { key = 'c';返回真;
           ...
           case Key.Y: if (shift) { key = 'Y'; } else { key = 'y';返回真;
           case Key.Z: if (shift) { key = 'Z'; } else { key = 'z';返回真;
           ...
           //更多带有特殊键的情况...
        }
        键=(字符)0;
        返回假;
    }
    
  2. 转换为使用条件运算符的第二个变体:

    public static bool SwitchConditionalOperator(Key inKey, out char key, bool shift)
    {
        开关(inKey)
        {
           案例Key.A:键=shift? 'A' : 'a';返回真;
           案例 Key.B: 键 = Shift ? 'B':'b';返回真;
           案例Key.C:键=shift? “C”:“C”;返回真;
           ...
           case Key.Y: 键 = Shift ? 'Y':'y';返回真;
           案例 Key.Z: 键 = Shift ? 'Z':'z';返回真;
           ...
           //更多带有特殊键的情况...
        }
        键=(字符)0;
        返回假;
    }
    
  3. 使用预先填充键/字符对的字典进行扭曲:

    public static bool DictionaryLookup(Key inKey, out char key, bool shift)
    {
        键='\0';
        如果(移位)
            return _upperKeys.TryGetValue(inKey, out key);
        别的
            return _lowerKeys.TryGetValue(inKey, out key);
    }
    

注意:两个 switch 语句具有完全相同的大小写,并且字典具有相同数量的字符。

我预计 1) 和 2) 在性能上有些相似,而 3) 会稍微慢一些。

对于每个方法运行两次 10.000.000 次迭代进行预热然后计时,令我惊讶的是,我得到以下结果:

  1. 每次调用 0.0000166 毫秒 每次调用
  2. 0.0000779 毫秒 每次调用
  3. 0.0000413 毫秒

这是怎么回事?条件运算符比 if-else 语句慢四倍,​​比字典查找慢几乎两倍。我在这里遗漏了一些重要的东西还是条件运算符本质上很慢?

更新 1:关于我的测试工具的几句话。我在 Visual Studio 2010 中的 Release 编译的 .Net 3.5 项目下为上述每个变体运行以下(伪)代码。代码优化已打开,DEBUG/TRACE 常量已关闭。在进行定时运行之前,我在测量下运行该方法一次以进行热身。 run 方法执行该方法进行大量迭代,将 shift 设置为 true 和 false,并使用一组选定的输入键:

Run(method);
var stopwatch = Stopwatch.StartNew();
Run(method);
stopwatch.Stop();
var measure = stopwatch.ElapsedMilliseconds / iterations;

Run 方法如下所示:

for (int i = 0; i < iterations / 4; i++)
{
    method(Key.Space, key, true);
    method(Key.A, key, true);
    method(Key.Space, key, false);
    method(Key.A, key, false);
}

更新 2: 进一步挖掘,我查看了为 1) 和 2) 生成的 IL,发现主开关结构与我预期的相同,但外壳体略有不同。这是我正在查看的 IL:

1) If/else 语句:

L_0167: ldarg.2 
L_0168: brfalse.s L_0170

L_016a: ldarg.1 
L_016b: ldc.i4.s 0x42
L_016d: stind.i2 
L_016e: br.s L_0174

L_0170: ldarg.1 
L_0171: ldc.i4.s 0x62
L_0173: stind.i2 

L_0174: ldc.i4.1 
L_0175: ret 

2) 条件运算符:

L_0165: ldarg.1 
L_0166: ldarg.2 
L_0167: brtrue.s L_016d

L_0169: ldc.i4.s 0x62
L_016b: br.s L_016f

L_016d: ldc.i4.s 0x42
L_016f: stind.i2 

L_0170: ldc.i4.1 
L_0171: ret 

一些观察:

  • 条件运算符在 shift 等于 true 时分支,而 if/else 在 时分支移位 为假。
  • 虽然 1) 实际上比 2) 编译为多一些指令,但当 shift 为 true 或 false 时执行的指令数对于两者来说是相等的。
  • 1) 的指令顺序是始终只占用一个堆栈槽,而 2) 始终加载两个堆栈槽。

这些观察结果是否意味着条件运算符的执行速度会变慢?还有其他副作用吗?

I was looking at some code with a huge switch statement and an if-else statement on each case and instantly felt the urge to optimize. As a good developer always should do I set out to get some hard timing facts and started with three variants:

  1. The original code looks like this:

    public static bool SwitchIfElse(Key inKey, out char key, bool shift)
    {
        switch (inKey)
        {
           case Key.A: if (shift) { key = 'A'; } else { key = 'a'; } return true;
           case Key.B: if (shift) { key = 'B'; } else { key = 'b'; } return true;
           case Key.C: if (shift) { key = 'C'; } else { key = 'c'; } return true;
           ...
           case Key.Y: if (shift) { key = 'Y'; } else { key = 'y'; } return true;
           case Key.Z: if (shift) { key = 'Z'; } else { key = 'z'; } return true;
           ...
           //some more cases with special keys...
        }
        key = (char)0;
        return false;
    }
    
  2. The second variant converted to use the conditional operator:

    public static bool SwitchConditionalOperator(Key inKey, out char key, bool shift)
    {
        switch (inKey)
        {
           case Key.A: key = shift ? 'A' : 'a'; return true;
           case Key.B: key = shift ? 'B' : 'b'; return true;
           case Key.C: key = shift ? 'C' : 'c'; return true;
           ...
           case Key.Y: key = shift ? 'Y' : 'y'; return true;
           case Key.Z: key = shift ? 'Z' : 'z'; return true;
           ...
           //some more cases with special keys...
        }
        key = (char)0;
        return false;
    }
    
  3. A twist using a dictionary pre-filled with key/character pairs:

    public static bool DictionaryLookup(Key inKey, out char key, bool shift)
    {
        key = '\0';
        if (shift)
            return _upperKeys.TryGetValue(inKey, out key);
        else
            return _lowerKeys.TryGetValue(inKey, out key);
    }
    

Note: the two switch statements have the exact same cases and the dictionaries have an equal amount of characters.

I was expecting that 1) and 2) was somewhat similar in performance and that 3) would be slightly slower.

For each method running two times 10.000.000 iterations for warm-up and then timed, to my amazement I get the following results:

  1. 0.0000166 milliseconds per call
  2. 0.0000779 milliseconds per call
  3. 0.0000413 milliseconds per call

How can this be? The conditional operator is four times slower than if-else statements and almost two times slower than dictionary look-ups. Am I missing something essential here or is the conditional operator inherently slow?

Update 1: A few words about my test harness. I run the following (pseudo)code for each of the above variants under a Release compiled .Net 3.5 project in Visual Studio 2010. Code optimization is turned on and DEBUG/TRACE constants are turned off. I run the method under measurement once for warm-up before doing a timed run. The run method executed the method for a large number of iterations, with shift set to both true and false and with a select set of input keys:

Run(method);
var stopwatch = Stopwatch.StartNew();
Run(method);
stopwatch.Stop();
var measure = stopwatch.ElapsedMilliseconds / iterations;

The Run method looks like this:

for (int i = 0; i < iterations / 4; i++)
{
    method(Key.Space, key, true);
    method(Key.A, key, true);
    method(Key.Space, key, false);
    method(Key.A, key, false);
}

Update 2: Digging further, I have looked at the IL generated for 1) and 2) and find that the main switch structures are identical as I would expect, yet the case bodies have slight differences. Here is the IL I'm looking at:

1) If/else statement:

L_0167: ldarg.2 
L_0168: brfalse.s L_0170

L_016a: ldarg.1 
L_016b: ldc.i4.s 0x42
L_016d: stind.i2 
L_016e: br.s L_0174

L_0170: ldarg.1 
L_0171: ldc.i4.s 0x62
L_0173: stind.i2 

L_0174: ldc.i4.1 
L_0175: ret 

2) The Conditional Operator:

L_0165: ldarg.1 
L_0166: ldarg.2 
L_0167: brtrue.s L_016d

L_0169: ldc.i4.s 0x62
L_016b: br.s L_016f

L_016d: ldc.i4.s 0x42
L_016f: stind.i2 

L_0170: ldc.i4.1 
L_0171: ret 

Some observations:

  • The conditional operator branches when shift equals true while if/else branches when shift is false.
  • While 1) actually compiles to a few more instructions than 2), the number of instructions executed when shift is either true or false, are equal for the two.
  • The instruction ordering for 1) is such that only one stack slot is occupied at all times, while 2) always loads two.

Do any of these observations imply that the conditional operator will perform slower? Is there other side-effects that come into play?

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

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

发布评论

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

评论(8

我们只是彼此的过ke 2024-08-28 15:02:57

很奇怪,也许 .NET 优化在您的情况下会适得其反:

作者拆解了几个
三元表达式的版本和
发现它们相同
if 语句,带有一个小
不同之处。三元语句
有时会生成测试的代码
与你相反的条件
期望,因为它测试了
子表达式为 false 而不是
测试是否属实。这重新排序
一些说明并可以
偶尔会提高性能。

http://dotnetperls.com/ternary

您可能会考虑枚举值上的 ToString(对于非特殊案例):

string keyValue = inKey.ToString();
return shift ? keyValue : keyValue.ToLower();

编辑:
我将 if-else 方法与三元运算符进行了比较,在 1000000 个周期中,三元运算符始终至少与 if-else 方法一样快(有时快几毫秒,这支持上面的文本)。我认为您在测量所用时间时犯了某种错误。

Very odd, perhaps .NET optimization is backfireing in your case:

The author disassembled several
versions of ternary expressions and
found that they are identical to
if-statements, with one small
difference. The ternary statement
sometimes produces code that tests the
opposite condition that you would
expect, as in it tests that the
subexpression is false instead of
testing if it is true. This reorders
some of the instructions and can
occasionally boost performance.

http://dotnetperls.com/ternary

You want might consider the ToString on the enum value (for the non-special cases):

string keyValue = inKey.ToString();
return shift ? keyValue : keyValue.ToLower();

EDIT:
I've compared the if-else method with the ternary operator and with 1000000 cycles the ternary operator is always at least as fast as the if-else method (sometimes a few millisec faster, which supports the text above). I think that you've made somekind of error in measuring the time it took.

强者自强 2024-08-28 15:02:57

我很想知道您是否正在使用调试或发布版本来测试它。如果它是调试版本,那么差异很可能是由于编译器在使用发布模式(或手动禁用调试模式并启用编译器优化)时添加的低级优化而导致的差异。

我希望然而,优化是三元运算符的速度与 if/else 语句相同或稍快,而字典查找最慢。以下是我的结果,每次进行 1000 万次预热迭代,然后进行 1000 万次计时:

DEBUG MODE

   If/Else: 00:00:00.7211259
   Ternary: 00:00:00.7923924
Dictionary: 00:00:02.3319567

RELEASE MODE

   If/Else: 00:00:00.5217478
   Ternary: 00:00:00.5050474
Dictionary: 00:00:02.7389423

我认为值得注意的是,在优化之前启用后,三元计算比 if/else 慢,而启用后则更快。

编辑:

经过更多的测试,在实际意义上,if/else 和三元之间几乎没有区别。虽然三进制代码会产生较小的 IL,但它们的性能几乎相同。在使用发布模式二进制的十几个不同测试中,if/else 和三元结果要么相同,要么在 10,000,000 次迭代中相差不到一毫秒。有时 if/else 稍快,有时三元则稍快,但在所有实用性中,它们的性能相同。

另一方面,字典的表现则明显较差。当涉及到这些类型的优化时,如果代码已经存在,我不会浪费时间在 if/else 和三元之间进行选择。但是,如果您当前有一个字典实现,我肯定会重构它以使用更有效的方法,并将性能提高约 400%(无论如何,对于给定的函数)。

I would be curious to know if you are testing this with a Debug or Release build. If it is a debug build, then the difference could quite likely be a difference due to the LACK of low-level optimizations that the compiler adds when you use Release mode (or manually disable debug mode and enable compiler optimizations.)

I would expect with optimizations on, however, that the ternary operator is either the same speed or a bit faster than the if/else statement, while the dictionary lookup is slowest. Here are my results, 10 million warm-up iterations followed by 10 million timed, for each:

DEBUG MODE

   If/Else: 00:00:00.7211259
   Ternary: 00:00:00.7923924
Dictionary: 00:00:02.3319567

RELEASE MODE

   If/Else: 00:00:00.5217478
   Ternary: 00:00:00.5050474
Dictionary: 00:00:02.7389423

I think it is interesting to note here that before optimizations were enabled, ternary computation was slower than if/else, while after, it was faster.

EDIT:

After a bit more testing, in a practical sense, there is little to no difference between if/else and ternary. While the ternary code results in smaller IL, they perform pretty much the same as each other. In a dozen different tests with a release mode binary, the if/else and ternary results were either identical, or off by a fraction of a millisecond for 10,000,000 iterations. Sometimes if/else was slightly faster, sometimes ternary was, but in all practicality, they perform the same.

Dictionary performs significantly worse, on the other hand. When it comes to these kinds of optimizations, I would not waste my time choosing between if/else and ternary if the code already exists. However, if you currently have a dictionary implementation, I would definitely refactor it to use a more efficient approach, and improve your performance by some 400% (for the given function, anyway.)

牵你的手,一向走下去 2024-08-28 15:02:57

有趣的是,我在这里开发了一个小类 IfElseTernaryTest ,好吧,代码并不是真正的“优化”或很好的例子,但尽管如此......为了讨论:

public class IfElseTernaryTest
{
    private bool bigX;
    public void RunIfElse()
    {
        int x = 4; int y = 5;
        if (x > y) bigX = false;
        else if (x < y) bigX = true; 
    }
    public void RunTernary()
    {
        int x = 4; int y = 5;
        bigX = (x > y) ? false : ((x < y) ? true : false);
    }
}

这是 IL 转储代码的...有趣的部分是 IL 中的三元指令实际上比 if 短...

.class /*02000003*/ public auto ansi beforefieldinit ConTern.IfElseTernaryTest
       extends [mscorlib/*23000001*/]System.Object/*01000001*/
{
  .field /*04000001*/ private bool bigX
  .method /*06000003*/ public hidebysig instance void 
          RunIfElse() cil managed
  // SIG: 20 00 01
  {
    // Method begins at RVA 0x205c
    // Code size       44 (0x2c)
    .maxstack  2
    .locals /*11000001*/ init ([0] int32 x,
             [1] int32 y,
             [2] bool CS$4$0000)
    .line 19,19 : 9,10 ''
//000013:     }
//000014: 
//000015:     public class IfElseTernaryTest
//000016:     {
//000017:         private bool bigX;
//000018:         public void RunIfElse()
//000019:         {
    IL_0000:  /* 00   |                  */ nop
    .line 20,20 : 13,23 ''
//000020:             int x = 4; int y = 5;
    IL_0001:  /* 1A   |                  */ ldc.i4.4
    IL_0002:  /* 0A   |                  */ stloc.0
    .line 20,20 : 24,34 ''
    IL_0003:  /* 1B   |                  */ ldc.i4.5
    IL_0004:  /* 0B   |                  */ stloc.1
    .line 21,21 : 13,23 ''
//000021:             if (x > y) bigX = false;
    IL_0005:  /* 06   |                  */ ldloc.0
    IL_0006:  /* 07   |                  */ ldloc.1
    IL_0007:  /* FE02 |                  */ cgt
    IL_0009:  /* 16   |                  */ ldc.i4.0
    IL_000a:  /* FE01 |                  */ ceq
    IL_000c:  /* 0C   |                  */ stloc.2
    IL_000d:  /* 08   |                  */ ldloc.2
    IL_000e:  /* 2D   | 09               */ brtrue.s   IL_0019

    .line 21,21 : 24,37 ''
    IL_0010:  /* 02   |                  */ ldarg.0
    IL_0011:  /* 16   |                  */ ldc.i4.0
    IL_0012:  /* 7D   | (04)000001       */ stfld      bool ConTern.IfElseTernaryTest/*02000003*/::bigX /* 04000001 */
    IL_0017:  /* 2B   | 12               */ br.s       IL_002b

    .line 22,22 : 18,28 ''
//000022:             else if (x < y) bigX = true; 
    IL_0019:  /* 06   |                  */ ldloc.0
    IL_001a:  /* 07   |                  */ ldloc.1
    IL_001b:  /* FE04 |                  */ clt
    IL_001d:  /* 16   |                  */ ldc.i4.0
    IL_001e:  /* FE01 |                  */ ceq
    IL_0020:  /* 0C   |                  */ stloc.2
    IL_0021:  /* 08   |                  */ ldloc.2
    IL_0022:  /* 2D   | 07               */ brtrue.s   IL_002b

    .line 22,22 : 29,41 ''
    IL_0024:  /* 02   |                  */ ldarg.0
    IL_0025:  /* 17   |                  */ ldc.i4.1
    IL_0026:  /* 7D   | (04)000001       */ stfld      bool ConTern.IfElseTernaryTest/*02000003*/::bigX /* 04000001 */
    .line 23,23 : 9,10 ''
//000023:         }
    IL_002b:  /* 2A   |                  */ ret
  } // end of method IfElseTernaryTest::RunIfElse

  .method /*06000004*/ public hidebysig instance void 
          RunTernary() cil managed
  // SIG: 20 00 01
  {
    // Method begins at RVA 0x2094
    // Code size       27 (0x1b)
    .maxstack  3
    .locals /*11000002*/ init ([0] int32 x,
             [1] int32 y)
    .line 25,25 : 9,10 ''
//000024:         public void RunTernary()
//000025:         {
    IL_0000:  /* 00   |                  */ nop
    .line 26,26 : 13,23 ''
//000026:             int x = 4; int y = 5;
    IL_0001:  /* 1A   |                  */ ldc.i4.4
    IL_0002:  /* 0A   |                  */ stloc.0
    .line 26,26 : 24,34 ''
    IL_0003:  /* 1B   |                  */ ldc.i4.5
    IL_0004:  /* 0B   |                  */ stloc.1
    .line 27,27 : 13,63 ''
//000027:             bigX = (x > y) ? false : ((x < y) ? true : false);
    IL_0005:  /* 02   |                  */ ldarg.0
    IL_0006:  /* 06   |                  */ ldloc.0
    IL_0007:  /* 07   |                  */ ldloc.1
    IL_0008:  /* 30   | 0A               */ bgt.s      IL_0014

    IL_000a:  /* 06   |                  */ ldloc.0
    IL_000b:  /* 07   |                  */ ldloc.1
    IL_000c:  /* 32   | 03               */ blt.s      IL_0011

    IL_000e:  /* 16   |                  */ ldc.i4.0
    IL_000f:  /* 2B   | 01               */ br.s       IL_0012

    IL_0011:  /* 17   |                  */ ldc.i4.1
    IL_0012:  /* 2B   | 01               */ br.s       IL_0015

    IL_0014:  /* 16   |                  */ ldc.i4.0
    IL_0015:  /* 7D   | (04)000001       */ stfld      bool ConTern.IfElseTernaryTest/*02000003*/::bigX /* 04000001 */
    .line 28,28 : 9,10 ''
//000028:         }
    IL_001a:  /* 2A   |                  */ ret
  } // end of method IfElseTernaryTest::RunTernary

所以看来,三元运算符显然更短,我猜,更快使用较少的指令...但在此基础上,它似乎与您的情况 #2 相矛盾,这令人惊讶...

编辑:在 Sky 的评论后,建议“#2 的代码膨胀”,这将反驳Sky所说的!!!好的,代码不同,上下文不同,这是一个检查 IL 转储以查看的示例练习...

Interesting, I went off and developed a small class IfElseTernaryTest here, ok, the code is not really 'optimized' or good example but nonetheless...for the sake of the discussion:

public class IfElseTernaryTest
{
    private bool bigX;
    public void RunIfElse()
    {
        int x = 4; int y = 5;
        if (x > y) bigX = false;
        else if (x < y) bigX = true; 
    }
    public void RunTernary()
    {
        int x = 4; int y = 5;
        bigX = (x > y) ? false : ((x < y) ? true : false);
    }
}

This was the IL dump of the code...the interesting part was that the ternary instructions in IL was actually shorter than the if....

.class /*02000003*/ public auto ansi beforefieldinit ConTern.IfElseTernaryTest
       extends [mscorlib/*23000001*/]System.Object/*01000001*/
{
  .field /*04000001*/ private bool bigX
  .method /*06000003*/ public hidebysig instance void 
          RunIfElse() cil managed
  // SIG: 20 00 01
  {
    // Method begins at RVA 0x205c
    // Code size       44 (0x2c)
    .maxstack  2
    .locals /*11000001*/ init ([0] int32 x,
             [1] int32 y,
             [2] bool CS$4$0000)
    .line 19,19 : 9,10 ''
//000013:     }
//000014: 
//000015:     public class IfElseTernaryTest
//000016:     {
//000017:         private bool bigX;
//000018:         public void RunIfElse()
//000019:         {
    IL_0000:  /* 00   |                  */ nop
    .line 20,20 : 13,23 ''
//000020:             int x = 4; int y = 5;
    IL_0001:  /* 1A   |                  */ ldc.i4.4
    IL_0002:  /* 0A   |                  */ stloc.0
    .line 20,20 : 24,34 ''
    IL_0003:  /* 1B   |                  */ ldc.i4.5
    IL_0004:  /* 0B   |                  */ stloc.1
    .line 21,21 : 13,23 ''
//000021:             if (x > y) bigX = false;
    IL_0005:  /* 06   |                  */ ldloc.0
    IL_0006:  /* 07   |                  */ ldloc.1
    IL_0007:  /* FE02 |                  */ cgt
    IL_0009:  /* 16   |                  */ ldc.i4.0
    IL_000a:  /* FE01 |                  */ ceq
    IL_000c:  /* 0C   |                  */ stloc.2
    IL_000d:  /* 08   |                  */ ldloc.2
    IL_000e:  /* 2D   | 09               */ brtrue.s   IL_0019

    .line 21,21 : 24,37 ''
    IL_0010:  /* 02   |                  */ ldarg.0
    IL_0011:  /* 16   |                  */ ldc.i4.0
    IL_0012:  /* 7D   | (04)000001       */ stfld      bool ConTern.IfElseTernaryTest/*02000003*/::bigX /* 04000001 */
    IL_0017:  /* 2B   | 12               */ br.s       IL_002b

    .line 22,22 : 18,28 ''
//000022:             else if (x < y) bigX = true; 
    IL_0019:  /* 06   |                  */ ldloc.0
    IL_001a:  /* 07   |                  */ ldloc.1
    IL_001b:  /* FE04 |                  */ clt
    IL_001d:  /* 16   |                  */ ldc.i4.0
    IL_001e:  /* FE01 |                  */ ceq
    IL_0020:  /* 0C   |                  */ stloc.2
    IL_0021:  /* 08   |                  */ ldloc.2
    IL_0022:  /* 2D   | 07               */ brtrue.s   IL_002b

    .line 22,22 : 29,41 ''
    IL_0024:  /* 02   |                  */ ldarg.0
    IL_0025:  /* 17   |                  */ ldc.i4.1
    IL_0026:  /* 7D   | (04)000001       */ stfld      bool ConTern.IfElseTernaryTest/*02000003*/::bigX /* 04000001 */
    .line 23,23 : 9,10 ''
//000023:         }
    IL_002b:  /* 2A   |                  */ ret
  } // end of method IfElseTernaryTest::RunIfElse

  .method /*06000004*/ public hidebysig instance void 
          RunTernary() cil managed
  // SIG: 20 00 01
  {
    // Method begins at RVA 0x2094
    // Code size       27 (0x1b)
    .maxstack  3
    .locals /*11000002*/ init ([0] int32 x,
             [1] int32 y)
    .line 25,25 : 9,10 ''
//000024:         public void RunTernary()
//000025:         {
    IL_0000:  /* 00   |                  */ nop
    .line 26,26 : 13,23 ''
//000026:             int x = 4; int y = 5;
    IL_0001:  /* 1A   |                  */ ldc.i4.4
    IL_0002:  /* 0A   |                  */ stloc.0
    .line 26,26 : 24,34 ''
    IL_0003:  /* 1B   |                  */ ldc.i4.5
    IL_0004:  /* 0B   |                  */ stloc.1
    .line 27,27 : 13,63 ''
//000027:             bigX = (x > y) ? false : ((x < y) ? true : false);
    IL_0005:  /* 02   |                  */ ldarg.0
    IL_0006:  /* 06   |                  */ ldloc.0
    IL_0007:  /* 07   |                  */ ldloc.1
    IL_0008:  /* 30   | 0A               */ bgt.s      IL_0014

    IL_000a:  /* 06   |                  */ ldloc.0
    IL_000b:  /* 07   |                  */ ldloc.1
    IL_000c:  /* 32   | 03               */ blt.s      IL_0011

    IL_000e:  /* 16   |                  */ ldc.i4.0
    IL_000f:  /* 2B   | 01               */ br.s       IL_0012

    IL_0011:  /* 17   |                  */ ldc.i4.1
    IL_0012:  /* 2B   | 01               */ br.s       IL_0015

    IL_0014:  /* 16   |                  */ ldc.i4.0
    IL_0015:  /* 7D   | (04)000001       */ stfld      bool ConTern.IfElseTernaryTest/*02000003*/::bigX /* 04000001 */
    .line 28,28 : 9,10 ''
//000028:         }
    IL_001a:  /* 2A   |                  */ ret
  } // end of method IfElseTernaryTest::RunTernary

So it seems, that ternary operator is apparently shorter and I would guess, faster as less instructions is used...but on that basis, it seems to contradict your case #2 which is surprising...

Edit: After Sky's comment, suggesting 'code bloat for #2', this will disprove what Sky said!!! Ok, the Code is different, the context is different, it's an example exercise to check the IL dump to see...

做个ˇ局外人 2024-08-28 15:02:57

我希望#1 和#2 是相同的。优化器应该产生相同的代码。 #3 中的字典预计会很慢,除非它以某种方式优化为不实际使用哈希。

在对实时系统进行编码时,我们总是使用查找表(一个简单的数组)来进行转换,如示例中给出的那样。当输入范围相当小时,它是最快的。

I would expect #1 and #2 to be the same. The optimizer should result in the same code. The dictionary in #3 would expected to be slow, unless it is optimized somehow to not actual use a hash.

When coding real-time systems, we always used a look-up table--a simple array--to translate as given in your example. It's the fastest when the range of input is fairly small.

悸初 2024-08-28 15:02:57

我不太明白为什么您会期望 if 语句比字典查找慢。至少需要计算哈希码,然后需要在列表中查找它。我不明白为什么你会认为这比 cmp/jmp 更快。

具体来说,我什至不认为你正在优化的方法有那么好;似乎可以在调用阶段做得更好(尽管我不能确定,因为您没有提供上下文)。

I don't quite understand why you would expect an if statement to be slower than a dictionary lookup. At the very least a hashcode needs to be calculated and then it needs to be looked up in a list. I don't see why you would assume this is faster than a cmp/jmp.

Specifically, I don't even think the method you're optimising is that great; it seems that it could be made better at the calling stage (though I can't be sure, as you haven't provided the context).

我家小可爱 2024-08-28 15:02:57

假设您担心该方法的性能(如果您不担心,为什么还要发布它?),您应该考虑将 char 值存储在数组中并转换 Key 值到数组的索引。

Assuming you're concerned about the performance of that method (and if you're not, why bother posting it?), you should consider storing the char values in an array and converting the Key values to an index into the array.

原谅我要高飞 2024-08-28 15:02:57

我手头没有 VS,但是肯定有一种简单的内置方法可以将密钥作为角色获取?类似于 toString 方法,因此您可以用以下方法替换那个可怕的 switch

if (shift)
  return inKey.toString().toUppercase();
else
  return inKey.toString().toLowercase();

I don't have VS to hand but surely there's a simple built-in way to get the key as a character? Something like a toString method so you can replace that monstrous switch with this:

if (shift)
  return inKey.toString().toUppercase();
else
  return inKey.toString().toLowercase();
烟─花易冷 2024-08-28 15:02:57

我会选择第三个选项只是因为它更具可读性/可维护性。
我敢打赌这段代码不是您的应用程序性能的瓶颈。

I would choose the third option only because it is more readable/maintainable.
I bet this code isn't the bottleneck of your application performance.

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