首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >由Visual Studio 2013 Update 2和Update 3生成的SSE 4说明

由Visual Studio 2013 Update 2和Update 3生成的SSE 4说明
EN

Stack Overflow用户
提问于 2014-08-26 05:31:02
回答 1查看 4.1K关注 0票数 18

如果我在VS 2013 Update 2或Update 3中编译此代码:(下面来自Update 3)

代码语言:javascript
复制
#include "stdafx.h"
#include <iostream>
#include <random>

struct Buffer
{
  long* data;
  int   count;
};

#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif

long Code(long* data, int count)
{
  long nMaxY = data[0];

  for (int nNode = 0; nNode < count; nNode++)
  {
    nMaxY = max(data[nNode], nMaxY);
  }

  return(nMaxY);
}

int _tmain(int argc, _TCHAR* argv[])
{
#ifdef __AVX__
  static_assert(false, "AVX should be disabled");
#endif
#ifdef __AVX2__
  static_assert(false, "AVX2 should be disabled");
#endif
  static_assert(_M_IX86_FP == 2, "SSE2 instructions should be enabled");
  Buffer buff;
  std::mt19937 engine;
  engine.seed(std::random_device{}());
  std::uniform_int_distribution<int> distribution(0, 100);

  buff.count = 1;
  buff.data = new long[1];
  buff.data[0] = distribution(engine);

  long result = Code(buff.data, buff.count);
  std::cout << result; // ensure result is used
  return result;
}

在启用SSE2指令但不启用AVX/AVX2的情况下,发行版中的编译器会生成:

