在一些上下文中,例如生物信息学,对字节大小的整数进行计算就足够了。为了获得最佳性能,许多处理器架构提供SIMD指令集(例如MMX,SSE,AVX),将寄存器分成字节、半字和字大小的组件,然后单独执行相应组件的算术、逻辑和移位操作。
然而,一些架构不提供这样的SIMD指令,需要进行模拟,这通常需要大量的位运算。目前,我正在研究SIMD比较,特别是有符号的字节大小整数的并行比较。我有一个解决方案,认为使用可移植的C代码非常高效(请参见下面的函数vsetles4)。它基于Peter Montgomery在2000年的网络帖子中所做的观察,即(A+B)/2 = (A AND B) + (A XOR B)/2,在中间计算中没有溢出。
这个特定的仿真代码(函数vsetles4)能否进一步加速?一般来说,任何基本操作次数更少的解决方案都将符合要求。我正在寻找ISO-C99的便携式解决方案,不使用机器特定的内部函数。大多数架构都支持ANDN(a & ~b),因此可以假定它作为效率的单个操作可用。
然而,一些架构不提供这样的SIMD指令,需要进行模拟,这通常需要大量的位运算。目前,我正在研究SIMD比较,特别是有符号的字节大小整数的并行比较。我有一个解决方案,认为使用可移植的C代码非常高效(请参见下面的函数vsetles4)。它基于Peter Montgomery在2000年的网络帖子中所做的观察,即(A+B)/2 = (A AND B) + (A XOR B)/2,在中间计算中没有溢出。
这个特定的仿真代码(函数vsetles4)能否进一步加速?一般来说,任何基本操作次数更少的解决方案都将符合要求。我正在寻找ISO-C99的便携式解决方案,不使用机器特定的内部函数。大多数架构都支持ANDN(a & ~b),因此可以假定它作为效率的单个操作可用。
#include <stdint.h>
/*
vsetles4 treats its inputs as arrays of bytes each of which comprises
a signed integers in [-128,127]. Compute in byte-wise fashion, between
corresponding bytes of 'a' and 'b', the boolean predicate "less than
or equal" as a value in [0,1] into the corresponding byte of the result.
*/
/* reference implementation */
uint32_t vsetles4_ref (uint32_t a, uint32_t b)
{
uint8_t a0 = (uint8_t)((a >> 0) & 0xff);
uint8_t a1 = (uint8_t)((a >> 8) & 0xff);
uint8_t a2 = (uint8_t)((a >> 16) & 0xff);
uint8_t a3 = (uint8_t)((a >> 24) & 0xff);
uint8_t b0 = (uint8_t)((b >> 0) & 0xff);
uint8_t b1 = (uint8_t)((b >> 8) & 0xff);
uint8_t b2 = (uint8_t)((b >> 16) & 0xff);
uint8_t b3 = (uint8_t)((b >> 24) & 0xff);
int p0 = (int32_t)(int8_t)a0 <= (int32_t)(int8_t)b0;
int p1 = (int32_t)(int8_t)a1 <= (int32_t)(int8_t)b1;
int p2 = (int32_t)(int8_t)a2 <= (int32_t)(int8_t)b2;
int p3 = (int32_t)(int8_t)a3 <= (int32_t)(int8_t)b3;
return (((uint32_t)p3 << 24) | ((uint32_t)p2 << 16) |
((uint32_t)p1 << 8) | ((uint32_t)p0 << 0));
}
/* Optimized implementation:
a <= b; a - b <= 0; a + ~b + 1 <= 0; a + ~b < 0; (a + ~b)/2 < 0.
Compute avg(a,~b) without overflow, rounding towards -INF; then
lteq(a,b) = sign bit of result. In other words: compute 'lteq' as
(a & ~b) + arithmetic_right_shift (a ^ ~b, 1) giving the desired
predicate in the MSB of each byte.
*/
uint32_t vsetles4 (uint32_t a, uint32_t b)
{
uint32_t m, s, t, nb;
nb = ~b; // ~b
s = a & nb; // a & ~b
t = a ^ nb; // a ^ ~b
m = t & 0xfefefefe; // don't cross byte boundaries during shift
m = m >> 1; // logical portion of arithmetic right shift
s = s + m; // start (a & ~b) + arithmetic_right_shift (a ^ ~b, 1)
s = s ^ t; // complete arithmetic right shift and addition
s = s & 0x80808080; // MSB of each byte now contains predicate
t = s >> 7; // result is byte-wise predicate in [0,1]
return t;
}
int8_t
数组并应用<=
,这种方法是否更快?(不是相对于vsetles4_ref
计时 - 而是相对于根本不尝试将这些东西打包成uint32_t
。) - user2357112