Мне известно о падении производительности при смешивании подписанных целых с плавающей точкой.
Хуже ли смешивать неподписанные целые с поплавками?
Есть ли хит при смешивании подписанного / неподписанного без поплавков?
Влияют ли разные размеры (u32, u16, u8, i32, i16, i8) на производительность? На каких платформах?
c++
performance
Луис
источник
источник
Ответы:
Большой штраф от смешивания целых чисел (любого типа) и чисел с плавающей точкой заключается в том, что они находятся в разных наборах регистров. Чтобы перейти от одного набора регистров к другому, вы должны записать значение в память и прочитать его обратно, что приводит к остановке load-hit-store .
При переходе между разными размерами или числом знаков со знаком все остается в одном наборе регистров, поэтому вы избежите большого штрафа. Из-за расширения знака и т. Д. Могут быть меньшие штрафы, но они намного меньше, чем в хранилище при загрузке.
источник
Я подозреваю, что информация о Xbox 360 и PS3 определенно будет находиться за стенами только для лицензированных разработчиков, как и большинство низкоуровневых деталей. Однако мы можем создать эквивалентную программу x86 и разобрать ее, чтобы получить общее представление.
Для начала посмотрим, что стоит беззнаковое расширение:
Соответствующая часть разбирается на (с помощью GCC 4.4.5):
Таким образом, в основном то же самое - в одном случае мы перемещаем байт, в другом мы перемещаем слово. Следующий:
Превращается в:
Таким образом, стоимость расширения знака является любой ценой,
movsbl
а неmovzbl
является уровнем подинструкций. В принципе невозможно дать количественную оценку современным процессорам из-за того, как работают современные процессоры. Все остальное, от скорости памяти до кэширования до того, что было в конвейере заранее, будет доминировать во время выполнения.За ~ 10 минут, которые потребовались мне для написания этих тестов, я мог легко найти реальную ошибку производительности, и как только я включил любой уровень оптимизации компилятора, код стал неузнаваемым для таких простых задач.
Это не переполнение стека, поэтому я надеюсь, что никто здесь не будет утверждать, что микрооптимизация не имеет значения. Игры часто работают с очень большими и очень числовыми данными, поэтому тщательное внимание к ветвлению, приведению, планированию, выравниванию структуры и т. Д. Может дать очень важные улучшения. Любой, кто потратил много времени на оптимизацию кода PPC, вероятно, имеет по крайней мере одну ужасную историю о загрузочных хит-магазинах. Но в этом случае это действительно не имеет значения. Размер хранилища целочисленного типа не влияет на производительность, если он выровнен и помещается в регистр.
источник
Целочисленные операции со знаком могут быть более дорогими практически на всех архитектурах. Например, деление на константу быстрее без знака, например:
будет оптимизирован для:
Но...
оптимизировать для:
или в системах, где разветвление дешево,
То же самое и по модулю. Это также верно для не-степеней-2 (но пример более сложный). Если ваша архитектура не имеет аппаратного деления (например, большинство ARM), беззнаковые деления неконстант также быстрее.
В общем, сообщение компилятору о том, что отрицательные числа не могут быть получены, поможет оптимизировать выражения, особенно те, которые используются для завершения цикла и других условий.
Что касается разных размеров, да, есть небольшое влияние, но вам придется взвесить, а не перемещать память. В наши дни вы, вероятно, получаете больше от доступа к меньшему количеству памяти, чем вы теряете от увеличения размера. Вы очень далеко в микро оптимизации в этот момент.
источник
Операции со знаком или без знака int имеют одинаковую стоимость на текущих процессорах (x86_64, x86, powerpc, arm). На 32-битном процессоре u32, u16, u8 s32, s16, s8 должны быть одинаковыми. Вы можете иметь штраф с плохим выравниванием.
Но преобразование int в float или float в int является дорогостоящей операцией. Вы можете легко найти оптимизированную реализацию (SSE2, Neon ...).
Наиболее важным моментом, вероятно, является доступ к памяти. Если ваши данные не помещаются в кэш L1 / L2, вы потеряете больше цикла, чем конверсия.
источник
Джон Пурди говорит выше (я не могу комментировать), что unsigned может быть медленнее, потому что он не может переполниться. Я не согласен, арифметика без знака - это простая арифметика Муляра по модулю 2 с количеством битов в слове. Подписанные операции в принципе могут быть переполнены, но обычно они отключены.
Иногда вы можете делать умные (но не очень удобочитаемые) вещи, такие как упаковка двух или более элементов данных в int и получение нескольких операций для каждой инструкции (карманная арифметика). Но ты должен понимать, что ты делаешь. Конечно, MMX позволяет делать это естественно. Но иногда использование самого большого размера слова, поддерживаемого HW, и ручная упаковка данных дают вам самую быструю реализацию.
Будьте осторожны с выравниванием данных. На большинстве реализаций HW не выровненные загрузки и хранилища работают медленнее. Естественное выравнивание означает, что, скажем, для 4-байтового слова адрес кратен четырем, а адреса из восьми байтов должны быть кратны восьми байтам. Это переносится в SSE (128-бит поддерживает 16-байтовое выравнивание). AVX скоро расширит эти «векторные» размеры регистров до 256 бит, а затем до 512 бит. И выровненные грузы / хранилища будут быстрее, чем выровненные. Для фанатов HW операция с невыровненной памятью может охватывать такие вещи, как кешлайн и даже границы страниц, для которых HW должен быть осторожен.
источник
Немного лучше использовать целые числа со знаком для индексов цикла, потому что переполнение со знаком не определено в C, поэтому компилятор будет предполагать, что такие циклы имеют меньше угловых случаев. Это контролируется gcc -fstrict-overflow (включено по умолчанию), и эффект, вероятно, трудно заметить, не читая вывод сборки.
Кроме того, x86 работает лучше, если вы не смешиваете типы, потому что он может использовать операнды памяти. Если он должен преобразовывать типы (знак или нулевые расширения), это означает явную загрузку и использование регистра.
Придерживайтесь int для локальных переменных, и большая часть этого произойдет по умолчанию.
источник
Как указывает Целион, накладные расходы на преобразование между целыми числами и числами с плавающей запятой во многом связаны с копированием и преобразованием значений между регистрами. Единственные издержки неподписанных целочисленных объектов сами по себе связаны с их гарантированным поведением с циклическим изменением, что требует определенной проверки переполнения в скомпилированном коде.
В основном, нет необходимости в преобразовании целых чисел со знаком и без знака. Различные размеры целых чисел могут (бесконечно) быстрее или медленнее получать доступ в зависимости от платформы. Вообще говоря, размер целого числа, ближайший к размеру слова платформы, будет самым быстрым для доступа, но общая разница в производительности зависит от многих других факторов, в частности от размера кэша: если вы используете
uint64_t
все, что вам нужноuint32_t
, это может будь то меньше ваших данных будет помещаться в кеш сразу, и вы можете понести некоторые накладные расходы.Хотя немного об этом даже думать. Если вы используете типы, подходящие для ваших данных, все должно работать идеально, а количество энергии, получаемое при выборе типов на основе архитектуры, в любом случае незначительно.
источник