【问题标题】:MSVC 2013: crash at addpd xmm6,xmmword ptr [rax+rbx*8]MSVC 2013:在 addpd xmm6,xmmword ptr [rax+rbx*8] 处崩溃
【发布时间】:2014-02-25 23:17:07
【问题描述】:

自从我使用 MSVC 2013 以来,我的应用程序在执行以下代码时崩溃(稀疏矩阵乘以向量,伪代码:A = this * pVector):
complex<double> x = (A.getValue(lRow) + (mValues[lIdx] * pVectorB->getValue(lCol)));
在我使用 MSVC 2005 之前,应用程序运行良好。
抛出异常 (First-chance exception at 0x000000014075D1D2 in psc64.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.)。
我跟踪程序集以:
addpd xmm6, xmmword ptr [rax+rbx*8]
它仅在优化 /O2(最大化速度)时崩溃,但在没有优化 /Od 时不会崩溃。
我还可以在将代码 (coutpVectorB->getValue(lCol) 时避免崩溃。
我相信未初始化变量可能是一些问题。但我找不到任何东西。因此,我研究了拆卸。
我检查 XMM6 和 ptr [rax+rbx*8]。没有崩溃(使用 cout除了 XMM6 和 ptr [rax+rbx*8] 的值之外,我还有什么需要寻找的吗?
我很长一段时间以来一直在寻找问题,但找不到任何提示来追踪问题到我必须更正的代码行。
非常感谢任何帮助。谢谢。

getValue 的代码:

template <class T> class Vector
{    const T& getValue(const int pIdx) const
    {
      if(false == checkBounds(pIdx)){
        throw MathException(__FILE__, __LINE__, "T& Vector<class T>::getValue(const pIdx): checkBounds fails pIdx = %i", pIdx);
      }
      return mVal[pIdx];
    }

bool checkBounds(const int pIdx)const
  {
    bool ret = true;
    if(pIdx >= mMaxSize){
      DBG_SEVERE2("pIdx >= mMaxSize, pIdx = %i, mMaxSize = %i", pIdx, mMaxSize);
      ret = false;
    }
    if(pIdx < 0){
      DBG_SEVERE1("pIdx < 0, pIdx = %i", pIdx);
      ret = false;
    }
    return ret;
  }
}



mVal的配置:

void* lTmp= calloc((4 * sizeof(complex<double>))+4, 1);
((char*)lTmp)[0]        = 0xC;
((char*)lTmp)[1]        = 0xC;
((char*)lTmp)[(4 * sizeof(complex<double>)) + 2]    = 0xC;
((char*)lTmp)[(4 * sizeof(complex<double>)) + 3]    = 0xC;
mVal= (void*)(((char*)lTmp) + 2)



解决方案:
正如建议的那样,它可以在所需数组 (mVal) 前后没有 2 个字节的情况下工作。但它也适用于数组前后的 16 字节的倍数。

【问题讨论】:

  • 你用汇编写乘法吗?
  • 能贴出getValue方法的源码吗?
  • 乘法不是汇编。它是纯 C++,使用复杂的 函数。
  • 那么我认为显示更多的源代码会更有帮助,一个独立的、可编译的、sn-p 的代码来证明问题是理想的。
  • 请参阅stackoverflow.com/questions/13013717/… 这里很可能存在对齐问题。

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


【解决方案1】:

在将内存操作数用于 SSE 指令(如 addpd xmm6, xmmword ptr [rax+rbx*8])时,内存操作数必须对齐。有未对齐的 load 指令:你可以从[rax+rbx*8] movdqu 然后对寄存器进行操作。但是如果你使用记忆形式,对齐很重要。优化标志可能改变了数组的对齐方式。或者它可能将负载折叠到内存操作数中(在某些情况下更快)并以这种方式导致问题。

【讨论】:

  • 那些折叠/融合的加载指令在某些情况下也可能更慢。 stackoverflow.com/questions/21134279/…。使用 fused add/sub + load 或 mul + load 似乎是 MSVC 的偏好,但我不确定这是一个好主意。无论如何,未对齐的负载不再变慢(自桑迪桥以来)。
  • @Zboson 我知道 GCC 在这些方面并不聪明。例如,加法是可交换的,但只有 add 内在函数的一个操作数有资格成为内存操作数。关于它是否更快,似乎确实是偶然的。
  • 我不确定我知道你的意思是 GCC 对此并不聪明。 GCC 正在用这些内在函数做我想要的,仅此而已。与 MSVC 的方法相比,我更喜欢这种方法,即尝试变得聪明,最终在我的情况下效率较低。也许更好的解决方案是折叠/融合内在函数,以便程序员可以选择而不是编译器。否则,唯一的解决办法就是用汇编来写。
猜你喜欢
  • 2012-11-12
  • 1970-01-01
  • 2021-02-10
  • 2017-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
相关资源
最近更新 更多