Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/125.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 用多个线程填充向量_C++_Multithreading_C++11 - Fatal编程技术网

C++ 用多个线程填充向量

C++ 用多个线程填充向量,c++,multithreading,c++11,C++,Multithreading,C++11,我需要用随机值填充一个巨大的(7734500个元素)std::vector,我正在尝试与多个线程并行执行,以实现更高的效率。以下是我目前掌握的代码: 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::unifor

我需要用随机值填充一个巨大的(7734500个元素)
std::vector
,我正在尝试与多个线程并行执行,以实现更高的效率。以下是我目前掌握的代码:

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;
}

然后程序如下

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);
    }
}
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();
    }
}
void unifrandvectorthread例程
(标准::向量和向量,无符号整数开始,
无符号整数(结束)
{
XORSHIFT 128plusGenerator prng;
for(无符号整数i=start;i
因为我现在在家,我使用的是另一台(功能更强大的)机器,所以我重新进行了测试以比较结果。以下是我得到的:

  • Mersenne捻线机,每根线一个发生器:0.075秒
  • xorshift128+在所有线程之间共享:0.023秒
  • xorshift128+,每个线程一个生成器:0.023秒
注意:每次重复的执行时间不同。这些只是典型值


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

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

std::vector v,它在线程外部声明,然后在每个线程中的for循环的每次迭代中命中。让每个线程都有自己的向量来填充,一旦所有线程都完成了,就在向量
v
中整理它们的结果。可能通过
std::future
将是最快的。争用的确切大小取决于缓存线大小以及所使用(和分段)的向量大小

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

W.R.T.你可以使用的数字线程,你应该考虑绑定<代码> NoMuthWords/COD>到目标上可用的硬件并发;i、 e.
std::thread::hardware\u concurrency()

在处理如此大量的元素时,还可以避免不必要的初始化和结果的移动(尽管给定了
int
类型,这里的移动不太明显)。容器本身也是需要注意的<代码>向量
需要连续内存,因此任何附加元素(在联盟阶段)都可能导致内存分配和复制

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


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

Mersenne捻线机生成器(
std::mt19937_64
)速度不太快。您可以考虑其他发电机,如XORSHIFT +。参见,例如,这个问题:(这里的讨论不仅仅是布尔)

您应该在代码中消除数据竞争。每个线程使用一个生成器。

std::vector v;
  std::vector<unsigned int> v;
    v.resize(rows*columns);
v、 调整大小(行*列);
不幸的是,
std::vector::resize
value也初始化了原语,使程序在向量内存上写一次零,然后用随机数覆盖该值

尝试
std::vector::reserve
+
std::vector::push_back

这意味着线程不能在没有锁的情况下共享向量,但您可以为每个线程提供自己的向量,使用
reserve+push_back
然后将所有结果合并为更大的向量


如果这还不够,我不想这么说,请将
std::unique_ptr
malloc
(与costment deleter一起使用)。是的,这是C,是的,这很糟糕,是的,我们有
new[]
,但是
malloc
不会对内存进行零初始化(与
new[]
和stl容器不同),然后您可以将内存段分散到每个线程,并让它在其上生成随机数。您将把组合向量保存为一个组合向量。

为什么创建线程后要加入它?这与按顺序执行基本相同。此外,每个线程使用RNG可能比共享RNG更好。在
生成器上有一个竞争,因为从多个线程访问未同步。使其成为
thread\u local
也许?PRNG有一个状态,因此通常不是线程安全的。而不是
NUM\u THREADS=4尝试
NUM_THREADS=std::thread::hardware_concurrency(),也就是说,为了不猜测内核的数量(有些内核支持连字符线程),我认为缓存争用不多,每个线程只从某个起始位置接触其
v
的相邻部分。争用只发生在边界和非常不同的时刻。整理线程本地向量时也会出现同样的问题,使用此解决方案将执行更多的内存操作。写入操作将使缓存线无效,因此这在很大程度上取决于缓存线的大小等。为每个线程提供自己要写入的向量(然后在最后整理)将避免争用,是的,这是我的观点,当每个线程只访问其
v
的相邻部分时,唯一的shraed缓存线就是位于该部分两端的缓存线。还有什么争论呢?正如Daniel Langr在本例中所写的那样。缓存争用发生在
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();
    }
}
  std::vector<unsigned int> v;
    v.resize(rows*columns);