在 C++ 中对整数数组进行线性搜索时,SSE 比较无法按预期工作

Posted

技术标签:

【中文标题】在 C++ 中对整数数组进行线性搜索时,SSE 比较无法按预期工作【英文标题】:SSE comparisons not working as intended while doing a linear search over an array of integers in c++ 【发布时间】:2016-06-25 22:40:15 【问题描述】:

我有以下代码旨在使用 c++ 中的流式 SIMD 扩展对数组执行线性搜索:

#include <iostream>
#include <emmintrin.h>

using namespace std;

bool sse2_search_array(int* arr, int size, int key) 
    int iterations;
    if (size % 16 == 0) 
        iterations = size / 16;
    
    else 
        iterations = size / 16 + 1;
    
    __m128i* arr_ = reinterpret_cast<__m128i*>(arr);  /*Cast to corresponding int type for 128 bit registers. Each __m128i
                                occupies 8 bits, so 16 integers can be processed simultaneously.*/
    __declspec(align(16)) int key_arr[16];
    fill_n(key_arr, 16, key);  /*fill key array with 16 keys (for SSE comparisons)*/
    __m128i* key_arr_ = reinterpret_cast<__m128i*>(key_arr);

    int result;
    /*Actual search begins here.*/
    for (int i = 0; i < iterations; i++, arr_++) 
        result = _mm_movemask_epi8(_mm_cmpeq_epi8( *key_arr_, *arr_));  /*Comparison of 2 16 bit arrays simultaneously.*/
        cout << "result: " << result << endl;
        if (result != 0)  return true; 
    
    return false;



int main() 
    __declspec(align(16)) int example_array[16] =  1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 ;

    cout << "found: " << sse2_search_array(example_array, 16, 128);
    cin.get();

它可以工作,但主函数中的示例应该返回 false,因为 128 不在 example_array 中,但 sse2_search_array 似乎总是返回 true,并且示例中 result 的值是 1110111011101110b 或 61166对我来说没有意义,因为我希望它为 0。所以有人可以告诉我问题是什么以及如何解决它吗?我对c++不是很熟悉,对SSE知之甚少。

【问题讨论】:

【参考方案1】:

两大问题:

永远不要仅仅为了将它作为向量加载而填充临时数组:

__declspec(align(16)) int key_arr[16];
fill_n(key_arr, 16, key);  /*fill key array with 16 keys (for SSE comparisons)*/
__m128i* key_arr_ = reinterpret_cast<__m128i*>(key_arr);

改为使用__m128i keyvec = _mm_set1_epi8(key);。与将 16 次标量存储到内存然后进行向量加载(这将遭受存储转发停顿)相比,将一个字节广播到向量的所有位置有 更多 的方法。让编译器为您选择使用 _mm_set 内在函数而不是写入本地数组。


int 是 4 字节(在所有现代 x86 编译器上),但您显然希望使用单字节元素数组,因为您使用的是 _mm_cmpeq_epi8。您的 example_array 实际上是 16 * 4 字节长:

__declspec(align(16)) int example_array[16] =  1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 ;
// equivalent byte array (on little-endian x86):
uint8_t byte_array[16*sizeof(int)] =  1,0,0,0,  2,0,0,0,  3,0,0,0, ... ;

您的 cmets 通常是完全错误的,例如Comparison of 2 16 bit arrays simultaneously。也许你的意思是“字节”?


如果您真的想搜索int 的数组,请使用_mm_set1_epi32(key)_mm_cmpeq_epi32。一个 16 字节的向量包含四个 ints。 movemask的结果还是以字节为单位的,但是结果中每组4位都是一样的。

另请参阅sse 标签维基和x86 标签维基以获得有用的链接。 c++ 标签 wiki 有很多关于语言的好东西,因为你说你也是新手。


IDK 为什么您的 key=128 会被点击;除非您的代码有更多我没有注意到的错误,否则这似乎没有意义。

您的调试器应该能够向您显示 __m128i 变量中的内容。将一些临时变量存储在变量中将使使用 C++ 源代码级调试器更容易查看它们,而不是单步执行 asm 代码。

【讨论】:

非常感谢您的详细回复,它非常有用,让我的代码正常工作。

以上是关于在 C++ 中对整数数组进行线性搜索时,SSE 比较无法按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

C++ SSE:存储到数组后的未定义行为

在现代 CPU 上,二分查找比线性查找快多少?

SSE:质量整数转换+SSE 比 FPU 慢?

SSE 从 __m128 中提取整数用于索引数组

如何在 C++ 中线性搜索两个数组?

用 SSE 在 C++ 中将两个 32 位整数向量相乘的最快方法