代码语言:javascript
复制
  {
    nMaxY = max(data[nNode], nMaxY);
010612E1  movdqu      xmm0,xmmword ptr [eax]  
010612E5  add         esi,8  
010612E8  lea         eax,[eax+20h]  
010612EB  pmaxsd      xmm1,xmm0  
010612F0  movdqu      xmm0,xmmword ptr [eax-10h]  
010612F5  pmaxsd      xmm2,xmm0  
010612FA  cmp         esi,ebx  
010612FC  jl          Code+41h (010612E1h)  
010612FE  pmaxsd      xmm1,xmm2  
01061303  movdqa      xmm0,xmm1  
01061307  psrldq      xmm0,8  
0106130C  pmaxsd      xmm1,xmm0  
01061311  movdqa      xmm0,xmm1  
01061315  psrldq      xmm0,4  
0106131A  pmaxsd      xmm1,xmm0  
0106131F  movd        eax,xmm1  
01061323  pop         ebx  
  long nMaxY = data[0];

其中包含pmaxsd指令等。

据我所知,pmaxsd指令是SSE4_1 instructions或AVX指令,不是SSE2指令。

英特尔core2s支持sse3,但不支持sse4,也不支持pmaxsd

这在VS2013更新1或更新0中不会发生。

有没有办法让Visual Studio生成SSE2指令而不是像pmaxsd这样的SSE4指令?这是Visual Studio更新2/3中的已知错误吗?有解决办法吗?Visual Studio不再支持Core2处理器了吗?

下面是上面代码的一个更复杂的版本,它将(在默认发布设置下)编译为导致Core2 CPU崩溃的代码:

代码语言:javascript
复制
#include "stdafx.h"
#include <iostream>
#include <random>
#include <array>

enum unused_name {
  _nNumPolygons = 10,
};


#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif

struct Buffer
{
  std::array<long*, _nNumPolygons> data;
  std::array<int, _nNumPolygons>   count;
};

long Code(Buffer* buff)
{
  long  nMaxY = buff->data[0][0];


  for (int nPoly = 0; nPoly < _nNumPolygons; nPoly++)
  {
    for (int nNode = 0; nNode < buff->count[nPoly]; nNode++)
    {
      nMaxY = max(buff->data[nPoly][nNode], nMaxY);
    }
  }

  return(nMaxY);
}

extern "C" __int32 __isa_available;

int _tmain(int argc, _TCHAR* argv[])
{
#ifdef __AVX__
  static_assert(false, "AVX should be disabled");
#endif
#ifdef __AVX2__
  static_assert(false, "AVX2 should be disabled");
#endif
#if !( defined( _M_AMD64 ) || defined( _M_X64 ) )
  static_assert(_M_IX86_FP == 2, "SSE2 instructions should be enabled");
#endif
  // __isa_available = 1; // to force code to act as if SSE4_2 is not available
  Buffer buff;
  std::mt19937 engine;
  engine.seed(std::random_device{}());
  std::uniform_int_distribution<int> distribution(0, 100);

  for (int i = 0; i < _nNumPolygons; ++i) {
    buff.count[i] = 10;
    buff.data[i] = new long[10];
    for (int k = 0; k < 10; ++k)
    {
      buff.data[i][k] = distribution(engine);
    }
  }

  long result = Code(&buff);
  std::cout << result; // ensure result is used
  return result;
}

Here is a link to a bug for this issue说,大约在我发布这个问题的同时,另一个人打开了。

下面是生成的.asm:

代码语言:javascript
复制
?Code2@@YAJPAUBuffer@@@Z PROC        ; Code2, COMDAT
; _buff$ = ecx
; File c:\users\adam.nevraumont.corelcorp.000\documents\visual studio 2013\projects\consoleapplication1\consoleapplication1\consoleapplication1.cpp
; Line 22
  push  ebp
  mov  ebp, esp
  sub  esp, 12          ; 0000000cH
  push  ebx
  push  esi
  push  edi
  mov  edi, ecx
; Line 26
  xor  ebx, ebx
  mov  DWORD PTR _buff$1$[ebp], edi
  mov  DWORD PTR _nPoly$1$[ebp], ebx
  mov  eax, DWORD PTR [edi]
  mov  edx, DWORD PTR [eax]
; Line 28
  movd  xmm0, edx
  pshufd  xmm1, xmm0, 0
  movdqa  xmm2, xmm1
  npad  12
$LL6@Code2:
  lea  ecx, DWORD PTR [ebx*4]
  xor  eax, eax
  mov  esi, DWORD PTR [ecx+edi+40]
  mov  DWORD PTR tv443[ebp], ecx
  test  esi, esi
  jle  SHORT $LN5@Code2
  cmp  esi, 8
  jb  SHORT $LN25@Code2
  cmp  DWORD PTR ___isa_available, 2
  jl  SHORT $LN25@Code2
; Line 26
  mov  ebx, DWORD PTR [ecx+edi]
  mov  ecx, esi
  and  ecx, -2147483641      ; 80000007H
  jns  SHORT $LN33@Code2
  dec  ecx
  or  ecx, -8          ; fffffff8H
  inc  ecx
$LN33@Code2:
  mov  edi, esi
  sub  edi, ecx
  npad  8
$LL3@Code2:
; Line 30
  movdqu  xmm0, XMMWORD PTR [ebx+eax*4]
  pmaxsd  xmm1, xmm0
  movdqu  xmm0, XMMWORD PTR [ebx+eax*4+16]
  add  eax, 8
  pmaxsd  xmm2, xmm0
  cmp  eax, edi
  jl  SHORT $LL3@Code2
  mov  ebx, DWORD PTR _nPoly$1$[ebp]
  mov  ecx, DWORD PTR tv443[ebp]
  mov  edi, DWORD PTR _buff$1$[ebp]
$LN25@Code2:
; Line 28
  cmp  eax, esi
  jge  SHORT $LN5@Code2
; Line 26
  mov  edi, DWORD PTR [ecx+edi]
  npad  4
$LL23@Code2:
; Line 30
  cmp  DWORD PTR [edi+eax*4], edx
  cmovg  edx, DWORD PTR [edi+eax*4]
  inc  eax
  cmp  eax, esi
  jl  SHORT $LL23@Code2
$LN5@Code2:
; Line 26
  mov  edi, DWORD PTR _buff$1$[ebp]
  inc  ebx
  mov  DWORD PTR _nPoly$1$[ebp], ebx
  cmp  ebx, 10          ; 0000000aH
  jl  $LL6@Code2
; Line 28
  movd  xmm0, edx
  pshufd  xmm0, xmm0, 0
  pmaxsd  xmm1, xmm0
  pmaxsd  xmm1, xmm2
  movdqa  xmm0, xmm1
  psrldq  xmm0, 8
  pmaxsd  xmm1, xmm0
  movdqa  xmm0, xmm1
  pop  edi
  psrldq  xmm0, 4
  pmaxsd  xmm1, xmm0
  pop  esi
  movd  eax, xmm1
  pop  ebx
; Line 35
  mov  esp, ebp
  pop  ebp
  ret  0

这里:

代码语言:javascript
复制
  cmp  esi, 8
  jb  SHORT $LN25@Code2
  cmp  DWORD PTR ___isa_available, 2
  jl  SHORT $LN25@Code2

如果(A)循环长度小于8,或者(B)我们不支持SSE3/SSE4,我们有一个分支到“单步”版本的测试。

单步版本为:

代码语言:javascript
复制
$LN5@Code2:
; Line 26
  mov  edi, DWORD PTR _buff$1$[ebp]
  inc  ebx
  mov  DWORD PTR _nPoly$1$[ebp], ebx
  cmp  ebx, 10          ; 0000000aH
  jl  $LL6@Code2

它没有SSE指令。然而,重要的部分是失败。如果eax (迭代参数)传递了10,那么它将落入:

代码语言:javascript
复制
; Line 28
  movd  xmm0, edx
  pshufd  xmm0, xmm0, 0
  pmaxsd  xmm1, xmm0

这是查找单步版本结果和SSE4结果的最大值的代码。第三条指令是pmaxsd,这是一条SSE4_1指令,它不受__isa_available的保护。

是否有编译器设置或解决方法可以使自动矢量化保持不变,同时不在启用Core2 SSE2的计算机上调用SSE4_1指令?是不是我的代码中有bug导致了这种情况的发生?

请注意,我试图删除循环的双重嵌套性质,这似乎使问题消失了。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-08-26 06:14:22

这是documented behaviour

自动向量化器还使用较新的SSE4.2指令集,如果您的计算机支持它的话。

如果仔细观察编译器生成的代码,您会发现SSE4.2指令的使用依赖于运行时测试:

代码语言:javascript
复制
cmp DWORD PTR ___isa_available, 2
jl  SHORT $LN11@Code

此处的值2为apparently means SSE4.2

然而,我能够确认您的第二个示例中的bug。原来我使用的Core2PC支持SSE4.1和PMAXSD指令,所以我不得不在装有奔腾4CPU的PC上进行测试,以获得非法指令异常。您应该向Microsoft Connect提交错误报告。一定要提到您的示例代码失败的特定Core 2 CPU型号。

至于解决方法,我只能建议更改受影响函数的优化级别。从优化速度切换到优化大小似乎生成的代码与仅用于SSE2指令的代码几乎相同。您可以使用#pragma optimize切换优化级别,如下所示:

代码语言:javascript
复制
#pragma optimize("s", on)

long Code(Buffer* buff)
{
     ...
}

#pragma optimize("", on)

作为documented on this bug report/d2Qvec-sse2only是一个未记录的标志,它在更新3(可能还有更新2)时工作,以防止编译器输出SSE4指令。这可以自然地防止一些循环被矢量化。/d2Qvec-sse2only可能会在任何时候停止工作(它可能会在未来的VC版本上发生变化,恕不另行通知)。

微软声称,这个问题已经在更新4和更新4 CTP 2(不用于生产)中得到解决。

票数 17
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25494681

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档