производительность без знака против целых чисел со знаком



есть ли прирост/потеря производительности при использовании беззнаковых целых чисел над целыми числами со знаком?



Если да, то это идет на короткий и длинный, а также?

677   12  

12 ответов:

деление по степеням 2 быстрее с unsigned int, потому что его можно оптимизировать в одиночную инструкцию переноса. С signed int, это обычно требует больше машинных инструкций, потому что деление раундов к нулю, но сдвиг вправо раундов вниз. Пример:

int foo(int x, unsigned y)
{
    x /= 8;
    y /= 8;
    return x + y;
}

здесь есть соответствующие x часть (подпись отдела):

movl 8(%ebp), %eax
leal 7(%eax), %edx
testl %eax, %eax
cmovs %edx, %eax
sarl , %eax

а вот и соответствующие y часть (беззнаковое деление):

movl 12(%ebp), %edx
shrl , %edx

в C++ (и C) переполнение целого числа со знаком не определено, тогда как переполнение целого числа без знака определено для обертывания. Обратите внимание, что, например, в gcc вы можете использовать флаг-fwrapv для определения переполнения со знаком (для обертывания).

undefined signed integer overflow позволяет компилятору предположить, что переполнения не происходит, что может ввести возможности оптимизации. См., например,этот блог для обсуждения.

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

unsigned приводит к той же или лучшей производительности, чем signed. Некоторые примеры:

  • деление на константу, которая является степенью числа 2 (см. Также ответ от FredOverflow)
  • деление на постоянное число (например, мой компилятор реализует деление на 13, используя 2 инструкции asm для unsigned и 6 инструкций для signed)
  • проверка четности числа (я понятия не имею, почему мой компилятор MS Visual Studio реализует его с 4 инструкции для signed numbers; gcc делает это с 1 инструкцией, как и в unsigned case)

short обычно приводит к той же или худшей производительности, чем int (если sizeof(short) < sizeof(int)). Снижение производительности происходит, когда вы назначаете результат арифметической операции (которая обычно int, не short) к переменной типа short, который хранится в регистре процессора (который тоже типа int). Все преобразования от short до int найдите время и раздражает.

Примечание: некоторые DSPs имеют быстрые инструкции умножения для signed short тип, в данном конкретном случае short быстрее int.

что касается разницы между int и long, я могу только догадываться (я не знаком с 64-разрядными архитектурами). Конечно, если int и long имеют одинаковый размер (на 32-битных платформах), их производительность также то же самое.


очень важное дополнение, отмеченное несколькими людьми:

что действительно важно для большинства приложений объем памяти и используемая пропускная способность. Вы должны использовать наименьшие необходимые целые числа (short, может быть, даже signed/unsigned char) для больших массивов.

это даст лучшую производительность, но коэффициент усиления нелинейный (т. е. не в 2 или 4 раза) и несколько непредсказуемый - это зависит от размера кэша и отношения между вычислениями и передачей памяти в вашем приложение.

Это в значительной степени зависит от конкретного процессора.

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

Если какой-либо из них быстрее, он полностью зависит от процессора, и, скорее всего, разница незначительна, если она вообще существует.

разница в производительности между знаковыми и беззнаковыми целыми числами на самом деле более общая, чем предполагает ответ на принятие. Деление целого числа без знака на любую константу может быть выполнено быстрее, чем деление целого числа со знаком на константу, независимо от того, является ли константа степенью двойки. См.http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html

В конце своего поста, он включает в себя следующий раздел:

естественный вопрос заключается в том, может ли одна и та же оптимизация улучшить подписанное разделение; к сожалению, похоже, что это не так по двум причинам:

приращение дивиденда должно стать увеличением величины, т. е. приращением, если n > 0, уменьшением, если n

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

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

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

если вы хотите иметь производительность должны использовать оптимизацию производительности компилятора который может работать против здравого смысла. Следует помнить, что разные компиляторы могут компилировать код по-разному, и сами они имеют разные виды оптимизации. Если мы говорим о g++ компилятор и говорить о максимизации это уровень оптимизации с помощью -Ofast, или, по крайней мере Ан -O3 флаг, по моему опыту она может составлять long введите в код с еще лучшей производительностью, чем любой unsigned тип, или даже просто int.

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

оптимизированная многопоточная Программа расчета линейной алгебры может легко иметь > 10x разница в производительности точно оптимизированный против неоптимизированного. Так что это имеет значение.

