Ответ 1
Я нашел проблему с скомпилированным кодом, однако я еще не нашел решение. Но это швы, что это всего лишь проблема clang, и использование g++ исправляет это.
Проблема лучше всего проиллюстрирована, показывая некоторые из полученного кода сборки. auto arr(e);
строка кода скомпилирована для некоторых команд перемещения для копирования данных из вектора в стек, clang использует (при компиляции с -mavx) инструкциями avx2, такими как следующие (синтаксис AT & T):
vmovaps 0xa0(%rax),%ymm0
vmovaps %ymm0,0x120(%rsp)
...
Где% rax - адрес текущего массива в векторе. Цель arr находится в 0x80 (% rsp). Программа будет копироваться в 32-битных фрагментах (256 бит команд avx2).
Однако проблема становится понятной при просмотре значений: %rax = 0x55555556be70
в моем отладочном тесте. проблема заключается в том, что vmovaps (перемещение с совмещенной упакованной одиночной точностью) в 256-битный avx2-регистр ожидает, что цель и источник выровнены с границами 256 бит или 32 байта (0x20), однако% rax выравнивается только по 16 байт. При компиляции без alignas clang использует vmovups (ту же инструкцию, но не требует выравнивания данных).
Таким образом, проблема заключается в том, что распределитель std :: vector не учитывает выравнивание и не выравнивает массив с границами 64 байта. g++ также не выравнивает массив внутри границ вектора до 32 байт и не использует команды avx, когда не используется -O [not 0]. Однако g++ всегда использует 128-битные xmm-регистры, для которых требуется только выравнивание до 16 байтов, к которым распределитель выравнивает данные с обоими компиляторами.
РЕДАКТИРОВАТЬ:
Я просто понял, что забыл скомпилировать с -std = С++ 17. с этим флагом он работает для меня с кланом g++. Код выглядит одинаково, но распределитель правильно выравнивает код на границе 64 байта. Поэтому я предполагаю, что это связано со старой библиотекой. Может быть, вы можете отправить мне свой двоичный код, тогда я мог бы более подробно рассмотреть его.