我正在尝试计算大量uint64的位奇偶校验。比特奇偶校验是指接受uint64的函数,如果设置的比特数是偶数则输出0,否则为1。
目前我正在使用以下功能(@Troyseph,发现here):
uint parity64(uint64 n){
n ^= n >> 1;
n ^= n >> 2;
n = (n & 0x1111111111111111) * 0x1111111111111111;
return (n >> 60) & 1;
}
相同的SO页面具有以下汇编例程(由@papadp提供):
.code
; bool CheckParity(size_t Result)
CheckParity PROC
mov rax, 0
add rcx, 0
jnp jmp_over
mov rax, 1
jmp_over:
ret
CheckParity ENDP
END
利用机器的parity flag。但我无法使用我的C程序(我知道没有组装)。
问题即可。如何在C源文件中包含上面(或类似)代码作为内联汇编,以便parity64()
函数运行该代码?
(我在Intel Xeon Haswell上使用GCC和64位Ubuntu 14)
如果有任何帮助,parity64()
函数将在以下例程中调用:
uint bindot(uint64* a, uint64* b, uint64 entries){
uint parity = 0;
for(uint i=0; i<entries; ++i)
parity ^= parity64(a[i] & b[i]); // Running sum!
return parity;
}
(这应该是Z / 2Z场上两个向量的“点积”,即GF(2)。)
答案 0 :(得分:8)
因为在处理位操作时C很糟糕,我建议使用gcc内置函数,在本例中为__builtin_parityl()。参见:
答案 1 :(得分:2)
如何在C源文件中包含上面(或类似)代码作为内联汇编,以便
parity64()
函数运行该代码?
这是XY problem ...您认为您需要内联该程序集才能从中获益,因此您询问如何内联 ...但您不需要内嵌 。
你不应该将汇编包含在你的C源代码中,因为在这种情况下你不需要,而且更好的选择(在可移植性和可维护性)是将两个源代码分开,单独编译它们并使用链接器链接它们。
在parity64.c
中,您应该拥有可移植版本(带有名为bool CheckParity(size_t result)
的包装器),您可以在非x86 / 64情况下默认使用该版本。
您可以将其编译为目标文件,如下所示:gcc -c parity64.c -o parity64.o
...然后将程序集生成的目标代码与C代码相关联:gcc bindot.c parity64.o -o bindot
在parity64_x86.s
中,您的问题可能包含以下汇编代码:
.code
; bool CheckParity(size_t Result)
CheckParity PROC
mov rax, 0
add rcx, 0
jnp jmp_over
mov rax, 1
jmp_over:
ret
CheckParity ENDP
END
您可以使用此命令使用parity64.o
将此代码编译为另一个gcc
目标文件对象代码:gcc -c parity64_x86.s -o parity64.o
...然后链接生成的对象代码:gcc bindot.c parity64.o -o bindot
同样,如果您想使用__builtin_parityl
(正如hdantes answer所建议的那样),您可以(而且应该)再次将该代码分开(在同一个地方保留其他 gcc来自便携式代码的/ x86优化)。parity64_x86.c
中您可能有:
bool CheckParity(size_t result) {
return __builtin_parityl(result);
}
要编译它,您的命令将是:gcc -c parity64_x86.c -o parity64.o
...然后链接生成的对象代码:gcc bindot.c parity64.o -o bindot
另一方面,如果你想检查一下gcc
会产生的汇编:gcc -S parity64_x86.c
你的程序集中的注释表明C中的等效函数原型是bool CheckParity(size_t Result)
,所以考虑到这一点,这里的bindot.c
可能是这样的:
extern bool CheckParity(size_t Result);
uint64_t bindot(uint64_t *a, uint64_t *b, size_t entries){
uint64_t parity = 0;
for(size_t i = 0; i < entries; ++i)
parity ^= a[i] & b[i]; // Running sum!
return CheckParity(parity);
}
您可以将其构建并将其链接到上述parity64.o
版本的任何,如下所示:gcc bindot.c parity64.o -o bindot
...
我强烈建议您在有空的时候阅读the manual for your compiler ...
答案 2 :(得分:1)
您必须使用扩展内联汇编(这是一个gcc扩展名)才能获得类似的效果。
您的parity64功能可以更改如下 -
uint parity64(uint64 n){
uint result = 0;
__asm__("addq $0, %0" : : "r"(n) :);
__asm__("jnp 1f");
__asm__("movl $1, %0" : "=r"(result) : : );
__asm__("1:");
return result;
}
但正如@MichaelPetch评论的那样,奇偶校验标志仅在低8位上计算。因此,如果您的n小于255,这将适用于您。对于更大的数字,您将必须使用您在问题中提到的代码。
要使其工作在64位,您可以通过执行
将32位整数的奇偶校验折叠为单字节n = (n >> 32) ^ n;
n = (n >> 16) ^ n;
n = (n >> 8) ^ n;
此代码必须只是在汇编之前的函数的开头。
您必须检查它对性能的影响。
我能得到的最优化的是
uint parity64(uint64 n){
unsigned char result = 0;
n = (n >> 32) ^ n;
n = (n >> 16) ^ n;
n = (n >> 8) ^ n;
__asm__("test %1, %1 \n\t"
"setp %0"
: "+r"(result)
: "r"(n)
:
);
return result;
}