I'm working on some image processing code that can generate pixel values outside of the normal range of 0 to 255, and I'd like to clamp them back into the valid range. I know that there are saturating SIMD instructions that make this a moot point, but I'm trying to stay within standard C++ code for the moment.
The fastest I've been able to do on my Athlon II is the following:
inline
BYTE Clamp(int n)
{
n &= -(n >= 0);
return n | ((255 - n) >> 31);
}
This compiles down into the following assembly with MSVC 6.0:
setns dl
neg edx
and eax, edx
mov edx, 255
sub edx, eax
sar edx, 31
or dl, al
Is there any improvement possible?
Conclusion 2011-12-05:
I tried all of the suggestions again with VS 2010 Express. The generated code didn't change much, but the register assignments did which affected the overall results. A slight modification of the straightforward implementation suggested by Ira Baxter came up the winner.
inline
BYTE Clamp(int n)
{
n = n>255 ? 255 : n;
return n<0 ? 0 : n;
}
cmp ecx, 255
jle SHORT $LN8
mov ecx, 255
$LN8:
test ecx, ecx
sets bl
dec bl
and bl, cl
I learned a valuable lesson with this. I started with an assumption that bit-twiddling would beat anything that included a branch; I hadn't really tried any code that included an if statement or ternary operator. That was a mistake, as I hadn't counted on the power of the branch prediction built into a modern CPU. A ternary solution turned out to be the fastest, especially when the compiler substituted its own bit-twiddling code for one of the cases. The overall timing for this function within my benchmark algorithm went from 0.24 seconds to 0.19. This is very close to the 0.18 seconds that resulted when I removed the clamp entirely.
