При отладке некоторого кода с использованием AVX я получал бессмысленные результаты. Я сократил свою программу до следующего:
#include <iostream>
#include <immintrin.h>
int main()
{
while (1)
{
static float v[] = {1, 2, 3, 4, 5, 6, 7, 8};
__m256 v8 = _mm256_load_ps(v);
std::cout << v8.m256_f32[2] << v8.m256_f32[5];
}
}
Когда я запускаю эту программу, она бесконечно печатает 36
, что правильно (она печатает 3
, а затем 6
). Если я устанавливаю точку останова в отладчике внутри цикла и выполняю пошаговое выполнение, он печатает 30
. Если я удалю точку останова и продолжу программу, она снова начнет печатать 36
. Я вижу такое же поведение в Release/Debug, Win32/x86 (4 комбинации).
Чтобы иметь возможность использовать AVX, я установил для параметра «Включить расширенный набор инструкций» значение «Расширенные векторные расширения (/arch:AVX)» в свойствах конфигурации — C/C++ — Генерация кода. Я забыл установить какую-то другую конфигурацию?
В результате такого поведения я не могу использовать отладчик для отладки моей реальной программы (не включенной сюда). Это раздражает.
Я сделал что-то не так? Можно ли исправить это поведение?
Моя Visual Studio: MS Visual Studio Professional 2017, версия 15.9.3.
FWIW, я не наблюдаю такого поведения в MSVC2019, x64 Debug/Release. Я получаю 36 как при беге, так и при прохождении.
Если вы посмотрите на генератор кода asm, MSVC с этими параметрами должен генерировать обычные инструкции AVX с правильным использованием vzeroupper, например, на godbolt.org/z/YMobhKK56. (Используя
-O2
, мы видим, что VS19.28 сохраняет вектор в стеке и выполняет загрузку YMM + перетасовку для каждого доступа к элементу объединения, не используя преимущество сохраненного вызова xmm5..15). Вы строите для 32-битной или x86-64? Хотя, конечно, это похоже на проблему с отладчиком; этот код является законным и должен быть четко определен в MSVC, а изменение поведения при пошаговом или нет является огромным красным флагом.Я вижу такое же поведение в Release/Debug, Win32/x86 (4 комбинации). Я вижу
vzeroupper
в разборке, но он находится в забавном месте внутри цикла. Я еще немного подправлю свой код, чтобы посмотреть, смогу ли я понять его.Возможно, IT: Разве
_mm256_load_ps
не требует выравнивания границ по 32 байтам? Что, кажется, не гарантируется в вашем случае.не могу воспроизвести в visual studio 2017 15.9.37
Первоначально я использовал
_mm256_loadu_ps
; изменен на_mm256_load_ps
позже. Такое же поведение для обоих.vzeroupper
позволяет избежать остановок перехода AVX/SSE при вызове функции после использования регистров YMM. Стандартное соглашение состоит в том, чтобы иметь чистые верхние значения при вызове/возврате, если только функция фактически не принимает аргумент в регистре YMM и, таким образом, можно предположить, что она не использует устаревшие инструкции SSE. MSVC глуп и использует регистры YMM в процессе извлечения элемента из вектора в памяти, поэтому ему нуженvzeroupper
, чтобы избежать риска производительности. Ваши правильные результаты при отсутствии пошагового выполнения доказывают, чтоvzeroupper
не является причиной проблемы.@DanielLangr: MSVC (и ICC) всегда использовать загрузки/сохранения AVX без выравнивания, такие как
vmovups
, даже если вы используете встроенную функцию, требующую выравнивания. Таким образом, они устраняют возможность обнаружения несовмещенных данных, намеренно используя требуемые для выравнивания загрузки/сохранения в отладочной сборке (где они не будут складываться в исходные операнды памяти для других инструкций, которые не требуют выравнивания).