_mm256_shuffle_epi8在此“人生游戏”实施中有何意义?

时间:2019-02-06 10:32:44

标签: c++ intrinsics avx conways-game-of-life

让我的功课使用内在函数实现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);
    }
}
}

以这种方式分配数组的内存

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

找到源代码

1 个答案:

答案 0 :(得分:6)

shuffle_epi8在这里用作并行表查找,具有恒定的第一个操作数和可变的第二个操作数。

Daniel的代码进行了一些计算,为向量中的每个字节生成一个4位整数,然后使用_mm256_shuffle_epi8将这些整数映射到0/1个存活或死亡的新状态。

请注意,shufmask的低通道和高通道是相同的:这两个通道的查找表相同。 (这不是一个交叉的改组,它是从2个16字节表中进行32次并行查找,使用每个元素的低4位。高位将其清零。)


shufmask是变量名称的错误选择。它不是洗牌控制向量。 alivetable可能是更好的选择。


使用[v]pshufb实现16个条目的LUT是一项(众所周知)的技术。这是一种实现大型数组的popcnt的方法,该方法比标量更快,可拆分字节进入低/高半字节并查找4位popcnt结果。请参见Counting 1 bits (population count) on large data using AVX-512 or AVX-2,特别是https://github.com/WojciechMula/sse-popcount/blob/master/popcnt-avx2-lookup.cpp