выход оптимизатора противоречит логике во многих случаях. Например, у меня был случай, когда разница между a[x]+=b и a[x]=b изменено время выполнения программы почти в 2 раза. И нет, a[x]=b не был более быстрым.

вот например NVIDIA с указанием что за Программирование их графических процессоров:

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

не только деление на степени 2 быстрее с беззнаковым типом, деление на любые другие значения также быстрее с беззнаковым типом. Если вы посмотрите на таблицы инструкций Agner Fog вы увидите, что неподписанные подразделений имеют аналогичную или лучшую производительность, чем подписанные версии

например, с AMD K7

╔═════════════╤══════════╤═════╤═════════╤═══════════════════════╗
║ Instruction │ Operands │ Ops │ Latency │ Reciprocal throughput ║
╠═════════════╪══════════╪═════╪═════════╪═══════════════════════╣
║ DIV         │ r8/m8    │ 32  │ 24      │ 23                    ║
║ DIV         │ r16/m16  │ 47  │ 24      │ 23                    ║
║ DIV         │ r32/m32  │ 79  │ 40      │ 40                    ║
║ IDIV        │ r8       │ 41  │ 17      │ 17                    ║
║ IDIV        │ r16      │ 56  │ 25      │ 25                    ║
║ IDIV        │ r32      │ 88  │ 41      │ 41                    ║
║ IDIV        │ m8       │ 42  │ 17      │ 17                    ║
║ IDIV        │ m16      │ 57  │ 25      │ 25                    ║
║ IDIV        │ m32      │ 89  │ 41      │ 41                    ║
╚═════════════╧══════════╧═════╧═════════╧═══════════════════════╝

то же самое относится и к Intel Pentium

╔═════════════╤══════════╤══════════════╗
║ Instruction │ Operands │ Clock cycles ║
╠═════════════╪══════════╪══════════════╣
║ DIV         │ r8/m8    │ 17           ║
║ DIV         │ r16/m16  │ 25           ║
║ DIV         │ r32/m32  │ 41           ║
║ IDIV        │ r8/m8    │ 22           ║
║ IDIV        │ r16/m16  │ 30           ║
║ IDIV        │ r32/m32  │ 46           ║
╚═════════════╧══════════╧══════════════╝

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

традиционно int - Это собственный целочисленный формат целевой аппаратной платформы. Любой другой целочисленный тип может привести к снижению производительности.

EDIT:

В современных системах все немного по-другому:

  • int может на самом деле быть 32-разрядной на 64-разрядных системах по причинам совместимости. Я считаю, что это происходит в системах Windows.

  • современные компиляторы могут неявно использовать int при выполнении вычисления для более коротких типов в некоторых случаях.

IIRC, на x86 signed / unsigned не должно иметь никакого значения. С другой стороны, Short/long-это другая история, поскольку объем данных, который должен быть перемещен в/из ОЗУ, больше для longs (другие причины могут включать операции приведения, такие как расширение короткого до длинного).

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

подписанные и беззнаковые целые числа всегда будут работать как одиночные инструкции часов и иметь одинаковую производительность чтения-записи, но в соответствии с Доктор Андрей Александреску unsigned предпочтительнее, чем signed. Причина этого заключается в том, что вы можете поместить в два раза больше чисел в одном и том же количестве битов, потому что вы не тратите бит знака, и вы будете использовать меньше инструкций, проверяющих отрицательные числа, что приводит к увеличению производительности от уменьшенного ПЗУ. По моему опыту с элемент Кабуки VM, который отличает ультра-высокой производительностью скрипт реализация, это редко, что вы на самом деле требуется подписанный номер при работе с памятью. Я провел майские годы, делая арифметику указателя со знаковыми и беззнаковыми числами, и я не нашел никакой пользы для подписанного, когда не нужен знаковый бит.

где знак может быть предпочтительным при использовании сдвига битов для выполнения умножения и деления степеней 2, потому что вы можете выполнить отрицательный полномочия 2-го деления со знаком 2-х дополняют целые числа. Пожалуйста, смотрите некоторые больше видео на YouTube от Андрей для получения дополнительных методов оптимизации. Вы также можете найти хорошую информацию в моей статье о самый быстрый в мире алгоритм преобразования целого числа в строку.

Comments

    Ничего не найдено.