在 SSE 中使用位集的实现和性能

Posted

技术标签:

【中文标题】在 SSE 中使用位集的实现和性能【英文标题】:Implementation and performance of using bitsets with SSE 【发布时间】:2012-05-29 15:28:07 【问题描述】:

我正在尝试使用 SSE(在 Visual Studio 上)加快我的方法。我是该地区的新手。我在我的方法中使用的主要数据类型是大小为 32 的位集,我主要使用的逻辑运算是 AND 运算(很少使用 _BitScanForward)。我想知道是否可以使用 SSE 指令来加快我的程序。

这就是我现在正在做的事情(我已经完全完成了,无法直接比较结果):

我使用 _mm_set_ps 加载操作数(位集)。我在位集上使用 to_ulong() 将它们转换为无符号长整数:

__m128 v1 = _mm_set_ps(b1.to_ulong(),b2.to_ulong(),b3.to_ulong(),b4.to_ulong());
__m128 v2 = _mm_set1_ps(b.to_ulong())

接下来是实际的AND运算:

__m128 v3 = _mm_and_ps(v1,v2);

此时,我有两个问题:

    我这样做的方式(使用 to_ulong() 将位集转换为无符号长整数)是一种好方法吗?我怀疑有很大的开销可能会扼杀我使用 SSE 可能获得的潜在性能改进。

    将 v3 以 4 个位集的形式存储回内存的最佳方法是什么?我打算使用 _mm_storeu_ps 内在函数。

【问题讨论】:

【参考方案1】:

有几点:

如果您的位集基本上是 32 位整数,那么您应该使用合适的整数 SIMD 类型,即__m128i,而不是浮点 (__m128)

_mm_set_XXX 宏相对昂贵 - 与常规 SSE 内在函数不同,它们可以生成相当多的指令 - 如果您所做的只是一个 AND 操作,那么 _mm_and_XXX 操作带来的任何性能优势都将被_mm_set_XXX 操作的成本

理想情况下,如果您只想对数组中的一组位集进行 AND 运算,那么代码应如下所示:

const int N = 1024;

int32_t b1[N]; // 2 x arrays of input bit sets
int32_t b2[N];
int32_t b3[N]; // 1 x array of output bit sets

for (int i = 0; i < N; i += 4)

    __m128i v1 = _mm_loadu_si128(&b1[i]); // load input bits sets
    __m128i v2 = _mm_loadu_si128(&b2[i]);
    __m128i v3 = _mm_and_si128(v1, v2);   // do the bitwise AND
    _mm_storeu_si128(&b3[i], v3);         // store the result

如果您只想用固定掩码对数组进行原位与操作,那么它会简化为:

const int N = 1024;

int32_t b1[N]; // input/output array of bit sets

const __m128i v2 = _mm_set1_epi32(0x12345678); // mask

for (int i = 0; i < N; i += 4)

    __m128i v1 = _mm_loadu_si128(&b1[i]); // load input bits sets
    __m128i v3 = _mm_and_si128(v1, v2);   // do the bitwise AND
    _mm_storeu_si128(&b1[i], v3);         // store the result

注意:为了获得更好的性能,请确保您的输入/输出数组是 16 字节对齐的,然后使用 _mm_load_si128/_mm_store_si128 而不是上面未对齐的对应物。

【讨论】:

Paul,_mm_set1_epi32 不适用于 bitset 实例。是否有适用于实际 bitset 实例的替代方法? 我不是 C++ 专家,但我希望您可以很容易地将 bitset 转换为 32 位 int,无论是使用现有方法还是编写辅助函数。 您可能不是 C++ 专家,但您对我的 SSE 问题帮助很大!我只是不想使用转换/转换来避免开销。祝先生玩得开心! 如果你只用_mm_set1_epi32设置一个掩码值在主循环之外那么效率不是太重要 - 如果你需要这样做在循环内部,那么您可能需要更仔细地查看如何实现 bitset 和 int 之间的转换。我主要使用 C 来编写高性能 SIMD 代码以避免此类问题。 使用 C 与 C++ 相比有何优势?

以上是关于在 SSE 中使用位集的实现和性能的主要内容,如果未能解决你的问题,请参考以下文章

相当于 Visual Studio 中位集的 .Find_first 方法

SSE、SSE2、SSE3指令集的区别?

Cuda:XOR 单个位集与位集数组

指令集的相关问题!

在 Visual Studio 中检测 SSE/SSE2 指令集的可用性

在初始化时定义位集大小?