_mm256_shuffle_epi8 在此 Game of Life 实施中有何意义?
How does the _mm256_shuffle_epi8 make sense in this Game of Life implementation?
为使用内部函数实现 Conway's Game of Life 做作业找到了工作代码,但无法理解其中的主要部分。
此实现首先为每个销售计算存活邻居的数量并将结果存储在数组 counts
中,因此销售(世界)数组为 states
。我真的不知道 newstate
是如何在这里生成的。我了解左移是如何工作的,按位或是如何工作的,但我不明白为什么要这样使用它们,为什么 shufmask
是这样的以及随机播放是如何工作的。如果数组元素的类型是 uint8_t,也无法理解为什么使用 _mm256_slli_epi16。所以我的问题是关于这个字符串
__m256i newstate = _mm256_shuffle_epi8(shufmask, _mm256_or_si256(c, _mm256_slli_epi16(oldstate, 3)));
你能为我解释一下吗,笨蛋,如果可能最详细的话,它是如何工作的。
void gameoflife8vec(uint8_t *counts, uint8_t *states, size_t width, size_t height) {
assert(width % (sizeof(__m256i)) == 0);
size_t awidth = width + 2;
computecounts8vec(counts, states, width, height);
__m256i shufmask =
_mm256_set_epi8(
0, 0, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 1, 0, 0
);
for (size_t i = 0; i < height; i++) {
for (size_t j = 0; j < width; j += sizeof(__m256i)) {
__m256i c = _mm256_lddqu_si256(
(const __m256i *)(counts + (i + 1) * awidth + j + 1));
c = _mm256_subs_epu8(
c, _mm256_set1_epi8(
1)); // max was 8 = 0b1000, make it 7, 1 becomes 0, 0 remains 0
__m256i oldstate = _mm256_lddqu_si256(
(const __m256i *)(states + (i + 1) * awidth + j + 1));
__m256i newstate = _mm256_shuffle_epi8(
shufmask, _mm256_or_si256(c, _mm256_slli_epi16(oldstate, 3)));
_mm256_storeu_si256((__m256i *)(states + (i + 1) * awidth + (j + 1)),
newstate);
}
}
}
array的内存是这样分配的
uint8_t *states = (uint8_t *)malloc((N + 2) * (N + 2) * sizeof(uint8_t));
uint8_t *counts = (uint8_t *)malloc((N + 2) * (N + 2) * sizeof(uint8_t));
也可以在这里找到源代码https://github.com/lemire/SIMDgameoflife
shuffle_epi8
在这里用作并行 table-查找,具有常量第一个操作数和可变的第二个操作数。
Daniel 的代码做了一些计算,为向量中的每个字节生成一个 4 位整数,然后使用 _mm256_shuffle_epi8
将这些整数映射到 0 / 1 个生或死的新状态。
请注意 shufmask
的低车道和高车道是相同的:两条车道的查找 table 相同。 (这不是交叉洗牌,它是来自 2x 16 字节 tables 的 32 次并行查找,使用每个元素中的低 4 位。高位将其清零。)参见 intrinsic and asm instruction 文档。
shufmask
是一个糟糕的变量名选择。它是 而不是 随机播放控制向量。 alivetable
可能是更好的选择。
使用 [v]pshufb
实现 16 项 LUT 是一种(相当)众所周知的技术。 例如,这是实现大型 popcnt 的一种方法比标量更快的数组,将字节拆分为 low/high 半字节并查找 4 位 popcnt 结果。参见 , specifically https://github.com/WojciechMula/sse-popcount/blob/master/popcnt-avx2-lookup.cpp
为使用内部函数实现 Conway's Game of Life 做作业找到了工作代码,但无法理解其中的主要部分。
此实现首先为每个销售计算存活邻居的数量并将结果存储在数组 counts
中,因此销售(世界)数组为 states
。我真的不知道 newstate
是如何在这里生成的。我了解左移是如何工作的,按位或是如何工作的,但我不明白为什么要这样使用它们,为什么 shufmask
是这样的以及随机播放是如何工作的。如果数组元素的类型是 uint8_t,也无法理解为什么使用 _mm256_slli_epi16。所以我的问题是关于这个字符串
__m256i newstate = _mm256_shuffle_epi8(shufmask, _mm256_or_si256(c, _mm256_slli_epi16(oldstate, 3)));
你能为我解释一下吗,笨蛋,如果可能最详细的话,它是如何工作的。
void gameoflife8vec(uint8_t *counts, uint8_t *states, size_t width, size_t height) {
assert(width % (sizeof(__m256i)) == 0);
size_t awidth = width + 2;
computecounts8vec(counts, states, width, height);
__m256i shufmask =
_mm256_set_epi8(
0, 0, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 1, 0, 0
);
for (size_t i = 0; i < height; i++) {
for (size_t j = 0; j < width; j += sizeof(__m256i)) {
__m256i c = _mm256_lddqu_si256(
(const __m256i *)(counts + (i + 1) * awidth + j + 1));
c = _mm256_subs_epu8(
c, _mm256_set1_epi8(
1)); // max was 8 = 0b1000, make it 7, 1 becomes 0, 0 remains 0
__m256i oldstate = _mm256_lddqu_si256(
(const __m256i *)(states + (i + 1) * awidth + j + 1));
__m256i newstate = _mm256_shuffle_epi8(
shufmask, _mm256_or_si256(c, _mm256_slli_epi16(oldstate, 3)));
_mm256_storeu_si256((__m256i *)(states + (i + 1) * awidth + (j + 1)),
newstate);
}
}
}
array的内存是这样分配的
uint8_t *states = (uint8_t *)malloc((N + 2) * (N + 2) * sizeof(uint8_t));
uint8_t *counts = (uint8_t *)malloc((N + 2) * (N + 2) * sizeof(uint8_t));
也可以在这里找到源代码https://github.com/lemire/SIMDgameoflife
shuffle_epi8
在这里用作并行 table-查找,具有常量第一个操作数和可变的第二个操作数。
Daniel 的代码做了一些计算,为向量中的每个字节生成一个 4 位整数,然后使用 _mm256_shuffle_epi8
将这些整数映射到 0 / 1 个生或死的新状态。
请注意 shufmask
的低车道和高车道是相同的:两条车道的查找 table 相同。 (这不是交叉洗牌,它是来自 2x 16 字节 tables 的 32 次并行查找,使用每个元素中的低 4 位。高位将其清零。)参见 intrinsic and asm instruction 文档。
shufmask
是一个糟糕的变量名选择。它是 而不是 随机播放控制向量。 alivetable
可能是更好的选择。
使用 [v]pshufb
实现 16 项 LUT 是一种(相当)众所周知的技术。 例如,这是实现大型 popcnt 的一种方法比标量更快的数组,将字节拆分为 low/high 半字节并查找 4 位 popcnt 结果。参见