c++ - SSE:reinterpret_cast<__m128*> 而不是 _mm_load_ps

标签 c++ x86 sse simd

我正在用 C++ 编写一个简单的卷积函数,从非常基本的“滑动窗口”卷积到常规产品(目前没有 FFT 的东西),直到 SEE、AVX 和可能的 OpenCL。不过我遇到了 SSE 的问题。我的代码如下所示:

for (x = 0; x < SIZEX - KSIZEX + 1; ++x)
{
    for (y = 0; y < SIZEY - KSIZEY + 1; ++y)
    {           
        tmp = 0.0f;

        float fDPtmp = 0.0f;
        float *Kp = &K[0];


        for (xi = 0; xi < KSIZEX; ++xi, Kp=Kp+4)
        {                               
            float *Cp = &C[(x+xi)*SIZEY + y];

            __m128 *KpSSE = reinterpret_cast<__m128*>(&K);
            __m128 *CpSSE = reinterpret_cast<__m128*>(&C[(x + xi)*SIZEY + y]);
            __m128 DPtmp = _mm_dp_ps(*KpSSE, *CpSSE, 0xFF);
            _mm_store_ss(&fDPtmp, DPtmp);

            tmp += fDPtmp;
        }

        R[k] = tmp;
        ++k;
    }
}

必要的矩阵是这样初始化的(这些矩阵的大小被认为是可以的,因为更简单的实现工作得很好):

__declspec(align(16)) float *C = ReadMatrix("E:\\Code\\conv\\C.bin");
__declspec(align(16)) float *K = ReadMatrix("E:\\Code\\conv\\K.bin");
__declspec(align(16)) float *R = new float[CSIZEX*CSIZEY];

代码在 y=1 处崩溃,所以我觉得我处理指针的方式可能有误。有趣的是,如果我用 _mm_set_ps 替换 reinterpret_casts,即

__m128 KpSSE = _mm_set_ps(Kp[0], Kp[1], Kp[2], Kp[3]);
__m128 CpSSE = _mm_set_ps(Cp[0], Cp[1], Cp[2], Cp[3]);
__m128 DPtmp = _mm_dp_ps(KpSSE, CpSSE, 0xFF);
_mm_store_ss(&fDPtmp, DPtmp);

虽然速度较慢,但​​整个过程运行良好,我将其归咎于所有复制操作。

谁能指出我到底做错了什么?

非常感谢

帕特

更新:好的,正如 Paul 所指出的,问题出在 ReadMatrix 上(或者另一个解决方案是使用 _mm_loadu_ps)。至于 ReadMatrix(),它看起来像这样:

__declspec(align(16)) float* ReadMatrix(string path)
{
    streampos size;

    ifstream file(path, ios::in | ios::binary | ios::ate);

    if (file.is_open())
    {
        size = file.tellg();
        __declspec(align(16)) float *C = new float[size];
        file.seekg(0, ios::beg);
        file.read(reinterpret_cast<char*>(&C[0]), size);
        file.close();

        return C;
    }
    else cout << "Unable to open file" << endl;
}

它并没有起到作用。有没有其他方法可以优雅地执行此操作,而不是被迫逐个读取文件并执行我认为应该有效的 memcpy?!

更新:

之后好像还是不想上类

__declspec(align(16)) float* ReadMatrix(string path)
{
    streampos size;

    ifstream file(path, ios::in | ios::binary | ios::ate);

    if (file.is_open())
    {
        size = file.tellg();
        __declspec(align(16)) float *C = static_cast<__declspec(align(16)) float*>(_aligned_malloc(size * sizeof(*C), 16));
        file.seekg(0, ios::beg);
        file.read(reinterpret_cast<char*>(&C[0]), size);
        file.close();

        return C;
    }
    else cout << "Unable to open file" << endl;
}

我在那里添加了 static_cast,因为它似乎有必要让 Paul 的代码编译(即 _aligned_malloc 返回一个空指针)。我快要用 fread 读取文件 block 并将它们 memcpy 到一个对齐的数组中。 :/我又一次发现自己在寻求建议。非常感谢大家。

帕特

PS:非 SSE 代码可以很好地处理这些数据结构。 _mm_loadu_ps 比使用非 SSE 代码慢。

最佳答案

这并不像你想象的那样:

__declspec(align(16)) float *C = ReadMatrix("E:\\Code\\conv\\C.bin");

此处对齐指令的作用是将指针本身(即 C)对齐到 16 字节边界,而不是指针的内容

您要么需要修复 ReadMatrix 以便它返回适当对齐的数据,要么使用 _mm_loadu_ps,正如其他人已经建议的那样。

不要使用 _mm_set_ps,因为这会在后台生成大量指令,这与映射到单个指令的 _mm_loadu_ps 不同。

更新

你在 ReadMatrix 中重复了同样的错误:

__declspec(align(16)) float *C = new float[size];

同样,这不能保证数据的对齐,只能保证指针 C 本身的对齐。要修复此分配,您可以使用 _mm_malloc_aligned_malloc:

float *C = _mm_malloc(size * sizeof(*C), 16); 

float *C = _aligned_malloc(size * sizeof(*C), 16); 

关于c++ - SSE:reinterpret_cast<__m128*> 而不是 _mm_load_ps,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21328985/

相关文章:

c++ - 如果..否则警告 "not all control paths return a value"

assembly - nasm 英特尔 : Access items in the stack without using pop

c - 两个固定长度整数数组的元素之和

c++ - [] std::list 的运算符?

c++ - 如何为类 union 类编写析构函数

assembly - 汇编语言有多少种

assembly - 在 fasm (MS-DOS) 中访问 0xF000FFFE(计算机类型)处的内存

assembly - 将两个x86 32位寄存器存储到128位xmm寄存器中

c - 使用 sse 内在函数时如何从循环中跳出?

c++ - 空 std::list 在调用析构函数时抛出异常