用多个线程填充向量

组学

我需要std::vector<unsigned int>用随机值填充一个巨大的(7734500个元素),并且我试图与多个线程并行执行以实现更高的效率。这是我到目前为止的代码:

std::random_device rd; // seed generator

std::mt19937_64 generator{rd()}; // generator initialized with seed from rd

static const unsigned int NUM_THREADS = 4;


std::uniform_int_distribution<> initialize(unsigned long long int modulus)
{
    std::uniform_int_distribution<> unifDist{0, (int)(modulus-1)};
    return unifDist;
}


void unifRandVectorThreadRoutine
    (std::vector<unsigned int>& vector, unsigned int start,
    unsigned int end, std::uniform_int_distribution<>& dist)
{
    for(unsigned int i = start ; i < end ; ++i)
    {
        vector[i] = dist(generator);
    }
}


std::vector<unsigned int> uniformRandomVector
    (unsigned int rows, unsigned int columns, unsigned long long int modulus)
{
    std::uniform_int_distribution<> dist = initialize(modulus);

    std::thread threads[NUM_THREADS];

    std::vector<unsigned int> v;
    v.resize(rows*columns);

    // number of entries each thread will take care of
    unsigned int positionsEachThread = rows*columns/NUM_THREADS;

    // all but the last thread
    for(unsigned int i = 0 ; i < NUM_THREADS - 1 ; ++i)
    {
        threads[i] = std::thread(unifRandVectorThreadRoutine, v, i*positionsEachThread,
            (i+1)*positionsEachThread, dist);
        // threads[i].join();
    }

    // last thread
    threads[NUM_THREADS - 1] = std::thread(unifRandVectorThreadRoutine, v,
        (NUM_THREADS-1)*positionsEachThread, rows*columns, dist);
    // threads[NUM_THREADS - 1].join();

    for(unsigned int i = 0 ; i < NUM_THREADS ; ++i)
    {
        threads[i].join();
    }

    return v;
}

目前,此过程大约需要0.3秒:您认为有办法提高效率吗?


编辑:给每个线程自己的生成器

我将例程修改如下

void unifRandVectorThreadRoutine
    (std::vector<unsigned int>& vector, unsigned int start,
    unsigned int end, std::uniform_int_distribution<>& dist)
{
    std::mt19937_64 generator{rd()};
    for(unsigned int i = start ; i < end ; ++i)
    {
        vector[i] = dist(generator);
    }
}

运行时间减少了一半。所以我仍然在共享,std::random_device但是每个线程都有自己的线程std::mt19937_64


编辑:给每个线程自己的向量,然后串联

我将代码更改如下:

void unifRandVectorThreadRoutine
    (std::vector<unsigned int>& vector, unsigned int length,
    std::uniform_int_distribution<>& dist)
{
    vector.reserve(length);
    std::mt19937_64 generator{rd()};
    for(unsigned int i = 0 ; i < length ; ++i)
    {
        vector.push_back(dist(generator));
    }
}

std::vector<unsigned int> uniformRandomVector
    (unsigned int rows, unsigned int columns, unsigned long long int modulus)
{
    std::uniform_int_distribution<> dist = initialize(modulus);

    std::thread threads[NUM_THREADS];

    std::vector<unsigned int> v[NUM_THREADS];

    unsigned int positionsEachThread = rows*columns/NUM_THREADS;

    // all but the last thread
    for(unsigned int i = 0 ; i < NUM_THREADS - 1 ; ++i)
    {
        threads[i] = std::thread(unifRandVectorThreadRoutine, std::ref(v[i]), positionsEachThread, dist);
    }

    // last thread
    threads[NUM_THREADS - 1] = std::thread(unifRandVectorThreadRoutine, std::ref(v[NUM_THREADS-1]),
        rows*columns - (NUM_THREADS-1)*positionsEachThread, dist);

    for(unsigned int i = 0 ; i < NUM_THREADS ; ++i)
    {
        threads[i].join();
    }

    std::vector<unsigned int> finalVector;
    finalVector.reserve(rows*columns);

    for(unsigned int i = 0 ; i < NUM_THREADS ; ++i)
    {
        finalVector.insert(finalVector.end(), v[i].begin(), v[i].end());
    }

    return finalVector;
}

当我只使用一个在所有线程之间共享的向量时,执行时间比以前差一些。我是否想念某些东西还是会发生?


编辑:使用不同的PRNG +基准

使用不同的PRNG(如一些评论/答案所建议)有很多帮助:我尝试了使用xorshift+,这是我正在使用的实现:

