Отказ от ответственности
На основании комментария, относящегося к Altive c, это спецификация c для архитектуры Power, с которой я не знаком. Кроме того, код является неполным, но похоже, что выделенная память организована в один или несколько смежных буферов, и настройка размера работает только при наличии нескольких буферов. Мы не знаем, как данные доступны в этих буферах. В этом ответе будет много предположений, вплоть до того, что он может быть совершенно неверным. Я публикую его в основном потому, что он слишком велик для комментария.
Ответ (вроде)
Я вижу одно возможное преимущество модификации размера. Во-первых, давайте вспомним некоторые подробности об архитектуре Power:
- Altive c векторный размер составляет 16 байтов (128 бит)
- Размер строки кэша составляет 128 байтов
Теперь давайте рассмотрим пример, в котором AllocateBuffers
выделяет память для 4 буферов (то есть mABL.mNumberBuffers
равно 4), а nBytes
равно 256. Давайте посмотрим, как эти буферы расположены в памяти:
| Buffer 1: 256+16=272 bytes | Buffer 2: 272 bytes | Buffer 3: 272 bytes | Buffer 4: 272 bytes |
^ ^ ^ ^
| | | |
offset: 0 272 544 816
Обратите внимание на значения смещений и сравните их с границами строк кэша. Для простоты предположим, что память выделена на границе строки кэша. Это не имеет большого значения, как будет показано ниже.
- Буфер 1 начинается со смещения 0, которое является началом строки кэша.
- Буфер 2 начинается через 16 байт граница строки кэша (со смещением 2 * 128 = 256).
- Буфер 3 начинается на 32 байта после границы строки кэша (со смещением 4 * 128 = 512).
- Буфер 4 начинается через 48 байт за границу строки кэша (которая имеет смещение 6 * 128 = 768).
Обратите внимание, как смещение от ближайшей границы строки кэша увеличивается на 16 байтов. Теперь, если мы предположим, что данные в каждом из буферов будут доступны в 16-байтовых блоках, в прямом направлении, в al oop, то строки кэша извлекаются из памяти в довольно специфическом c порядке. Давайте рассмотрим середину l oop (поскольку в начале ЦПУ придется извлекать строки кэша для начала каждого буфера):
- Итерация 5
- Загрузка из буфера 1 со смещением 5 * 16 = 80, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях.
- Загрузка из буфера 2 со смещением 352, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях , Граница строки кэша по смещению 256, мы смещены по 96.
- Загрузка из буфера 3 по смещению 624, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях. Граница строки кэша находится по смещению 512, мы находимся по ее смещению 112.
- Загружаем из буфера 4 по смещению 896, мы достигаем новую границу строки кеша и выбираем новую строку кэша из памяти.
- Итерация 6
- Загрузка из буфера 1 со смещением 6 * 16 = 96, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях.
- Загрузка из буфера 2 по смещению 368, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях. Граница строки кеша находится по смещению 256, мы находимся по смещению 112.
- Загружаем из буфера 3 по смещению 640, мы достигаем новую границу строки кеша и получаем новую строку кэша из памяти.
- Загрузка из буфера 4 со смещением 896, мы все еще используем строку кэша, которая была извлечена на последней итерации. Граница строки кэша находится по смещению 896, мы смещены на 16.
- Итерация 7
- Загрузка из буфера 1 со смещением 7 * 16 = 112, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях.
- Загрузка из буфера 2 со смещением 384, мы достигли новой границы строки кэша и извлекли новую строку кэша из памяти.
- Загрузка из буфера 3 по смещению 656, мы все еще используем строку кэша, которая была извлечена на последней итерации. Граница строки кэша по смещению 640, мы смещены на 16.
- Загрузка из буфера 4 по смещению 912, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях. Граница строки кэша находится по смещению 896, мы смещены по 32.
- Итерация 8
- Загрузка из буфера 1 со смещением 8 * 16 = 128, мы достиг новой границы кеша границы и извлекает новую строку кеша из памяти.
- Загрузка из буфера 2 со смещением 400, мы все еще используем строку кеша, которая была извлечена на предыдущих итерациях. Граница строки кэша по смещению 384, мы смещены на 16.
- Загрузка из буфера 3 по смещению 672, мы все еще используем строку кэша, которая была выбрана на предыдущих итерациях. Граница строки кэша по смещению 640, мы смещены по 32.
- Загрузка из буфера 4 по смещению 944, мы все еще используем строку кэша, которая была извлечена на предыдущих итерациях. Граница строки кэша по смещению 896, мы смещены на 48.
Обратите внимание, что порядок, в котором новые строки кэша извлекаются из памяти, не зависит от порядок доступа к буферам в каждой итерации l oop. Кроме того, это не зависит от того, было ли выделено все выделение памяти по границе строки кэша. Также обратите внимание, что если бы доступ к содержимому буфера осуществлялся в обратном порядке, то строки кэша были бы извлечены в прямом порядке, но все еще в порядке.
Эта упорядоченная выборка строк кэша может помочь аппаратному предпочтителю в ЦП, поэтому, когда следующая l oop итерация выполнена, требуемая строка кэша уже предварительно выбрана. Без него каждая 8-я итерация l oop потребовала бы 4 новых строки кэша в любом порядке, в котором программа обращается к буферам, что можно интерпретировать как произвольный доступ к памяти и затруднить предварительную выборку. В зависимости от сложности l oop, эта выборка из 4 строк кэша может не быть скрыта из-за неправильной модели выполнения и может привести к остановке. Это менее вероятно, когда вы выбираете до 1 строки кэша за итерацию.
Еще одно возможное преимущество - избегать псевдонимов адресов . Я не знаю организацию кэша Power, но если nBytes
кратно размеру страницы, использование нескольких буферов одновременно, когда каждый буфер выравнивается по странице, может привести к множеству ложных зависимостей и затруднить store -нагрузочная пересылка . Хотя код выполняет корректировку не только в случае, когда nBytes
кратен размеру страницы, так что псевдонимы, вероятно, не были главной проблемой.
Правильно ли я думаю, что вышеуказанная функция будет работать корректно только на основании предположения, что новый оператор вернет выровненную память размером не менее 16 байтов? В C ++ оператор new определяется как возвращающий указатель на хранилище с выравниванием, подходящим для любого объекта с фундаментальным требованием выравнивания, которое не обязательно должно быть 16 байтов.
Да, C ++ не гарантировать любое конкретное выравнивание, кроме того, которое подходит для хранения любого объекта фундаментального типа. В C ++ 17 добавлена поддержка динамического выделения c для выровненных типов.
Однако даже в старых версиях C ++ каждый компилятор также придерживается спецификации ABI целевой системы, которая может указывать выравнивание для выделения памяти , На практике во многих системах malloc
возвращает как минимум 16-байтовые выровненные указатели, а operator new
использует память, возвращаемую malloc
или аналогичным API нижнего уровня.
Хотя это не переносимо, и поэтому не рекомендуется практика. Если вам требуется определенное выравнивание, убедитесь, что вы компилируете для C ++ 17, или используйте специализированные API, такие как posix_memalign
.