Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/143.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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_Memory_Optimization - Fatal编程技术网

C++ 并行计算内存访问瓶颈

C++ 并行计算内存访问瓶颈,c++,multithreading,memory,optimization,C++,Multithreading,Memory,Optimization,以下算法在我的程序中迭代运行。如果没有下面所示的两条线,运行它需要1.5倍的时间。这对我来说是非常令人惊讶的。然而,更糟糕的是,使用这两条线运行会将完成率提高到不使用这两条线运行的4.4倍(不运行整个算法的6.6倍)。此外,它还导致我的程序无法扩展到8核以上。事实上,当在一个内核上运行时,这两条线路只会将时间增加到1.7倍,考虑到它们的功能,这仍然太高了。我已经排除了这与我程序中其他地方修改数据的影响有关 所以我想知道是什么导致了这一切。也许和缓存有关 void NetClass::Age_In

以下算法在我的程序中迭代运行。如果没有下面所示的两条线,运行它需要1.5倍的时间。这对我来说是非常令人惊讶的。然而,更糟糕的是,使用这两条线运行会将完成率提高到不使用这两条线运行的4.4倍(不运行整个算法的6.6倍)。此外,它还导致我的程序无法扩展到8核以上。事实上,当在一个内核上运行时,这两条线路只会将时间增加到1.7倍,考虑到它们的功能,这仍然太高了。我已经排除了这与我程序中其他地方修改数据的影响有关

所以我想知道是什么导致了这一切。也许和缓存有关

void NetClass::Age_Increment(vector <synapse> & synapses, int k)  
{
    int size = synapses.size();
    int target = -1;

    if(k > -1)
    {
        for(int q=0, x=0 ; q < size; q++)
        {
            if(synapses[q].active)
                synapses[q].age++;
            else
            {
                if(x==k)target=q;
                x++;
            }
        }
        /////////////////////////////////////Causing Bottleneck/////////////
        synapses[target].active = true;
        synapses[target].weight = .04 + (float (rand_r(seedp) % 17) / 100);
        ////////////////////////////////////////////////////////////////////
    }

    else
    {
        for(int q=0 ; q < size; q++)
            if(synapses[q].active)
                synapses[q].age++;
    }
}

解决了这个问题。暗示可能与内存访问有关?

如果
大小相对较小,那么调用PRNG、整数除法、浮点除法和加法将大大增加程序执行,我一点也不感到惊讶。您正在做大量的工作,因此增加运行时间似乎是合乎逻辑的。此外,由于您告诉编译器以
float
而不是
double
的方式进行计算,因此在某些系统上(本机浮点为double)可能会进一步增加时间。您是否考虑过使用
int
s的定点表示

我说不出为什么它会随着更多的内核而扩展得更糟,除非您的程序超过了操作系统提供的内核数(或者如果您的系统的
rand\r
是使用锁定或特定于线程的数据来实现的,以保持额外的状态)


还要注意的是,在将
目标用作数组索引之前,永远不要检查它是否有效,如果它在
for
循环中仍然设置为-1,则程序的所有赌注都将被取消。

尝试重新引入这两行,但不要使用
rand\r
,只是为了看看是否会出现相同的性能下降。如果不这样做,这可能表明
rand\u r
是内部序列化的(例如通过互斥),因此您需要找到一种更并发地生成随机数的方法


另一个潜在的关注领域是(如果你有时间,看看Herb Sutter's和治疗这个主题的方法,等等)。本质上,如果线程碰巧修改了足够接近的不同内存位置,从而落入同一缓存线,那么缓存一致性硬件可能会有效地序列化内存访问并破坏可伸缩性。使这一点难以诊断的是,这些内存位置在逻辑上可能是独立的,而且从直觉上看,它们在运行时可能靠得很近。如果怀疑共享错误,请尝试添加一些填充以将这些内存位置分开。

每个线程修改内存,其他所有线程读取:

for(int q=0, x=0 ; q < size; q++)
   if(synapses[q].active) ... // ALL threads read EVERY synapse.active
...
synapses[target].active = true; // EVERY thread writes at leas one synapse.active
for(int q=0,x=0;q
这些从不同线程对同一地址的读写操作会导致大量缓存失效,这将导致您描述的症状。解决方案是避免循环内的写操作,而将写操作移动到局部变量这一事实再次证明了问题在于缓存失效。请注意,即使您不编写正在读取的sane字段(
active
),您也可能会由于错误共享而看到相同的症状,因为我怀疑
active
age
weight
共享缓存线

有关更多详细信息,请参阅


最后一点需要注意的是,分配给
active
weight
,更不用说
age++
的增量似乎都非常不安全。这种更新的联锁操作或锁/互斥保护将是强制性的。

@Mankarse这可能是什么原因,它有什么意义?@GMan它与
rand()
做同样的事情。我在其他地方更常这样称呼它。我认为它是在
中定义的。我将测试以确保这不是问题。@MattMunson:
rand
的一部分,但我从未听说过
rand\u r
。谷歌搜索,它似乎是一个Linux扩展。(一个很好的方法是删除全局状态。)
seedp
是共享的吗?例如,如果将
rand\u r(seedp)
替换为
0
,会发生什么情况?@GMan将
rand\u r()
全部替换为常量没有帮助。@GMan据我所知
rand\u r
是一个POSIX函数,2001年添加,2008年从标准中删除。我在程序的其他地方做了更多的算术。而且,我没有超过核心,移除rand_r也没有改善任何东西。酷。但是顺便说一句,synapse对象不在线程之间共享。我通常会添加一个[cache line]大小的缓冲区作为每个对象的第一个私有数据成员。在c'tor中将其设置为零通常足以防止其被优化。一些语言/lib(boost,AFAIK)允许精确控制对象的放置,这应该更好——对于大型对象,让实例数据从页面边界开始,然后填充页面大小倍数,这将非常好@Martin当涉及到我正在处理的对象集时,我为每个对象创建一个线程本地副本,然后在处理完后将其放回进程本地向量中。如果有任何错误的共享,我会认为这是来自成员向量(来自对象)的数组,这些成员向量恰好彼此分配在一起(如果有意义的话)。有什么办法吗?
for(int q=0, x=0 ; q < size; q++)
   if(synapses[q].active) ... // ALL threads read EVERY synapse.active
...
synapses[target].active = true; // EVERY thread writes at leas one synapse.active