带有unordered_map的initializer_list和MMX指令的bad_alloc,可能发生堆损坏吗?

时间:2019-02-08 21:46:02

标签: c++11 gcc initializer-list bad-alloc mmx

我从下面用gcc编译的代码中抛出bad_alloc(尝试4.9.3、5.40和6.2)。 gdb告诉我它发生在unordered_map的initalizer_list的最后一行。如果我注释掉mmx指令_m_maskmovq,就没有错误。同样,如果我注释掉unordered_map的初始化,这也没有错误。仅当调用mmx指令并使用initializer_list初始化unordered_map时,我才能获得bad_alloc。如果我默认构造unordered_map并调用map.emplace(1,1),那么也没有错误。我已经在具有48核(intel xeon)和376 GB RAM的centos7机器上以及在Ubuntu WSL下的Dell笔记本电脑(intel core i7)上运行了该软件,结果相同。这里发生了什么? MMX指令是否在破坏堆? Valgrind似乎没有发现任何有用的东西。

编译器命令和输出:

$g++ -g -std=c++11 main.cpp
$./a.out
   terminate called after throwing an instance of 'std::bad_alloc'
   what():  std::bad_alloc
   Aborted

源代码(main.cpp):

#include <immintrin.h>
#include <unordered_map>

int main()
{
  __m64 a_64 = _mm_set_pi8(0,0,0,0,0,0,0,0);
  __m64 b_64 = _mm_set_pi8(0,0,0,0,0,0,0,0);
  char dest[8] = {0};
  _m_maskmovq(a_64, b_64, dest);

  std::unordered_map<int, int> map{{ 1, 1}};
}

更新: _mm_empty()解决方法确实解决了此示例。当使用多线程代码(其中一个线程在执行矢量指令,而另一个线程在使用unordered_map)时,这似乎不是可行的解决方案。另一个有趣的观点是,如果我在-O3上启用了优化功能,那么bad_alloc就会消失。手指交叉,我们在生产过程中从未遇到过这个错误(痛苦)。

1 个答案:

答案 0 :(得分:2)

没有堆损坏。发生这种情况是因为std::unordered_map内部使用long double,用于根据初始化程序中元素的数量来计算存储桶数(请参阅libstdc ++源代码中的_Prime_rehash_policy::_M_bkt_for_elements)。

从MMX代码切换到FPU代码之前,必须先调用_mm_empty。这与一个历史性决定有关,就是将FPU寄存器重用于MMX寄存器文件(这与现代CPU中的寄存器重命名相反)。

如果添加了_mm_empty调用,则异常消失:

…
  _m_maskmovq(a_64, b_64, dest);
  _mm_empty();
  std::unordered_map<int, int> map{{ 1, 1}};
…

请参见GCC PR 88998所标识的cpplearner

ongoing work to implement the MMX intrinsics with SSE on x86-64,它将使此问题消失,因为SSE指令不会影响FPU状态,反之亦然。