【问题标题】:AVX/SSE version of xorshift128+AVX/SSE 版本的 xorshift128+
【发布时间】:2014-06-02 19:36:23
【问题描述】:

我正在努力制作尽可能快的高质量 RNG。阅读 http://xorshift.di.unimi.it/ 后,xorshift128+ 似乎是一个不错的选择。 C代码是

#include <stdint.h>
uint64_t s[ 2 ];

uint64_t next(void) { 
    uint64_t s1 = s[ 0 ];
    const uint64_t s0 = s[ 1 ];
    s[ 0 ] = s0;
    s1 ^= s1 << 23; // a
    return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0; // b, c
}

遗憾的是,我不是 SSE/AVX 专家,但我的 CPU 支持 SSE4.1 / SSE4.2 / AVX / F16C / FMA3 / XOP 指令。您如何使用这些来加速此代码(假设您想要生成数十亿个这样的随机数)以及在实践中这种加速的预期限制是什么?

【问题讨论】:

    标签: c performance sse avx


    【解决方案1】:

    对于其他可能遇到这个问题的人,我认为这段 C++ 代码正确地实现了 4 个并行运行的 xorshift128plus 生成器,使用 AVX2:

    __m256i xorshift128plus_avx2(__m256i &state0, __m256i &state1)
    {
        __m256i s1 = state0;
        const __m256i s0 = state1;
        state0 = s0;
        s1 = _mm256_xor_si256(s1, _mm256_slli_epi64(s1, 23));
        state1 = _mm256_xor_si256(_mm256_xor_si256(_mm256_xor_si256(s1, s0),
                                                   _mm256_srli_epi64(s1, 18)),
                                  _mm256_srli_epi64(s0, 5));
        return _mm256_add_epi64(state1, s0);
    }
    

    我使用的标量实现是:

    u64 xorshift128plus(u64 &state0, u64 &state1)
    {
        u64 s1 = state0;
        const u64 s0 = state1;
        state0 = s0;
        s1 ^= s1 << 23;                              // a
        state1 = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c
        return state1 + s0;
    }
    

    xorshiftplus paper 中的相同。请注意,原始问题中的右移常数与论文中的不对应。

    【讨论】:

    • 您可以使用不同的操作顺序来获得更多指令级并行性,以防编译器不知道_mm256_xor 是关联的。 s1^s0 可以与班次并行运行。哎呀 NVM,我读错了, s1^s0 实际上在最里面的嵌套中。我发现在单个表达式中大量嵌套内在函数非常不可读,并且发现使用描述性名称命名临时变量以提高可读性会更好。所以gcc does make good code here.
    • 确实,我指望编译器来解决这个问题。顺便说一句,该网站是一个宝石!已收藏。
    • 问题中的常量是维基百科上的内容,并且是论文中一个表格中的第一个条目。 IDK 哪些常量是最佳选择,但xoroshiro.di.unimi.it/xorshift128plus.c 中当前发布的常量与此匹配,而不是 Wikipedia。
    【解决方案2】:

    XorShift 确实是一个不错的选择。它是如此的好,如此之快,并且需要如此之少的状态,以至于我惊讶地看到如此之少的采用。它应该是所有平台上的标准生成器。我在 8 年前自己实现了它,即便如此它也可以生成 800MB/s 的随机字节。

    您不能使用向量指令来加速生成单个随机数。这几条指令中的指令级并行性太少了。

    但您可以轻松加快生成 N 个数字的速度,其中 N 是目标指令集的向量大小。只需并行运行 N 个生成器。保持 N 个生成器的状态,同时生成 N 个数字。

    如果客户端代码一次需要一个数字,您可以保留 N 个(或更多)数字的缓冲区。如果缓冲区为空,则使用向量指令填充它。如果缓冲区不为空,则返回下一个数字。

    【讨论】:

    • 我认为 xorshift 本身并不好。然而,这个“加号”版本是我感兴趣的。
    • @user2179021 它通过了除一项 AFAIK 的顽固测试之外的所有测试。所以还不错。无论如何,我回答你的问题了吗?
    • 我真的在寻找一些 AVX/SSE 代码或关于我的代码可能会加速多少的特定内容。
    • 您可以使用 SSE 并行运行两个这样的生成器 - 当然,您需要以不同的方式播种它们,否则它们只会为每个生成器生成相同的值。右移使用_mm_srli_epi64,异或使用_mm_xor_si128,加法使用_mm_add_epi64
    • 不幸的是,您在 AVX 中没有等效的操作 - 您需要在 Haswell 及更高版本上可用的 AVX2 - 这将使您能够并行运行 4 个 64 位 RNG,而不是 2 个。
    猜你喜欢
    • 2020-01-12
    • 1970-01-01
    • 1970-01-01
    • 2013-12-28
    • 2015-10-08
    • 2013-03-21
    • 2018-01-04
    • 2015-03-11
    相关资源
    最近更新 更多