我有一个4D vector:std::array<float,4>
我想检查它的所有组件是否都在值范围内:0.0f <= X && X < 256.0f
我如何检查是否有任何向量分量在这个范围之外?我只需要一个bool就可以知道整个向量是否通过测试。
我第一次尝试解决这个问题是以下代码:
bool Check_If_Outside_2(std::array<float, 4> vec)
{
bool outside = false;
for (int i = 0; i < 4; i++)
if (vec[i] < VType(0) || vec[i] >= VType(256))
outside = true;
return outside;
}
字符串
这导致了汇编器的输出:
Check_If_Outside_2(std::array<float, 4ul>): # @Check_If_Outside_2(std::array<float, 4ul>)
vxorps xmm2, xmm2, xmm2
vucomiss xmm0, xmm2
setb al
vmovss xmm3, dword ptr [rip + .LCPI7_0] # xmm3 = mem[0],zero,zero,zero
vucomiss xmm0, xmm3
setae cl
or cl, al
vmovshdup xmm0, xmm0 # xmm0 = xmm0[1,1,3,3]
vucomiss xmm0, xmm2
setb dl
vucomiss xmm0, xmm3
setae al
or al, dl
or al, cl
vxorps xmm0, xmm0, xmm0
vcmpltps xmm0, xmm1, xmm0
vpermilps xmm0, xmm0, 212 # xmm0 = xmm0[0,1,1,3]
vmovaps xmm2, xmmword ptr [rip + .LCPI7_1] # xmm2 = <2.56E+2,2.56E+2,u,u>
vcmpleps xmm1, xmm2, xmm1
vpermilps xmm1, xmm1, 212 # xmm1 = xmm1[0,1,1,3]
vorps xmm0, xmm0, xmm1
vpsllq xmm0, xmm0, 63
vmovmskpd ecx, xmm0
or al, cl
shr cl
or al, cl
and al, 1
ret
型
然后我尝试了下面的优化版本,它使用了负整数值填充寄存器中的最高位的思想。所以我简单地将浮点数转换为整数,并测试高位是否有任何非零位。如果有非零位,则向量的分量要么是负数,要么高于法律的最大范围。
template<typename T, unsigned long N>
static inline std::array<int32_t, N> To_Int_Vec(const std::array<T, N>& x)
{
std::array<int32_t, N> int_vec;
for (int i = 0; i < N; ++i)
int_vec[i] = floor(x[i]);
return int_vec;
}
bool Check_If_Outside(std::array<float, 4> vec)
{
constexpr int32_t neg_mask = ~255;
auto vec_int = To_Int_Vec(vec);
bool outside = false;
for (int i = 0; i < 4; i++)
if (vec_int[i] & neg_mask)
outside = true;
return outside;
}
型
它给出了以下汇编器输出:
Check_If_Outside(std::array<float, 4ul>): # @Check_If_Outside(std::array<float, 4ul>)
vshufps xmm0, xmm0, xmm1, 65 # xmm0 = xmm0[1,0],xmm1[0,1]
vroundps xmm0, xmm0, 9
vcvttps2dq xmm0, xmm0
vpshufd xmm1, xmm0, 78 # xmm1 = xmm0[2,3,0,1]
vpor xmm0, xmm0, xmm1
vpshufd xmm1, xmm0, 229 # xmm1 = xmm0[1,1,2,3]
vpor xmm0, xmm0, xmm1
vmovd eax, xmm0
cmp eax, 255
seta al
ret
型
我认为这种测试仍然可以使用英特尔SIMD指令进行进一步优化,但我不确定如何做到这一点。是否有可能在纯C++中处理得更好,或者需要内部函数,甚至内联汇编程序?或者甚至有可能进一步优化它?
要查看优化后的输出,我使用x86-64 clang 11.0.0,编译器标志为:-O3 -mtune=skylake -ffast-math -funsafe-math-optimizations -fno-math-errno -msse4.1 -mavx -mfma 4
编辑:问题在你的帮助下解决了!谢谢!
2条答案
按热度按时间gajydyqb1#
直接使用SIMD似乎比原始代码更简单:
字符串
结果代码(至少是我编译它的方式):
型
因此,我们跳过了整数的转换,而不是进行水平或,我们依赖于
vmovmskps
。另一个想法,基于通过原始位模式比较浮点数,
型
无论哪种方式,我都公然忽略了负零和NaN的存在,但
-ffast-math
已经忽略了NaN。对于Skylake,如果我假设输入来自内存(而不是将它们从寄存器中混洗在一起-以打破任何偶然的依赖性),则会得到uiCA结果:
在所有情况下,这只是一些粗略的指示,这里的代码是单独分析的,而不是在任何真实的上下文中,但是吞吐量是依赖于上下文的(其影响例如执行端口压力)。此外,
setcc
在大多数真实的用法中应该消失(其中此函数内联到调用者中,条件立即分支)。(或者从内存中加载输入,我用它来做uiCA分析)将被任何实际产生输入的东西所取代。5m1hhzi42#
如果您接受
-0.0f
被视为外部,则可以利用仅为abs(x)>=2.0f
(或NaNs)设置指数的最高位,并为负输入(包括(-0.0f
))设置符号位。因此,在将向量缩放
1.f/128.f
之后,您可以检查是否有任何元素具有最高位集,这可以通过ptest
针对具有高位集的掩码来完成。字符串