Определение требуемой памяти на целевом процессоре

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

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

Выравниваемый доступ к памяти не требуется на "стандартном" процессоре x86, но это требуется многим другим процессорам и выдает исключение, если правило не соблюдается.

До сих пор я имел дело с этим, заставляя компилятор быть осторожным при обращении к конкретным данным, которые, как известно, рискованно, используя атрибут pack (или прагму). И это прекрасно работает.

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

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

А теперь вопрос: как обнаружить во время компиляции, что целевая архитектура требует строго ориентированного доступа к памяти? (или наоборот)

2 ответа

Решение

Ни одна реализация C, о которой я знаю, не предоставляет какого-либо макроса препроцессора, который бы помог вам разобраться в этом. Поскольку ваш код предположительно работает на широком спектре машин, я предполагаю, что у вас есть доступ к широкому кругу машин для тестирования, поэтому вы можете найти ответ с помощью тестовой программы. Затем вы можете написать свой собственный макрос, что-то вроде ниже:

#if defined(__sparc__)
/* Unaligned access will crash your app on a SPARC */
#define ALIGN_ACCESS 1
#elif defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC)
/* Unaligned access is too slow on a PowerPC (maybe?) */
#define ALIGN_ACCESS 1
#elif defined(__i386__) || defined(__x86_64__) || \
      defined(_M_IX86) || defined(_M_X64)
/* x86 / x64 are fairly forgiving */
#define ALIGN_ACCESS 0
#else
#warning "Unsupported architecture"
#define ALIGN_ACCESS 1
#endif

Обратите внимание, что скорость неприсоединенного доступа будет зависеть от границ, которые он пересекает. Например, если доступ пересекает границу страницы 4 КБ, это будет намного медленнее, и могут быть другие границы, которые заставляют это быть еще медленнее. Даже на x86 некоторые невыровненные обращения не обрабатываются процессором, а обрабатываются ядром ОС. Это невероятно медленно.

Также нет гарантии, что будущая (или текущая) реализация не изменит внезапно характеристики производительности невыровненных обращений. Это случилось в прошлом и может произойти в будущем; PowerPC 601 очень простил доступ, но PowerPC 603e - нет.

Еще более усложняют тот факт, что код, который вы пишете для создания неприровненного доступа, будет отличаться по реализации на разных платформах. Например, на PowerPC это упрощается тем, что x << 32 а также x >> 32 всегда 0, если x 32 бита, но на x86 вам не повезло.

В любом случае, написание кода для строгого выравнивания памяти - хорошая идея. Даже в системах x86, которые разрешают доступ без выравнивания, ваши операции чтения / записи с выравниванием вызовут два обращения к памяти, и некоторая производительность будет потеряна. Нетрудно написать эффективный код, который работает на всех архитектурах ЦП. Запомните простое правило: указатель должен быть выровнен по размеру объекта, который вы читаете или пишете. например, если пишется DWORD, то (dest_pointer & 3 == 0). Использование костылей типа UNALIGNED_PTR приведет к тому, что компилятор сгенерирует неэффективный код. Если у вас есть большой объем унаследованного кода, который должен работать немедленно, то имеет смысл использовать компилятор, чтобы "исправить" ситуацию, но если это ваш код, то напишите его с самого начала, чтобы он работал на всех системах.

Другие вопросы по тегам