我正在优化瓶颈代码:
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。
根据this page,ARM中有一条饱和指令。 x86 / x64中有类似的东西吗?
答案 0 :(得分:5)
我完全不相信尝试消除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 ++,因此上面给出的是大多数当前x86处理器编译器(gcc,icc,clang,msvc)中使用的编译器内部函数的名称。如果您直接编写汇编语言,则说明分别为vpaddsw和vpsubsw。
如果您可以依靠一个真正最新的处理器(一个支持AVX 512指令的处理器),则可以使用它们同时对32个16位值的向量进行操作。
答案 1 :(得分:4)
您确定可以在此击败编译器吗?
这里的x64零售启用了最大尺寸优化。 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