【问题标题】:How can I use SSE (and SSE2, SSE3, etc.) extensions when building with Visual C++?使用 Visual C++ 构建时,如何使用 SSE(以及 SSE2、SSE3 等)扩展?
【发布时间】:2011-10-27 12:43:03
【问题描述】:

我现在正在使用 Visual Studio 中的 SSE 指令对基本点积函数进行小型优化。

这是我的代码:(函数调用约定是 cdecl):

float SSEDP4(const vect & vec1, const vect & vec2)
{
    __asm
    {
        // get addresses
        mov ecx, dword ptr[vec1]
        mov edx, dword ptr[vec2]
        // get the first vector
        movups xmm1, xmmword ptr[ecx]
        // get the second vector (must use movups, because data is not assured to be aligned to 16 bytes => TODO align data)
        movups xmm1, xmmword ptr[edx]
        // OP by OP multiply with second vector (by address)
        mulps xmm1, xmm2
        // add everything with horizontal add func (SSE3)
        haddps xmm1, xmm1
        // is one addition enough ?
        // try to extract, we'll see
        pextrd eax, xmm1, 03h
    }
}

vect 是一个简单的结构,包含 4 个单精度浮点数,不对齐到 16 个字节(这就是我使用 movups 而不是 movaps 的原因)

vec1 初始化为 (1.0, 1.2, 1.4, 1.0)vec2 初始化为 (2.0, 1.8, 1.6, 1.0)

一切都编译得很好,但是在执行时,我在两个 XMM 寄存器中都得到了 0,因此结果 在调试时,Visual Studio 向我显示了 2 个寄存器(MMX1 和 MMX2,有时是 MMX2 和 MMX3),它们是 64 位寄存器,但没有 XMM,一切都为 0。

有人知道发生了什么吗?

提前谢谢你:)

【问题讨论】:

  • 请注意,Visual Studio 不是编译器; Visual C++ 是实际的编译器; Visual Studio 是 IDE。 (因此我编辑了这个问题)
  • 调试 + Windows + 寄存器。右键单击窗口并勾选 SSE2

标签: c++ visual-c++ assembly sse inline-assembly


【解决方案1】:

有几种方法可以在 MSVC++ 上获取 SSE 指令:

  1. 编译器内部函数 -> http://msdn.microsoft.com/en-us/library/t467de55.aspx
  2. 外部 MASM 文件。

内联汇编(如您的示例代码)不再是一个合理的选择,因为在为非 32 位 x86 系统构建时它不会编译。 (例如,构建 64 位二进制文​​件会失败)

此外,程序集块会抑制大多数优化。这对您不利,因为即使是像内联这样简单的事情也不会发生在您的函数中。内在函数的工作方式不会破坏优化器。

【讨论】:

  • 我会回应上面比利的 cmets 并强烈建议您尽可能使用内在函数。请注意,Win8 将在 ARM 芯片上可用,因此如果您希望代码能够通过简单的重新编译在 ARM 上运行,请尽可能避免汇编。
  • 您好,感谢您的快速回答!我还有一个小问题:是否可以直接在函数中使用 MASM 文件?还是他们定义了一个从代码中调用的函数?
  • @Gregoire 您在汇编文件中定义函数并从代码中调用它。当然你需要在调用它之前声明这个函数。
  • @Richard 内在函数绝对不能保证在不同的 CPU 上工作。
  • @Jasper:嗯,作为一个整体的 SSE 不能跨不同的 CPU 工作。这不是对内在函数的破坏,而是对 SSE 时期的破坏。您至少可以使用内部函数为 x64 进行构建。当然,对 ARM 没有多大帮助,除非 MSVC++ 使用相同的串行版本来模拟它们。 (不知道;无法真正测试,因为没有公共版本的 MSVC++ 可以为 ARM 构建)
【解决方案2】:

您已正确编译和运行,因此您至少能够使用 SSE。

为了在寄存器窗口中查看 SSE 寄存器,右键单击寄存器窗口并选择 SSE。这应该让您看到 XMM 寄存器。

您还可以在监视窗口中使用@xmm<register><component>(例如,@xmm00 查看xmm0[0])来查看 XMM 寄存器的各个组件。

现在,至于您的实际问题,您正在用[edx] 覆盖xmm1,而不是将其填充到xmm2 中。

此外,标量浮点值在st(0) 中的x87 堆栈上返回。我没有试图记住如何做到这一点,而是将结果存储在堆栈变量中,并让编译器为我完成:

float SSEDP4(const vect & vec1, const vect & vec2)
{
    float result;
    __asm
    {
        // get addresses
        mov ecx, dword ptr[vec1]
        mov edx, dword ptr[vec2]
        // get the first vector
        movups xmm1, xmmword ptr[ecx]
        // get the second vector (must use movups, because data is not assured to be aligned to 16 bytes => TODO align data)
        movups xmm2, xmmword ptr[edx] // xmm2, not xmm1
        // OP by OP multiply with second vector (by address)
        mulps xmm1, xmm2
        // add everything with horizontal add func (SSE3)
        haddps xmm1, xmm1
        // is one addition enough ?
        // try to extract, we'll see
        pextrd [result], xmm1, 03h
    }

    return result;
}

【讨论】:

  • 现在效果更好了!我还设法通过将数据对齐到 16 个字节(使用 __declspec(align 16) )保存一条指令,然后将第二个 movups 和 mulps 融合在一起(mulps 只能在对齐模式下使用地址)。然而,通过进行一些测试(在调试模式下,没有编译器优化),我发现使用基本指令比使用 SSE 指令集运行 100 000 000 次点积更快。有谁知道那里发生了什么?
猜你喜欢
  • 2010-10-14
  • 2012-07-19
  • 2010-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-11
  • 2021-06-16
  • 2013-02-28
相关资源
最近更新 更多