Выравнивание памяти на современных процессорах? - PullRequest
12 голосов
/ 06 декабря 2009

Я часто вижу код, такой как следующий, когда, например, представляю большое растровое изображение в памяти:

size_t width = 1280;
size_t height = 800;
size_t bytesPerPixel = 3;
size_t bytewidth = ((width * bytesPerPixel) + 3) & ~3; /* Aligned to 4 bytes */
uint8_t *pixelData = malloc(bytewidth * height);

(то есть битовый массив, выделенный как непрерывный блок памяти, имеющий bytewidth, выровненный по определенному числу байтов, чаще всего 4)

Точка на изображении затем задается через:

pixelData + (bytewidth * y) + (bytesPerPixel * x)

Это приводит меня к двум вопросам:

  1. Влияет ли подобное выравнивание буфера на производительность современных процессоров? Должен ли я вообще беспокоиться о выравнивании, или компилятор справится с этим?
  2. Если это окажет влияние, может кто-нибудь указать мне на ресурс, чтобы найти идеальное выравнивание байтов для различных процессоров?

Спасибо.

Ответы [ 4 ]

7 голосов
/ 06 декабря 2009

Это зависит от множества факторов. Если вы получаете доступ к пиксельным данным по одному байту за раз, выравнивание не будет иметь значения в подавляющем большинстве случаев. Для чтения / записи одного байта данных большинству процессоров совершенно не важно, находится ли этот байт на 4-байтовой границе или нет.

Однако, если вы обращаетесь к данным в единицах размером больше байта (скажем, в 2-байтовых или 4-байтовых единицах), то вы обязательно увидите эффекты выравнивания. Для некоторых процессоров (например, для многих процессоров RISC) доступ к невыровненным данным на определенных уровнях совершенно незаконен: попытка прочитать 4-байтовое слово по адресу, который не выровнен по 4 байту, приведет к созданию исключения доступа к данным (или исключения хранения данных) ) на PowerPC, например.

На других процессорах (например, x86) доступ к невыровненным адресам разрешен, но это часто сопровождается скрытым снижением производительности. Загрузка / сохранение памяти часто реализуется в микрокоде, и микрокод обнаружит не выровненный доступ. Обычно микрокод извлекает правильное 4-байтовое количество из памяти, но если он не выровнен, ему придется извлечь два 4-байтовых местоположения из памяти и восстановить желаемое 4-байтовое количество из соответствующего байты двух локаций. Извлечение двух ячеек памяти, очевидно, медленнее, чем одного.

Это только для простых загрузок и магазинов. Некоторые инструкции, например, в наборах команд MMX или SSE, требуют, чтобы их операнды памяти были правильно выровнены. Если вы попытаетесь получить доступ к невыровненной памяти с помощью этих специальных инструкций, вы увидите что-то вроде исключения недопустимой инструкции.

Подводя итог, я бы не особо беспокоился о выравнивании, если бы вы не писали очень критичный для производительности код (например, в сборке). Компилятор вам очень поможет, например добавляя структуры так, чтобы 4-байтовые величины были выровнены по 4-байтовым границам, а на x86 ЦП также помогает вам при работе с не выровненными обращениями. Поскольку данные о пикселях, с которыми вы имеете дело, имеют размеры 3 байта, вы почти всегда будете делать однобайтовые обращения в любом случае.

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

Исходя из вашего кода, я предполагаю, что это связано с чтением формата растрового файла Windows - для растровых файлов длина каждой строки развертки должна быть кратна 4 байтам, поэтому настройка буферов пиксельных данных с этим свойством имеет свойство, которое вы можете просто прочитать во всем растровом изображении одним махом в свой буфер (конечно, вам все равно придется иметь дело с тем фактом, что строки развертки хранятся снизу вверх, а не сверху вниз, и что данные пикселей - BGR вместо RGB). Это, на самом деле, не слишком большое преимущество - его не так сложно прочитать в растровом изображении по одной строчке за раз.

4 голосов
/ 06 декабря 2009

Да, выравнивание оказывает влияние на производительность современных - скажем, x86 - процессоров. Как правило, загрузка и хранение данных происходят на границах естественного выравнивания; если вы получаете 32-битное значение в регистр, оно будет самым быстрым, если оно уже выровнено по 32-битной границе. Если это не так, x86 «позаботится об этом за вас», в том смысле, что процессор все равно будет выполнять нагрузку, но для этого потребуется значительно большее количество циклов, потому что будет внутренний спор для » заново выровняйте "доступ".

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

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

Обратите внимание, что в приведенном выше примере выравнивается только каждая «строка» данных пикселей. Сами пиксели по-прежнему имеют длину 3 байта и часто не выровнены внутри «линий», поэтому здесь нет особой выгоды. Например, существуют форматы текстур, которые имеют 3 байта реальных данных на пиксель и буквально просто тратят лишний байт на каждый из них для выравнивания данных.

Здесь есть более общая информация: http://en.wikipedia.org/wiki/Data_structure_alignment

(Конкретные характеристики варьируются в зависимости от архитектуры, как в отношении естественного выравнивания, так и в том, обрабатывает ли ЦП автоматическую загрузку / сохранение без выравнивания, и в зависимости от того, насколько дорогими они в конечном итоге оказываются. часто среда выполнения компилятора / C делает все возможное, чтобы выполнить эту работу за вас.)

1 голос
/ 06 декабря 2009

Выравнивание буфера оказывает влияние. Вопрос: это значительное влияние? Ответ может быть очень для конкретного приложения . В архитектурах, которые изначально не поддерживают невыровненный доступ - например, 68000 и 68010 (в 68020 добавлен невыровненный доступ) - это действительно проблема производительности и / или обслуживания, поскольку ЦП выйдет из строя или может перехватить обработчик для выполнения невыровненного доступа .

Можно оценить идеальное выравнивание для различных процессоров: 4-байтовое выравнивание подходит для архитектур с 32-битным трактом данных. 8-байтовое выравнивание для 64-битных. Однако кеширование L1 дает эффект . Для многих процессоров это 64 байта, хотя, без сомнения, изменится в будущем.

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

Ваш пример довольно своеобразен: 3-байтовые элементы имеют 50% -ную вероятность индивидуального выравнивания (до 32 бит), поэтому выравнивание буфера кажется бессмысленным - по крайней мере, из соображений производительности. Тем не менее, в случае массовой передачи всего, это оптимизирует первый доступ. Обратите внимание, что невыровненный первый байт может также повлиять на производительность при передаче на видеоконтроллер.

1 голос
/ 06 декабря 2009
  • Влияет ли подобное выравнивание буфера на производительность современных процессоров?

Да. Например, если memcpy оптимизирован с использованием инструкций SIMD (например, MMX / SSE), некоторые операции будут выполняться быстрее с выровненной памятью. В некоторых архитектурах есть инструкции (процессора), которые не выполняются, если данные не выровнены, поэтому что-то может работать на вашем компьютере, но не на другом.

Используя выровненные данные, вы также лучше используете кэш процессора.

  • Должен ли я вообще беспокоиться о выравнивании, или компилятор справится с этим?

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

Для других вещей в вашем коде у вас есть флаг -malign и выровненный атрибут для игры.

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