Если вы не возражаете против небольшого встроенного ассемблера x86 (синтаксис GNU C), вы можете воспользоваться предложением supercat использовать rotate-with-carry после добавления, чтобы поместить старшие 32 бита полного 33-битного результата в регистр.
Конечно, вы обычно должны возражать против использования inline-asm, потому что это нарушает некоторые оптимизации (https://gcc.gnu.org/wiki/DontUseInlineAsm). Но вот все равно:
// works for 64-bit long as well on x86-64, and doesn't depend on calling convention
unsigned average(unsigned x, unsigned y)
{
unsigned result;
asm("add %[x], %[res]\n\t"
"rcr %[res]"
: [res] "=r" (result) // output
: [y] "%0"(y), // input: in the same reg as results output. Commutative with next operand
[x] "rme"(x) // input: reg, mem, or immediate
: // no clobbers. ("cc" is implicit on x86)
);
return result;
}
%
модификатор, сообщающий компилятору, что аргументы являются коммутативными, не на самом деле помогает улучшить asm в случае, когда я пытался, вызывая функцию с y, являющимся константой или указателем-deref (операнд памяти). Вероятно, использование ограничения соответствия для выходного операнда побеждает это, поскольку вы не можете использовать его с операндами чтения-записи.
RCR-by-one не слишком медленный, но на Skylake это все еще 3 мкп с задержкой в 2 цикла. Это отлично работает на процессорах AMD, где RCR имеет задержку в один цикл. (Источник: таблицы инструкций Agner Fog, см. также x86 пометить вики для ссылок на производительность x86). Это все еще лучше, чем версия @sellibitze, но хуже, чем версия @Sheldon, зависящая от порядка. (См. код на Godbolt)
Но помните, что встроенный ассемблер отменяет такие оптимизации, как распространение констант, поэтому в этом случае любая версия на чистом C++ будет лучше.
И правильный ответ...
person
fredoverflow
schedule
28.09.2010