在两个值之间切换整数变量的最简单方法

时间:2013-09-22 06:25:11

标签: c

我有一个变量a,它只能有两个值x1x2。如何在这些值之间切换a。我想出了这个。还有其他更有效的方法吗?

a = (a == x1 ? x2: x1);

5 个答案:

答案 0 :(得分:13)

(高度)不太可能是你的瓶颈,但你可以使用XOR方法:

togglex1x2 = (x1 ^ x2);     // This is the combined toggle value

a = x1;            // Initialise to either x1 or x2

a ^= togglex1x2;   // toggles

a ^= togglex1x2;   // toggles

...

[你应该首先编写可以理解的代码,并且只有在你测量了瓶颈时才进行优化(然后仔细检查它是你认为的位置!),如果你进行了优化,请确保用推理进行评论。 ]

答案 1 :(得分:6)

尝试这样的事情。将在x1和x2之间切换

a = (x1 + x2) - a;

答案 2 :(得分:3)

如果没有上下文,很难预测哪种方法更好 - 最大的未知是这个操作的关键方式 - 是否是延迟限制(例如,如果你在进行长时间的数据依赖性计算时)这个代码),或者可能是带宽关键(你正在交换许多不相关的元素,并开始运行你的资源)。

尝试对此处提出的解决方案进行基准测试。 请参阅此代码,例如:

int main()
{
  int x1 = 123, x2 = 456;
  int x1_xor_x2 = x1 ^ x2;
  int a = x1;
  int i;

  for (i = 0; i < 10000; ++i)
      a = (a == x1 ? x2: x1);

  for (i = 0; i < 10000; ++i)
      a ^= x1_xor_x2;

  printf ("a=%d\n", a); // prevent all this from being optimized out
}

成为(gcc,带-O3):

0000000000400440 <main>:
 400440:       b8 10 27 00 00          mov    $0x2710,%eax         // loop counter
 400445:       ba c8 01 00 00          mov    $0x1c8,%edx         
 40044a:       be 7b 00 00 00          mov    $0x7b,%esi           // 123 in esi
 40044f:       b9 c8 01 00 00          mov    $0x1c8,%ecx          // 456 in ecx
 400454:       eb 12                   jmp    400468 <main+0x28>   
 400456:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 40045d:       00 00 00 

 400460:       83 fa 7b                cmp    $0x7b,%edx
 400463:       89 ca                   mov    %ecx,%edx
 400465:       0f 45 d6                cmovne %esi,%edx            // conditional move
 400468:       83 e8 01                sub    $0x1,%eax
 40046b:       75 f3                   jne    400460 <main+0x20>

 40046d:       b8 10 27 00 00          mov    $0x2710,%eax
 400472:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

 400478:       81 f2 b3 01 00 00       xor    $0x1b3,%edx
 40047e:       83 e8 01                sub    $0x1,%eax            // xoring
 400481:       75 f5                   jne    400478 <main+0x38>

 400483:       be 6c 06 40 00          mov    $0x40066c,%esi
 400488:       bf 01 00 00 00          mov    $0x1,%edi
 40048d:       31 c0                   xor    %eax,%eax
 40048f:       e9 9c ff ff ff          jmpq   400430 <__printf_chk@plt>

在添加时间检查(并将循环次数增加到100M)后,我上了服务器(AMD Opteron 6272):

first: 0.089000s
second: 0.067000s
a=123

这不是很有趣,因为没有消费者需要低延迟数据(所以计算可能缓冲,我们正在检查ALU BW,而不是延迟)

尝试在每次迭代时添加sum += a会导致delta增加,有利于第一次 -

first: 0.106000s
second: 0.066000s

但是!因为简单的添加本身并不是非常耗时,尝试使用倒数(浮点数和+= 1/a) - 这个确实需要快速数据:

first: 0.014000s
second: 0.087000s

最后,倒置:))

这表明根据给定操作的方式,您可以获得不同的性能结果 在你的程序中使用。在没有其余代码的情况下对单个方法进行基准测试没有太大意义(不是我们不这样做,只是你需要用一大块盐来获取任何结果)。

当然,这只是为了讨论,很可能这段代码甚至不是远程瓶颈......

答案 3 :(得分:3)

另一种方法是在0和1之间切换索引,并用以下内容索引数组:

int main() {
    int const values[] = {0x55, 0xaa};
    int selector = 0;

    selector ^= 1;                // toggle index
    int value = values[selector]; // select value
}

答案 4 :(得分:2)

所以你对它进行了基准测试,这是瓶颈,对吗?

哦,好吧,没有...然后忘记效率。这已经是一个非常小的表达式,可以快速评估。

顺便说一句,还有其他方法,但我不确定1.它们真的更快,2。如果它们更快,它真的很重要,3。如果它们较慢,可读性惩罚值得权衡

例如:

#define FIRST 42
#define SECOND 1337

/* initialize */
int x = FIRST;

/* toggle */
x = FIRST + SECOND - x;