class xorShift128PlusGenerator
{
public:
    xorShift128PlusGenerator()
    {
        state[0] = rd();
        state[1] = rd();
    };


    unsigned long int next()
    {
        unsigned long int x = state[0];
        unsigned long int const y = state[1];
        state[0] = y;
        x ^= x << 23; // a
        state[1] = x ^ y ^ (x >> 17) ^ (y >> 26); // b, c
        return state[1] + y;
    }


private:
    std::random_device rd; // seed generator
    unsigned long int state[2];

};

然后例程如下

void unifRandVectorThreadRoutine
    (std::vector<unsigned int>& vector, unsigned int start,
    unsigned int end)
{
    xorShift128PlusGenerator prng;
    for(unsigned int i = start ; i < end ; ++i)
    {
        vector[i] = prng.next();
    }
}

由于我现在在家中,并且正在使用其他(功能更强大)的计算机,因此我重新测试以比较结果。这是我得到的:

  • Mersenne Twister每线程一个生成器:0.075秒
  • 所有线程之间共享的xorshift128 +:0.023秒
  • xorshift128 +,每个线程一个生成器:0.023秒

注意:每次重复执行的时间都不同。这些只是典型值。

因此,是否共享xorshift生成器似乎没有什么区别,但是通过所有这些改进,执行时间大大减少了。

尼尔

生成器std::mt19937_64 generator{rd()};在线程之间共享。将存在一些需要更新的共享状态,因此存在争用;有一场数据竞赛。您还应该允许每个线程使用自己的生成器-您只需要确保它们生成单独的序列即可。

您可能在附近有一个缓存争用问题std::vector<unsigned int> v;,它在线程外声明,然后在每个线程中的for循环的每次迭代中都命中。让每个线程都有其自己的向量来填充,一旦所有线程都完成,就将它们的结果整理到vector中v可能通过astd::future最快。争用的确切大小取决于高速缓存行大小和正在使用(和分段)的向量的大小

在这种情况下,用相对较少的线程(4)填充大量元素(7734500),该比率可能会导致较少的争用。

要使用可能使用的线程数,应考虑将其绑定NUM_THREADS到目标上可用的硬件并发;std::thread::hardware_concurrency()

在处理大量元素时,您还可以避免不必要的初始化和结果的移动(尽管给定了int类型,但移动在这里不太明显)。容器本身也是需要注意的事情。vector需要连续的内存,因此(在合并阶段)任何其他元素都可能导致内存分配和复制。

随机数生成器的速度也可能会产生影响,其他实现和/或算法可能会充分影响最终执行时间以至于需要考虑。

与所有基于性能的问题一样,最终解决方案需要评估。实施可能的解决方案,在目标处理器和环境上进行测量,并进行调整,直到找到合适的性能为止。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

用C中的多个线程填充数组

来自分类Dev

用概率计算填充向量

来自分类Dev

用概率计算填充向量

来自分类Dev

Boost:创建对象并用线程填充向量

来自分类Dev

用随机数填充向量C ++

来自分类Dev

用随机数填充向量

来自分类Dev

根据向量序列用NA填充行

来自分类Dev

用unique_pointers填充向量

来自分类Dev

用随机字节填充字节向量

来自分类Dev

用随机值填充通用向量

来自分类Dev

用'for'循环产生的NA的向量填充矩阵

来自分类Dev

用JavaScript填充多个DIV

来自分类Dev

用多个变量填充网格

来自分类Dev

用线程C#填充datagridview

来自分类Dev

熊猫用多个值划分填充

来自分类Dev

Excel用多个值填充列

来自分类Dev

用Java中的多个变量填充对象?

来自分类Dev

用重复列表填充Julia中的向量

来自分类Dev

用新值填充向量并向左移动

来自分类Dev

用C ++中的乱序数据填充向量

来自分类Dev

使用numpy用零向量填充空列表

来自分类Dev

多个线程在同一向量的不同向量上同时添加元素发生错误

来自分类Dev

试图用向量填充结构时向量下标超出范围

来自分类Dev

一种用内部向量填充结构向量的优雅/有效方式

来自分类Dev

创建模板函数以根据大小用另一个向量填充向量

来自分类Dev

用两个或多个向量的高阶列构成矩阵

来自分类Dev

R:用多个规则替换列/向量值的明智方法?

来自分类Dev

用返回多个值的函数的向量化调用替换for循环

来自分类Dev

用R中多个对象的信息填充data.table