如何让 GCC 优化长 XOR 链
How to get GCC to optimize long XOR chain
我有一个像这样的循环:
uint32_t result = 0;
for ( int i = 0; i < CONSTANT; ++i )
{
result ^= expr;
}
return result;
总的来说,GCC 使用这段代码做得很好。它完全展开循环并为 expr
生成最佳代码。但是,它执行 result
XOR CONSTANT
次。它可能会累积部分结果并将它们分层异或。
我怀疑如果我用宏手动展开这个我可以手动完成(CONSTANT
并不大),但我想知道为什么它看不到这个,或者如果我由于一些神秘的 C++ 语言规则,正在做一些阻止它的事情。
在这里累积部分结果可能没有任何好处。如果您使用分而治之的策略(异或偶数使大小减半,然后重复,每次将操作数减半),您最终仍然会做 O(CONSTANT)
工作(一半的工作加上四分之一的工作加上八分之一的工作等等,最终执行 CONSTANT - 1
操作)。
在块中累积部分结果的行为相同。从根本上说,您必须进行 CONSTANT - 1
异或运算。由于这些是固定宽度的寄存器,而不是增长任意精度的整数,因此每个 XOR 的工作是相同的。除非并行化 expr
工作,否则您极不可能从更复杂的方法中获得任何收益。
对于您的循环,expr
不依赖于 i
,在这种情况下 gcc
应该完全优化循环1,或者在这种情况下 gcc
可以 仍然 优化它(因为循环边界是常数,整个循环可以是 pre-calculated)。
好像是fails in the latter case though, _unless you optimize for -march=haswell
. That seems really weird, but I've seen exactly that kind of behavior before.
无论如何2,你提到expr
编译成两条指令。为 xor
、循环增量和测试指令添加 3 条指令,这个循环已经有 5 条指令,这甚至超过了 high-end x86 CPU 的报废率,因此没有任何好处在这里寻找额外的指令级并行性(除非你可能正在编译到宽度更高的非 x86 arch?)。
1 ...而且它一般都会在 -O3
无论如何。
2 我们只需要猜测就可以了,因为你真的在严密保护expr
的秘密。
我有一个像这样的循环:
uint32_t result = 0;
for ( int i = 0; i < CONSTANT; ++i )
{
result ^= expr;
}
return result;
总的来说,GCC 使用这段代码做得很好。它完全展开循环并为 expr
生成最佳代码。但是,它执行 result
XOR CONSTANT
次。它可能会累积部分结果并将它们分层异或。
我怀疑如果我用宏手动展开这个我可以手动完成(CONSTANT
并不大),但我想知道为什么它看不到这个,或者如果我由于一些神秘的 C++ 语言规则,正在做一些阻止它的事情。
在这里累积部分结果可能没有任何好处。如果您使用分而治之的策略(异或偶数使大小减半,然后重复,每次将操作数减半),您最终仍然会做 O(CONSTANT)
工作(一半的工作加上四分之一的工作加上八分之一的工作等等,最终执行 CONSTANT - 1
操作)。
在块中累积部分结果的行为相同。从根本上说,您必须进行 CONSTANT - 1
异或运算。由于这些是固定宽度的寄存器,而不是增长任意精度的整数,因此每个 XOR 的工作是相同的。除非并行化 expr
工作,否则您极不可能从更复杂的方法中获得任何收益。
对于您的循环,expr
不依赖于 i
,在这种情况下 gcc
应该完全优化循环1,或者在这种情况下 gcc
可以 仍然 优化它(因为循环边界是常数,整个循环可以是 pre-calculated)。
好像是fails in the latter case though, _unless you optimize for -march=haswell
. That seems really weird, but I've seen exactly that kind of behavior before.
无论如何2,你提到expr
编译成两条指令。为 xor
、循环增量和测试指令添加 3 条指令,这个循环已经有 5 条指令,这甚至超过了 high-end x86 CPU 的报废率,因此没有任何好处在这里寻找额外的指令级并行性(除非你可能正在编译到宽度更高的非 x86 arch?)。
1 ...而且它一般都会在 -O3
无论如何。
2 我们只需要猜测就可以了,因为你真的在严密保护expr
的秘密。