使用“__m256i”中的值高效访问数组 - SIMD
Using values from `__m256i` to access an array efficiently - SIMD
例如,假设我有 2 个变量 __m256i
,分别称为 rows
和 cols
,其中的值是:
rows: 0, 2, 7, 5, 7, 2, 3, 0
cols: 1, 2, 7, 5, 7, 2, 2, 6
现在,这些值代表 8 个点的 x
和 y
位置,因此,在这种情况下,我将得到这些点:
p0: [0, 1], p1: [2, 2], p2: [7, 7], p3: [5, 5]
p4: [7, 7], p5: [2, 2], p6: [3, 2], p7: [0, 6]
我还有一个名为 lut
的数组,它将具有 int
类型的值:
lut: [0, 1, 2, 3, ..., 60, 61, 62, 63]
我想做的是使用来自 rows
和 cols
变量的这些位置值,用它访问 lut
数组并创建一个新的 __m256i
lut
个访问值的值。
我知道如何做到这一点的方法是将 rows
和 cols
值存储在两个大小为 8 的 int
数组中,然后从 [=19] 中读取值=] 一次一个数组,然后使用 _mm256_set_epi32()
创建新的 _m256i
值。
这行得通,但在我看来效率很低。所以我的问题是是否有某种方法可以更快地做到这一点。
请注意,这些值仅用于更具体的示例,lut
不需要具有有序值或大小 64。
谢谢!
您可以使用 avx2 gather instruction 构建解决方案,就像这样
// index = (rows << 3) + cols;
const __m256i index = _mm256_add_epi32( _mm256_slli_epi32(rows, 3), cols);
// result = lut[index];
const __m256i result = _mm256_i32gather_epi32(lut, index, 4);
请注意,在当前的 CPU 上,收集指令具有相当大的延迟,因此除非您可以在实际使用之前插入一些指令 result
,否则这可能不值得使用。
解释 4 的因数:
中的 scale
因数
__m256i _mm256_i32gather_epi32 (int const* base_addr, __m256i vindex, const int scale)
被认为是实际的字节偏移量,即每个索引的返回值为:
*(const int*)((const char*) base_addr + scale*index)
我不知道这种行为是否有很多用例(也许这是为了可以访问具有 1 字节或 2 字节条目的 LUT(之后需要一些屏蔽))。也许这只是被允许的,因为缩放 4 是可能的,而缩放 1/4 或 1/2 是不可能的(如果有人真的需要的话)。
例如,假设我有 2 个变量 __m256i
,分别称为 rows
和 cols
,其中的值是:
rows: 0, 2, 7, 5, 7, 2, 3, 0
cols: 1, 2, 7, 5, 7, 2, 2, 6
现在,这些值代表 8 个点的 x
和 y
位置,因此,在这种情况下,我将得到这些点:
p0: [0, 1], p1: [2, 2], p2: [7, 7], p3: [5, 5]
p4: [7, 7], p5: [2, 2], p6: [3, 2], p7: [0, 6]
我还有一个名为 lut
的数组,它将具有 int
类型的值:
lut: [0, 1, 2, 3, ..., 60, 61, 62, 63]
我想做的是使用来自 rows
和 cols
变量的这些位置值,用它访问 lut
数组并创建一个新的 __m256i
lut
个访问值的值。
我知道如何做到这一点的方法是将 rows
和 cols
值存储在两个大小为 8 的 int
数组中,然后从 [=19] 中读取值=] 一次一个数组,然后使用 _mm256_set_epi32()
创建新的 _m256i
值。
这行得通,但在我看来效率很低。所以我的问题是是否有某种方法可以更快地做到这一点。
请注意,这些值仅用于更具体的示例,lut
不需要具有有序值或大小 64。
谢谢!
您可以使用 avx2 gather instruction 构建解决方案,就像这样
// index = (rows << 3) + cols;
const __m256i index = _mm256_add_epi32( _mm256_slli_epi32(rows, 3), cols);
// result = lut[index];
const __m256i result = _mm256_i32gather_epi32(lut, index, 4);
请注意,在当前的 CPU 上,收集指令具有相当大的延迟,因此除非您可以在实际使用之前插入一些指令 result
,否则这可能不值得使用。
解释 4 的因数:
中的scale
因数
__m256i _mm256_i32gather_epi32 (int const* base_addr, __m256i vindex, const int scale)
被认为是实际的字节偏移量,即每个索引的返回值为:
*(const int*)((const char*) base_addr + scale*index)
我不知道这种行为是否有很多用例(也许这是为了可以访问具有 1 字节或 2 字节条目的 LUT(之后需要一些屏蔽))。也许这只是被允许的,因为缩放 4 是可能的,而缩放 1/4 或 1/2 是不可能的(如果有人真的需要的话)。