65.9K
CodeProject 正在变化。 阅读更多。
Home

快速 SSE 伪随机数生成器

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (4投票s)

2024年2月19日

CPOL

2分钟阅读

viewsIcon

6837

能够比标准库更快生成伪随机数的东西

引言

这是在测试数据生成过程中获得的知识的一种保存点。使用该代码的生成器比标准 C 库的随机函数更快,几乎在任何地方都能工作,并且不依赖于其他东西。

背景

这段代码是在测试数据生成器实现期间创建的。标准库太慢,而且没有依赖其他库的意愿。另一个意图是使代码能够跨平台编译。该代码将在任何支持 SSE/SIMD 的 CPU 上工作。它未在 RISC 或类似 ARM 的 CPU 上进行测试。

Using the Code

代码本身并不大或复杂

/* definition for RAND_MAX constant */
#include <stdlib.h>
/* random number storage */
static unsigned long long g_seed;

/**
 * Used to seed the generator
 * @param seed initializing "generator"
 */
void fast_srand(unsigned int seed)
{
    g_seed = seed;
}

/**
 * Compute a pseudo random integer. Output value in range [0, RAND_MAX]
 * @return pseudo random integer
 */
int fast_rand()
{
    g_seed = (214013*g_seed+2531011);
    return (g_seed>>32)&RAND_MAX;
}

这段代码并非完全是我写的。我在互联网上找到了它的主要思想。我尝试将这段代码与标准库函数(rand/random)进行比较,并尝试读取来自/dev/urandom的随机数据。

性能和随机性测试

有人要求我比较文章中生成器的性能和随机性,以及最常用的库生成器。

测试代码

我编写了一个测试代码

/* definition for RAND_MAX constant */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DEF_RANDOM_COUNT 10000
/* random number storage */
static unsigned long long g_seed;

/**
 * Used to seed the generator
 * @param seed initializing "generator"
 */
void fast_srand(unsigned int seed)
{
    g_seed = seed;
}

/**
 * Compute a pseudo random integer. Output value in range [0, RAND_MAX]
 * @return pseudo random integer
 */
int fast_rand()
{
    g_seed = (214013*g_seed+2531011);
    return (g_seed>>32)&RAND_MAX;
}
int cStrToInt(const char* sz)
{
    char* pEnd;
    if(sz==NULL)    {
        return 0;
    }
    return strtol(sz,&pEnd, 10);
}

int main(int argc, char* argv[])  {
    int i, n;
    int randomCount = DEF_RANDOM_COUNT;
    int randomType = 0;
    int dontPrint = 0;
    for(i=1; i<argc; i++)   {
        if(argv[i][0]=='-') {
            switch(argv[i][1])  {
                case 't':/* type */
                    if(i+1<argc)    {
                        randomType = cStrToInt(argv[++i]);
                    }
                    break;
                case 'c':
                    if(i+1<argc)    {
                        randomCount = cStrToInt(argv[++i]);
                    }
                    break;
                case 'p':
                    dontPrint = 1;
                    break;
            }
        }
        else    {
            randomCount = cStrToInt(argv[i]);
        }
    }
    if(randomCount<0)   {
        randomCount = DEF_RANDOM_COUNT;
    }
    switch(randomType)  {
        case 2:
            srandom(time(NULL));
            break;
        case 1:
            srand(time(NULL));
            break;
        default:
            fast_srand(time(NULL));
            break;
    }
    for(i=0; i<randomCount; i++)    {
        switch(randomType)  {
            case 2:
                n = random();
                break;
            case 1:
                n = rand();
                break;
            default:
                n = fast_rand();
                break;
        }
        if(dontPrint)   {
            continue;
        }
        printf("%d\n",fast_rand());
    }
}

我将测试代码保存为rndGenTest.c,并使用以下命令编译代码

gcc rndGenTest.c -o rndGenTest 

性能

在我的 Debian WSL 上的性能比较结果是(第一个结果为文章中的代码,第二个 - srand/rand,第三个 - srandom/random

-bash $ time ./rndGenTest -p 10000000 ; time ./rndGenTest -p -t 1 10000000  ; time ./rndGenTest -p -t 2 10000000

real    0m0.034s
user    0m0.030s
sys     0m0.000s

real    0m0.071s
user    0m0.066s
sys     0m0.000s

real    0m0.057s
user    0m0.052s
sys     0m0.000s

随机性

我还对我的 Debian WSL 上的所有这些生成器进行了一个快速的随机性测试(第一个结果为文章中的代码,第二个 - srand/rand,第三个 - srandom/random

-bash $ ./rndGenTest 1000000 | sort -u | wc -l ; ./rndGenTest -t 1 1000000 | sort -u | wc -l ; ./rndGenTest -t 2 1000000
 | sort -u | wc -l
999766
999762
999762

关注点

我对代码速度的比较结果感到非常惊讶。文章中的代码比库代码快两倍。我认为我可以找到解释为什么会发生这种情况,但是...

无论如何,文章中的生成器非常适合生成大型测试数据。

历史

  • 2024年2月19日:初始文本
  • 2024年2月20日:添加测试部分
© . All rights reserved.