Является ли SSE невыровненной нагрузкой внутренней, чем медленная, чем выровненная нагрузка на процессоры Intel x64_64?

Я рассматриваю возможность изменения кода высокой производительности кода, который в настоящее время требует 16 байт выровненных массивов и использует _mm_load_ps для ослабления ограничения выравнивания и использования _mm_loadu_ps. Существует много мифов о влиянии производительности на выравнивание памяти для инструкций SSE, поэтому я сделал небольшой тестовый пример того, что должно быть связанным циклом полосы пропускания памяти. Используя встроенную или невыровненную нагрузку, она выполняет 100 итераций через большой массив, суммируя элементы с внутренними функциями SSE. Исходный код здесь. https://gist.github.com/rmcgibbo/7689820

Результаты на 64-битном MacBook Pro с Sandy Bridge Core i5 приведены ниже. Более низкие показатели указывают на более высокую производительность. Когда я прочитал результаты, я не вижу в основном никакого штрафа за производительность от использования _mm_loadu_ps в неизмененной памяти.

Я нахожу это удивительным. Является ли это справедливым тестом/обоснованным заключением? На каких аппаратных платформах есть разница?

$ gcc -O3 -msse aligned_vs_unaligned_load.c  && ./a.out  200000000
Array Size: 762.939 MB
Trial 1
_mm_load_ps with aligned memory:    0.175311
_mm_loadu_ps with aligned memory:   0.169709
_mm_loadu_ps with unaligned memory: 0.169904
Trial 2
_mm_load_ps with aligned memory:    0.169025
_mm_loadu_ps with aligned memory:   0.191656
_mm_loadu_ps with unaligned memory: 0.177688
Trial 3
_mm_load_ps with aligned memory:    0.182507
_mm_loadu_ps with aligned memory:   0.175914
_mm_loadu_ps with unaligned memory: 0.173419
Trial 4
_mm_load_ps with aligned memory:    0.181997
_mm_loadu_ps with aligned memory:   0.172688
_mm_loadu_ps with unaligned memory: 0.179133
Trial 5
_mm_load_ps with aligned memory:    0.180817
_mm_loadu_ps with aligned memory:   0.172168
_mm_loadu_ps with unaligned memory: 0.181852

Ответы

Ответ 1

У вас много шума в ваших результатах. Я перезапустил это на Xeon E3-1230 V2 @3.30GHz, работающем под управлением Debian 7, выполнив 12 прогонов (отбрасывая первый, чтобы учитывать шум виртуальной памяти), на массив 200000000, с 10 итерациями для i в рамках эталонных функций, явный noinline для функций, которые вы предоставили, и каждый из трех тестов, выполняемых изолированно: https://gist.github.com/creichen/7690369

Это было с gcc 4.7.2.

noinline гарантировал, что первый тест не был оптимизирован.

Точный вызов

./a.out 200000000 10 12 $n

для $n от 0 до 2.

Вот результаты:

load_ps aligned

min:    0.040655
median: 0.040656
max:    0.040658

loadu_ps aligned

min:    0.040653
median: 0.040655
max:    0.040657

loadu_ps unaligned

min:    0.042349
median: 0.042351
max:    0.042352

Как вы можете видеть, это некоторые очень жесткие ограничения, которые показывают, что loadu_ps работает медленнее при неизменном доступе (замедление примерно на 5%), но не на выровненном доступе. Очевидно, что на этой конкретной машине loadu_ps не платит за выравнивание доступа к памяти.

Рассматривая сборку, единственная разница между версиями load_ps и loadu_ps заключается в том, что последняя включает инструкцию movups, повторно заказывает некоторые другие команды для компенсации и использует несколько разные имена регистров. Последнее, вероятно, совершенно не имеет значения, и первое может быть оптимизировано во время преобразования микрокода.

Теперь трудно сказать (не будучи инженером Intel с доступом к более подробной информации), будет ли/как команда movups оптимизирована, но учитывая, что процессорный кремний не будет платить штраф за простое использование выровненных данных путь, если младшие биты в адресе загрузки равны нулю, а неровный путь данных в противном случае, что кажется мне правдоподобным.

