Given input array of Float32
(float
) with numElements
how could one efficiently convert it into array of UINT8
(unsigned char
)?
The tricky part here is to apply Unsigned Saturation in the conversion
For instance, here is a vanilla code for it (Pay attention there is a scaling operation):
void ConvertToUint8(unsigned char* mO, float* mI, int numElements, float scalingFctr)
{
int ii;
for (ii = 0; ii < numElements; ii++) {
mO[ii] = (unsigned char)(fmin(fmax(mI[ii] * scalingFctr, 0.0), 255.0));
}
}
Where mO
is the output array.
I'm looking for a way to optimize (Performance wise) this code on AVX2 enabled CPU's. Any idea, Intrinsics included, is welcome.
Pay attention the above code apply unsigned saturation manually (Is there a function for unsigned saturation based casting in C
?). I think in practice SSE
and AVX have it built in (See _mm_packus_epi16()
for SSE
).
The objective is to yield faster code than the vanilla example as in Compiler Explorer - ConvertToUint8.
For simplicity one could assume the arrays are aligned.
mI[ii] * scalingFctr
can have any legitFloat32
value (But notNAN
orINF
). I think inSSE
the intrinsic_mm_packus_epi16
does the trick \$\endgroup\$vcvttps2dq
producesINT_MIN
for out of range floats (including large positive), and then a pack-with-unsigned-saturation still interprets that asINT_MIN
so it would result in zero, but maybe we wanted 0xFF. So it gets trickier \$\endgroup\$