如何判断16u32‘S的对齐块是否连续(且在增加)?

例如:[100, [100, 99, 3 ...], 102, ..., 115]是. 而[100, 99, 3 ...]并非如此.

我在AVX512F上.这就是我到目前为止所拥有的:

A类,A类:

* predefine DECREASE_U32, a u32x16 of [15,14,13,...0]
* let a = input + DECREASE_32 // wrapping is OK
* compare a to u32x16::splat(first_item(a))
* Return whether all true

替代(算法 B)

* let b = copy of A
* permute the elements of b by one position
* let b = a-b
* Is b all 1's (except for 1st position)

我在Rust中使用packed_simd crate来做这件事,但任何语言/伪代码都可以.(我希望有一个SIMD操作来减go 相邻的项目.

推荐答案

我认为您的第一个 idea 可能最好在一个循环中完成,该循环可以分摊加载向量常量的成本.AVX-512可以有效地做到这一点.

或者使用向量加载,然后用vpbroadcastd单独广播低元素,或者使用向量加载和广播加载.例如vpaddd zmm16, zmm31, [rdi]{1to16}/vpcmpeqd k1, zmm16, [rdi].

嗯,但然后判断所有元素是否为真,我猜可能是kaddw与常量1,并判断低16位是否为0与kortest?或者仅将kmov0xffff进行比较,就像我们在SSE/AVX pmovmskb中所做的那样.我试过了,克朗有一个更好的主意:比较不相等,并判断掩码是否全为零.(即,通过判断每个元素是否不相等来判断它们是否相等.)这允许在掩码本身上使用kortest.我把朗的 idea 应用到我的本质上,这样GCC也可以做得更好.

在C++中:

#include <immintrin.h>

// compare for not-equal, checking the mask for 0
bool check_contig(int *p)
{
    __m512i bcast_first = _mm512_set1_epi32(*p);
    __m512i desired = _mm512_add_epi32(bcast_first, _mm512_setr_epi32(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0));

    __m512i v = _mm512_loadu_si512(p);
    __mmask16 cmp = _mm512_cmpneq_epi32_mask(desired, v);
    return cmp == 0;
}

GCC和Cang的Godbolt-ASM:

# GCC
check_contig(int*):
        vmovdqa32       zmm0, ZMMWORD PTR .LC0[rip]
        vpaddd  zmm0, zmm0, DWORD PTR [rdi]{1to16}
        vpcmpd  k0, zmm0, ZMMWORD PTR [rdi], 4
        kortestw        k0, k0
        sete    al
        vzeroupper
        ret
# clang
check_contig(int*):
        vpbroadcastd    zmm0, dword ptr [rdi]
        vpaddd  zmm0, zmm0, zmmword ptr [rip + .LCPI0_0]
        vpcmpneqd       k0, zmm0, zmmword ptr [rdi]
        kortestw        k0, k0
        sete    al
        vzeroupper
        ret

因此,它们都 Select 加载两次而不是vpbroadcastd zmm1, xmm0次,至少在不在循环中时是这样,因此向量常量也必须从.rodata开始加载.

也许如果我把它写成_mm512_broadcastd_epi32( _mm512_castsi512_si128(v)),他们会更喜欢一个加载,代价是一个额外的洗牌.(当您有512位微处理器在运行时,情况可能会更糟,因此Intel CPU关闭了端口1上的矢量ALU,只留下端口0和5.https://agner.org/optimize/https://uops.info/)


ALGO B-避免非平凡向量常量

也许您的第二种方法也可以高效地使用valignd来旋转向量;它需要的唯一向量常量是全一,它可以更便宜地生成(vpternlogd)而不是加载.

判断比较掩码可能需要kmov到整数,and+cmp才能判断除一位以外的所有位,除非我们可以使用相同的技巧clang进行排列,以便我们实际上希望掩码在我们想要的位置为全零.在这种情况下,test eax, imm32可以判断我们想要的位,而忽略我们不想要的位.

Rust相关问答推荐

如何在Tauri中将变量从后端传递到前端

当第二个`let`依赖于第一个`let()`时,如何在一行中有多个`let()`?

如何指定不同的类型来常量Rust中的泛型参数?

在跨平台应用程序中使用std::OS::Linux和std::OS::Windows

try 创建随机数以常量

使用 serde::from_value 反序列化为泛型类型

为什么`tokio::main`可以直接使用而不需要任何导入?

Const 上下文:从 init 函数创建具有 const 通用长度的数组

如何将 struct 数组放置在另一个 struct 的末尾而不进行内存分段

如何将一个矩阵的列分配给另一个矩阵,纳尔代数?

在 Rust 中,是否可以定义一个需要实现类型的构造函数的对象安全特征?

Rust中的一生语法有什么作用?

为什么 Rust 的临时值有时有参考性有时没有?

为什么要这样编译?

如何在 Rust 中将 Vec> 转换为 Vec>?

以 `static` 为前缀的闭包是什么意思?我什么时候使用它?

仅在运行测试时生成调试输出

为什么我可以同时传递可变和不可变引用?

意外的正则表达式模式匹配

令人困惑的错误消息? (解包运算符)