Я попробовал то же самое на моем ноутбуке Core i7 и получил очень похожие результаты.

В заключение я бы сказал, что да, вы платите штраф за доступ к неограниченному доступу к памяти, но он достаточно мал, чтобы его можно было заглушить другими эффектами. В прогонах, о которых вы сообщали, кажется, что достаточно шума, чтобы гипотеза о том, что он медленнее для вас тоже (обратите внимание, что вы должны игнорировать первый запуск, так как ваше первое испытание будет платить за разогрев таблицы страниц и кешей.)

Ответ 2

Здесь есть два вопроса: неуравновешенные нагрузки медленнее, чем выровненные нагрузки при одинаковых согласованных адресах? И есть ли грузы с невысокими адресами медленнее нагрузок с выровненными адресами?

Старые процессоры Intel ( "более старые" в этом случае всего несколько лет назад) имели небольшие штрафы за производительность при использовании невыровненных инструкций загрузки с выровненными адресами по сравнению с согласованными нагрузками с новыми адресами. Более новые процессоры, как правило, не имеют этой проблемы.

Как у старых, так и у более новых процессоров Intel есть штрафы за производительность при загрузке с неаудированных адресов, особенно когда строки кэша пересекаются.

Поскольку детали зависят от модели процессора и модели процессора, вам нужно будет проверить каждый отдельно для деталей.

Иногда проблемы с производительностью могут маскироваться. Простые последовательности инструкций, используемых для измерения, могут не указывать на то, что инструкции с неравномерной нагрузкой сохраняют загрузочные устройства более загруженными, чем инструкции с выравниваемой нагрузкой, так что будет ухудшение производительности, если в первом случае были предприняты некоторые дополнительные операции, но не в последнем.

Ответ 3

Это зависит от архитектуры, и последние поколения значительно улучшили ситуацию. С другой стороны, с более старой архитектурой Core2:

$ gcc -O3 -fno-inline foo2.c -o a; ./a 1000000 
Array Size: 3.815 MB                    
Trial 1
_mm_load_ps with aligned memory:    0.003983
_mm_loadu_ps with aligned memory:   0.003889
_mm_loadu_ps with unaligned memory: 0.008085
Trial 2
_mm_load_ps with aligned memory:    0.002553
_mm_loadu_ps with aligned memory:   0.002567
_mm_loadu_ps with unaligned memory: 0.006444
Trial 3
_mm_load_ps with aligned memory:    0.002557
_mm_loadu_ps with aligned memory:   0.002552
_mm_loadu_ps with unaligned memory: 0.006430
Trial 4
_mm_load_ps with aligned memory:    0.002563
_mm_loadu_ps with aligned memory:   0.002568
_mm_loadu_ps with unaligned memory: 0.006436
Trial 5
_mm_load_ps with aligned memory:    0.002543
_mm_loadu_ps with aligned memory:   0.002565
_mm_loadu_ps with unaligned memory: 0.006400

Ответ 4

См. "§2.4.5.1" Эффективная обработка опасностей выравнивания "в Архитектуры Intel® 64 и IA-32 Справочное руководство по оптимизации:

Подсистемы кэша и памяти обрабатывают значительный процент инструкций в каждой рабочей нагрузке. Различные сценарии выравнивания адресов будут оказывать различное влияние производительности на операции с памятью и кешем. Например, 1-циклная пропускная способность L1 (см. Таблицу 2-25) обычно применяется к естественно выровненным нагрузкам из кеша L1. Но с использованием невыложенных инструкций загрузки (например, MOVUPS, MOVUPD, MOVDQU и т.д.) Для доступа к данным из L1 будет испытывать различное количество задержек в зависимости от конкретных микроархитектур и сценариев выравнивания.

Я не мог скопировать таблицу здесь, это в основном показывает, что выровненные и невыровненные нагрузки L1 равны 1 циклу; граница разделительной кэш-линии составляет ~ 4,5 цикла.