我有一个变量a
,它只能有两个值x1
或x2
. 如何a
在这些值之间切换。我想出了这个。还有其他更有效的方法吗?
a = (a == x1 ? x2: x1);
它(极)不太可能成为您的瓶颈,但您可以使用以下XOR
方法:
togglex1x2 = (x1 ^ x2); // This is the combined toggle value
a = x1; // Initialise to either x1 or x2
a ^= togglex1x2; // toggles
a ^= togglex1x2; // toggles
...
[您应该首先编写易于理解的代码,并且仅在您测量到瓶颈时才进行优化(然后仔细检查它是否在您认为的位置!),如果您进行优化,请确保您用推理进行评论。]
尝试这样的事情。a 将在 x1 和 x2 之间切换
a = (x1 + x2) - a;
在没有上下文的情况下很难预测哪种方法更好 - 最大的未知数是此操作在哪种方式中至关重要 - 是否存在延迟限制(例如,如果您正在使用通过此代码的数据依赖关系进行长时间计算) ,或者可能是带宽关键(您正在交换许多不相关的元素,并开始精简资源)。
试图对这里提出的解决方案进行基准测试。请参阅此代码,例如:
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
最后,反转:)
这表明您可以根据程序中给定操作的使用方式获得不同的性能结果。在没有其余代码的情况下对单个方法进行基准测试没有多大意义(并不是我们不这样做,只是您需要用一大块盐来获取任何结果)。
当然,这一切都是为了讨论,很可能这段代码甚至都不是瓶颈。
另一种方法是在 0 和 1 之间切换索引并使用它来索引数组:
int main() {
int const values[] = {0x55, 0xaa};
int selector = 0;
selector ^= 1; // toggle index
int value = values[selector]; // select value
}
所以你对它进行了基准测试,这就是瓶颈,对吧?
哦,好吧,不......然后忘记效率。这已经是一个非常小的表达式,可以快速评估。
顺便说一句,还有其他方法,但我不确定 1. 它们真的更快,2. 如果它们更快,那真的很重要, 3. 如果它们更慢,可读性损失是值得的权衡。
例如:
#define FIRST 42
#define SECOND 1337
/* initialize */
int x = FIRST;
/* toggle */
x = FIRST + SECOND - x;