bad_alloc с unordered_map initializer_list и инструкцией MMX, возможно ли повреждение кучи? - PullRequest
0 голосов
/ 09 февраля 2019

Я получаю bad_alloc сгенерированный из кода ниже, скомпилированного с gcc (пробовал 4.9.3, 5.40 и 6.2).GDB говорит мне, что это происходит в последней строке с initalizer_list для unordered_map.Если я закомментирую инструкцию mmx _m_maskmovq, ошибки не будет.Точно так же, если я закомментирую инициализацию unordered_map, это не ошибка.Только при вызове инструкции mmx и инициализации unordered_map с помощью initializer_list я получаю bad_alloc.Если я по умолчанию создаю unordered_map и вызываю map.emplace(1,1), то ошибки также нет.Я запустил это на машине centos7 с 48 ядрами (intel xeon) и 376 ГБ ОЗУ, а также на ноутбуке Dell (intel core i7) под Ubuntu WSL с тем же результатом.Что здесь происходит?Инструкция 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 исчезнет.Скрестив пальцы, мы никогда не сталкивались с этой ошибкой во время производства (cringe).

1 Ответ

0 голосов
/ 09 февраля 2019

Нет повреждения кучи.Это происходит потому, что std::unordered_map использует long double внутренне, для вычисления количества сегментов по количеству элементов в инициализаторе (см. _Prime_rehash_policy::_M_bkt_for_elements в источниках libstdc ++).

Необходимо вызвать _mm_emptyперед переключением с кода MMX на код FPU.Это связано с историческим решением повторно использовать регистры FPU для файла регистров MMX (в отличие от переименования регистров в современных процессорах).

Исключение исчезает, если добавлен вызов _mm_empty:

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

См. GCC PR 88998 , как указано cpplearner .

В настоящее время продолжается работа по внедрению встроенных функций MMX сSSE на x86-64 , что устранит эту проблему, поскольку инструкции SSE не влияют на состояние FPU и наоборот.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...