4

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

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

5 回答 5

15

它(极)不太可能成为您的瓶颈,但您可以使用以下XOR方法:

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

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

a ^= togglex1x2;   // toggles

a ^= togglex1x2;   // toggles

...

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

于 2013-09-22T06:30:31.933 回答
8

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

a = (x1 + x2) - a;
于 2013-09-22T06:26:59.330 回答
4

在没有上下文的情况下很难预测哪种方法更好 - 最大的未知数是此操作在哪种方式中至关重要 - 是否存在延迟限制(例如,如果您正在使用通过此代码的数据依赖关系进行长时间计算) ,或者可能是带宽关键(您正在交换许多不相关的元素,并开始精简资源)。

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

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, with -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

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

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

first: 0.106000s
second: 0.066000s

但!由于一个简单的加法本身并不是很耗时,因此尝试使用倒数(浮点数和+= 1/a) - 这个真的需要快速的数据:

first: 0.014000s
second: 0.087000s

最后,反转:)

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

当然,这一切都是为了讨论,很可能这段代码甚至都不是瓶颈。

于 2013-09-22T08:15:32.400 回答
4

另一种方法是在 0 和 1 之间切换索引并使用它来索引数组:

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

    selector ^= 1;                // toggle index
    int value = values[selector]; // select value
}
于 2013-09-22T10:01:48.730 回答
2

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

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

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

例如:

#define FIRST 42
#define SECOND 1337

/* initialize */
int x = FIRST;

/* toggle */
x = FIRST + SECOND - x;
于 2013-09-22T06:28:33.823 回答