Насыщенный короткий (int16) в C++
Я оптимизирую код узкого места:
int sum = ........
sum = (sum >> _bitShift);
if (sum > 32000)
sum = 32000; //if we get an overflow, saturate output
else if (sum < -32000)
sum = -32000; //if we get an underflow, saturate output
short result = static_cast<short>(sum);
Я хотел бы написать условие насыщения как одно условие "if" или даже лучше без условия "if", чтобы сделать этот код быстрее. Я не нуждаюсь в насыщении точно в значении 32000, любое подобное значение как 32768 приемлемо.
Согласно этой странице, в ARM есть инструкция по насыщению. Есть ли что-нибудь подобное в x86/x64?
2 ответа
Вы уверены, что можете победить компилятор в этом?
Вот x64 retail с включенной оптимизацией максимального размера. Visual Studio v15.7.5.
ecx содержит начальное значение в начале этого блока. Eax заполняется насыщенным значением, когда это сделано.
return (x > 32767) ? 32767 : ((x < -32768) ? -32768 : x);
mov edx,0FFFF8000h
movzx eax,cx
cmp ecx,edx
cmovl eax,edx
mov edx,7FFFh
cmp ecx,edx
movzx eax,ax
cmovg eax,edx
Я совсем не уверен, что попытка устранить if
заявление (я), вероятно, принесет какую-то реальную пользу. Быстрая проверка показывает, что с учетом этого кода:
int clamp(int x) {
if (x < -32768)
x = -32768;
else if (x > 32767)
x = 32767;
return x;
}
... и gcc, и Clang дают такие результаты без веток:
clamp(int):
cmp edi, 32767
mov eax, 32767
cmovg edi, eax
mov eax, -32768
cmp edi, -32768
cmovge eax, edi
ret
Вы можете сделать что-то вроде x = std::min(std::max(x, -32768), 32767);
, но при этом получается та же последовательность, и источник кажется менее читаемым, по крайней мере, мне.
Вы можете добиться значительно большего, чем это, если будете использовать векторные инструкции Intel, но, вероятно, только в том случае, если вы готовы приложить к этому немало усилий - в частности, вам, вероятно, придется работать с целым (небольшим) вектором. ценностей одновременно, чтобы достичь многого таким образом. Если вы идете по этому пути, вы, как правило, хотите использовать несколько иной подход к задаче, чем вы, кажется, делаете это сейчас. Прямо сейчас вы, очевидно, зависите от int
будучи 32-битным типом, вы выполняете арифметику с 32-битным типом, а затем обрезаете его до (насыщенного) 16-битного значения.
С чем-то вроде AVX вы, как правило, захотите использовать такую инструкцию, как _mm256_adds_epi16
взять вектор из 16 значений (по 16 бит на штуку) и выполнить насыщающее сложение для всех них одновременно (или, аналогично, _mm256_subs_epi16
делать насыщающее вычитание).
Поскольку вы пишете на C++, я привел выше имена встроенных функций компилятора, используемых в большинстве современных компиляторов (gcc, icc, clang, msvc) для процессоров x86. Если вы пишете на ассемблере напрямую, инструкциями будут vpaddsw и vpsubsw соответственно.
Если вы можете рассчитывать на действительно текущий процессор (который поддерживает инструкции AVX 512), вы можете использовать их вместо этого для одновременной работы с вектором из 32 16-битных значений.