“奇数大小的对齐向量上的“安全” SIMD算法?

问题描述 投票:5回答:2

比方说,我有一些16字节对齐的结构,只包装3xFloat32数组:

#[repr(C, align(16))]
pub struct Vector(pub [f32; 3]);

现在,我想将其划分为两个实例,例如:

use core::arch::x86_64;

let a = Vector([1f32, 2f32, 3f32]);
let b = Vector([4f32, 5f32, 6f32]);
let mut sum = Vector([0f32, 0f32, 0ff32]);

unsafe {
    let a1 = x86_64::_mm_load_ps(a.0.as_ptr());
    let b1 = x86_64::_mm_load_ps(b.0.as_ptr());
    let sum1 = x86_64::_mm_div_ps(a1, b1);
    x86_64::_mm_store_ps(sum.0.as_mut_ptr(), sum);
}

它可以进行除法,但是存在一个问题:第4个元素包含垃圾,除其他外,这可能表示NaN。并且,如果未屏蔽某些例外标志,则将触发SIGFPE。我想以某种方式避免这种情况,而不会完全沉默信号。即我或者只想在第4对元素上使其静音,或者在其中添加一些合理的值。最好,最快的方法是什么?还是总体上有更好的方法?

rust floating-point sse simd floating-point-exceptions
2个回答
3
投票

[通常,没有人会掩盖FP异常,否则您需要改组,例如复制元素之一,因此顶部元素与其他元素之一进行相同的划分。或还有其他一些已知的安全物品。

也许,如果您可以假设该元素中的分红不是NaN,那么也许只需要对除数进行改组就可以摆脱。

使用AVX512,您可以使用零掩码抑制元素的异常,但是直到那时为止,还没有这样的功能。此外,AVX512还允许您覆盖舍入模式+不遮盖所有异常(SAE),因此您可以使最接近偶数显式获得SAE。但这会抑制all元素的异常。


严重,请勿启用FP例外。如果异常的数量是明显的副作用,编译器几乎/不知道如何以安全的方式进行优化。例如GCC的-ftrapping-math默认为打开状态,但已损坏。

我不会认为LLVM会更好;默认的严格FP可能仍会进行优化,使一个SIGFPE的源将提高2或4。也许甚至将其提高0的源也将提高1,或者反之亦然,例如GCC破损且几乎无用的默认设置。] >

但是,如果您希望永远都没有某种异常,启用FP异常对于调试可能会很有用。但是您可以通过忽略具有该源地址的SIMD指令来处理偶尔出现的误报。


如果在性能和异常正确性之间进行权衡,则大多数库用户宁愿使性能最大化。

即使清除然后再用fenv填充物检查粘滞的FP掩蔽标志,也很少,并且需要在受控的情况下使用。我对库函数调用没有任何期望,尤其是没有使用任何SIMD的期望。


在垃圾元素中避免subnormals

如果MXCSR没有设置FTZ和DAZ,则您可以从次常态(也称为非常态)中放慢速度。 (即正常情况,除非您使用-ffast-math的Rust等效符号进行编译。)对于具有SSE / AVX指令的典型x86硬件,

产生NaN或+ -Inf不需要花费额外的时间

。 (有趣的事实:NaN也很慢,即使在现代硬件上也具有x87数学功能)。因此,例如,在进行数学运算之前,以_mm_or_ps结果为cmpps是安全的,可以在向量的某些元素中创建NAN。或使用_mm_and_ps在除数之前在除数中创建一些零。

但是要注意填充中的垃圾是什么,因为它可能导致虚假的次正常状态。

0.0和NaN(全都是)通常都是安全的。

通常避免在SIMD中使用水平填充。 SIMD vec!=几何vec。

通常仅使用SIMD向量的4个元素中的3个是个坏主意,因为这通常意味着您使用的是单个SIMD向量来保存单个几何向量,而不是3个向量为4的向量x坐标,4个y坐标和4个z坐标。

随机播放/水平填充主要花费额外的指令(内存中已存储的标量的广播负载除外),但是如果您以这种方式使用SIMD,通常会需要大量随机播放。在某些情况下,您无法对一系列事物进行矢量化处理,但仍可以通过SIMD加快速度。


3
投票

[像在C语言中一样,在Rust中,sizeof始终是alignof的倍数:这是必须的,因为sizeof用作数组中的stride

© www.soinside.com 2019 - 2024. All rights reserved.