Тип операнда RHS при побитовом сдвиге беззнаковых типов

Я хочу сдвинуть тип без знака (например, size_t) на неотрицательное число (двоичных) цифр/мест влево/вправо, например.

size_t x;
x << non_const_expr

в предположении, что значение non_const_expr соответствует (unsigned) int, а never вызывает неопределенное поведение (его значение неотрицательно и не превышает количество цифр в x). (Обратите внимание, что значение неизвестно во время компиляции.)

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

  • unsigned int кажется наиболее читаемым/интуитивно понятным)
  • int (насколько мне известно, int является родным (и самым быстрым?) типом на платформе, тогда как unsigned может быть реализован менее эффективно`)
  • тип LHS в операции сдвига (т.е. size_t в данном случае)

Имеет ли это вообще значение? Если да, то какой из них обычно дает самый быстрый код?


person eold    schedule 01.04.2014    source источник
comment
Вы всегда можете использовать std::uint_fast32_t, если вас беспокоит производительность типов unsigned int, и это должно дать вам быстрый результат.   -  person Jay    schedule 01.04.2014
comment
Я не думаю, что это имеет значение. Есть ли у вас доказательства того, что эта инструкция смены является узким местом в производительности?   -  person Filipe Gonçalves    schedule 01.04.2014
comment
Уверен, что, поскольку сдвиг определен только, скажем, от 0 до 31 (учитывая 32-битное x), а все остальное является UB, большинство (слова ласки) будут просто использовать 5 LS-битов non_const_expr независимо от его типа. .   -  person chux - Reinstate Monica    schedule 01.04.2014
comment
Целочисленный тип LHS, который соответствует собственному размеру ЦП, определенно не медленнее, чем что-либо еще. т.е. Самый быстрый, хотя другие типы могут быть такими же быстрыми.   -  person chux - Reinstate Monica    schedule 01.04.2014
comment
Примечание. Многие процессоры по своей конструкции сдвигают не на счет, а на X младших разрядов счета. BITD, 8088 будет сдвигать 16-битное int на основе 8 младших бит счета. Каждая смена занимала 1 цикл. Так что, если по дурацкому счету было 255, эта инструкция займет долго время. Таким образом, создается огромное время задержки в худшем случае для прерываний. Intel разумно использовала 4 (или 5) младших разрядов для последующих процессоров.   -  person chux - Reinstate Monica    schedule 01.04.2014
comment
Мои основные опасения заключаются в том, что если я выберу неподходящий тип, компилятор может сгенерировать дополнительные проверки/инструкции, которые в противном случае не были бы сгенерированы. Например, компилятор может быть слишком умным и сделать отрицательные сдвиги четко определенными, вставив дополнительный ассемблерный код, так что создание RHS без знака, возможно, устранит эту попытку.   -  person eold    schedule 01.04.2014
comment
Целое число (со знаком) допускает отрицательные суммы сдвига, что является неопределенным поведением на большинстве платформ.   -  person Thomas Matthews    schedule 01.04.2014
comment
@OP, является ли non_const_expr одним и тем же значением в разделе, критичном для производительности? Если это так, вызовите код, использующий фиксированный сдвиг. if (non_const_expr==5) for(i=n; i>0; i--) { foo1(); x << 5; foo2(); }. Каким-то образом, в конце концов, я просто не вижу, чтобы оптимизация этого сдвига существенно повлияла на скорость.   -  person chux - Reinstate Monica    schedule 01.04.2014


Ответы (1)


Оптимальная производительность сдвига влево или вправо - это когда RHS является положительной числовой константой.

В противном случае это зависит от процессора.

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

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

Если вы используете переменную для RHS, вы смотрите на минимальные операции:

  1. Загрузите RHS из памяти.
  2. Сдвиньте регистр, содержащий значение LHS, на значение RHS.
  3. Сохраните результат в памяти.

Правда будет в листинге на ассемблере.

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

person Thomas Matthews    schedule 01.04.2014