Обладает ли int32_t меньшей задержкой, чем int8_t, int16_t и int64_t?

(Я имею в виду процессоры Intel и в основном GCC, но, возможно, ICC или MSVC)

Это правда, используя int8_t, int16_t или же int64_t менее эффективен по сравнению с int32_tиз-за дополнительных инструкций, генерируемых для преобразования между размером слова процессора и выбранным размером переменной?

Мне было бы интересно, если у кого-нибудь есть примеры или лучшие практики для этого? Иногда я использую переменные меньшего размера, чтобы уменьшить нагрузку на строку кэша, но, скажем, я использовал только 50 байтов строки кэша с одной переменной, являющейся 8-битным целым, это может быть более быстрой обработкой с использованием оставшегося пространства строки кэша и повышением 8-битного целого до 32-битный int и т. Д.?

2

Решение

Вы можете набить больше uint8_ts в строку кэша, так что загрузка N uint8_ts будет быстрее, чем загрузка N uint32_ts.

Кроме того, если вы используете современный чип Intel с инструкциями SIMD, умный компилятор будет векторизовать все, что может. Опять же, использование небольшой переменной в вашем коде позволит компилятору вставлять больше строк в регистр SIMD.

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

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

(Раньше снова было верно, что на рабочей станции Sun, используя double был значительно быстрее, чем float, потому что аппаратное обеспечение поддерживается только double, Я не думаю, что это больше верно для современного x86, поскольку оборудование SIMD (SSE и т. Д.) Имеет прямую поддержку одинарной и двойной точности).

5

Другие решения

Марк Лаката отвечает точками в правильном направлении.
Я хотел бы добавить несколько моментов.

Прекрасным ресурсом для понимания и принятия решения по оптимизации являются Agner документы.

Таблицы инструкций Документ имеет время ожидания для наиболее распространенных инструкций. Вы можете видеть, что некоторые из них работают лучше в оригинальной версии.
mov например, могут быть устранены, а mul меньше задержек
Однако здесь мы говорим о получении 1 часа, нам пришлось бы выполнить много инструкций, чтобы компенсировать промах кеша.
Если бы это была целая история, это не стоило бы того.

Настоящие проблемы возникают с декодерами.
Когда вы используете некоторые префиксы с изменяющейся длиной (и вы будете использовать слово нестандартного размера), декодер берет дополнительные циклы.

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

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

Другой аспект, который следует учитывать, заключается в том, что при использовании не родного требует префикса инструкции и тем самым генерируя больший код.
Это как можно ближе к заявлениюсоздаются дополнительные инструкции для преобразования размера слова ЦП в выбранный размер переменной«поскольку процессор Intel может обрабатывать не родные размеры слов.
С другими, особенно RISC, процессорами это обычно не так, и можно генерировать больше инструкций.

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

Также ничего не стоит, что в обычном x64 ABI стек должен быть выровнен по 16-байтовой границе и что обычно компилятор сохраняет локальные переменные в собственном размере слова или близком (например, DWORD в 64-битной системе).
Только если вы выделяете достаточное количество локальных переменных или используете массив или упакованные структуры, вы можете получить выгоду от использования небольшого переменного размера.
Если вы объявите один uint16_t var, это, вероятно, займет то же самое пространство стека одного uint64_tтак что лучше всего пойти на самый быстрый размер.

Кроме того, когда дело доходит до кэша данных, это местонахождение это имеет значение, а не только размер данных.

К счастью, вам не нужно выбирать между маленькими данными или маленьким кодом.

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

t = my_big_data[i];

Вот мой подход:

  • Держать внешний представление данных, то есть my_big_data массив, как можно меньше. Например, если в этом массиве хранятся температуры, используйте кодированный uint8_t для каждого элемента.

  • Держать внутренний представление данных, то есть t переменная, максимально приближенная к размеру слова процессора. Например t может быть uint32_t или же uint64_t,

Таким образом, вы программируете оптимизировать оба кэша и использовать собственный размер слова.
В качестве бонуса вы можете позже принять решение перейти на SIMD инструкции без необходимости перепаковывать my_big_data макет памяти.


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

Когда вы проектируете свои структуры, структура памяти будет ориентирована на проблемы. Например, для значений возраста требуется 8 бит, для городских расстояний в милях — 16 бит.
При кодировании алгоритмы используют самый быстрый тип, компилятор известен иметь для этой сферы. Например, целые числа быстрее, чем числа с плавающей запятой, uint_fast8_t не медленнее, чем uint8_t,

Когда наступит время улучшать производительность, начните с изменения алгоритма (используя более быстрые типы, исключая избыточные операции и т. Д.), А затем если необходимо структуры данных (путем выравнивания, заполнения, упаковки и т. д.).

4

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector