Caching “什么是”呢;虚假分享;?如何复制/避免它?

Caching “什么是”呢;虚假分享;?如何复制/避免它?,caching,optimization,parallel-processing,computer-architecture,false-sharing,Caching,Optimization,Parallel Processing,Computer Architecture,False Sharing,今天在并行编程课上,我和我的教授对什么是“虚假共享”有了不同的理解。我的教授所说的毫无意义,所以我立即指出了这一点。她认为“虚假分享”会导致节目结果出错 我说过,“错误共享”发生在不同的内存地址被分配到同一个缓存线时,将数据写入其中一个缓存线会导致另一个缓存线被踢出缓存线。如果处理器在两个假共享地址之间写入数据,则两个地址都无法保留在缓存中,因此所有操作都将导致对DRAM的访问 到目前为止,这是我的看法。事实上我也不确定我说了什么。。。如果我有误解,请指出 所以有一些问题。缓存假定为64字节对齐

今天在并行编程课上,我和我的教授对什么是“虚假共享”有了不同的理解。我的教授所说的毫无意义,所以我立即指出了这一点。她认为“虚假分享”会导致节目结果出错

我说过,“错误共享”发生在不同的内存地址被分配到同一个缓存线时,将数据写入其中一个缓存线会导致另一个缓存线被踢出缓存线。如果处理器在两个假共享地址之间写入数据,则两个地址都无法保留在缓存中,因此所有操作都将导致对DRAM的访问

到目前为止,这是我的看法。事实上我也不确定我说了什么。。。如果我有误解,请指出

所以有一些问题。缓存假定为64字节对齐、4路集关联

  • 两个被64字节以上分隔的地址是否可能是“错误共享”
  • 单线程程序是否可能遇到“错误共享”问题
  • 复制“虚假共享”的最佳代码示例是什么
  • 一般来说,应该注意什么来避免程序员的“错误共享”

  • 我将分享我对你的问题的观点

  • 两个地址之间的间距大于块大小的字节数,不会驻留在完全相同的缓存线上。因此,如果一个内核的缓存中有第一个地址,而另一个内核请求第二个地址,那么第一个地址不会因为该请求而从缓存中删除。这样就不会出现错误的共享遗漏

  • 我无法想象在完全没有并发性的情况下会发生怎样的错误共享,因为除了单个线程之外,不会有任何其他线程竞争缓存线

  • 使用OpenMP复制虚假共享的简单示例如下:

    double sum=0.0, sum_local[NUM_THREADS];
    
    #pragma omp parallel num_threads(NUM_THREADS)
    {
        int me = omp_get_thread_num();
        sum_local[me] = 0.0;
    
        #pragma omp for
        for (i = 0; i < N; i++)
            sum_local[me] += x[i] * y[i];
    
        #pragma omp atomic
        sum += sum_local[me];
    }
    
    double sum=0.0,sum_local[NUM_THREADS];
    #pragma omp并行num_线程(num_线程)
    {
    int me=omp_get_thread_num();
    sum_local[me]=0.0;
    #pragma omp for
    对于(i=0;i
  • 为避免错误分享,我可以想到的一些一般性注释如下:

    double sum=0.0, sum_local[NUM_THREADS];
    
    #pragma omp parallel num_threads(NUM_THREADS)
    {
        int me = omp_get_thread_num();
        sum_local[me] = 0.0;
    
        #pragma omp for
        for (i = 0; i < N; i++)
            sum_local[me] += x[i] * y[i];
    
        #pragma omp atomic
        sum += sum_local[me];
    }
    
    a。尽可能多地使用私有数据

    b。有时,您可以使用来对齐数据,以确保共享数据所在的缓存中不会有其他变量


  • 欢迎进行任何更正或添加。

    启用优化后,任何体面的编译器都会将
    sum\u local[me]
    保存在寄存器中,除非它无法证明
    x[i]
    y[i]
    不能对其进行别名。(有些编译器会在运行时发出代码来检查重叠情况,并在这种情况下使用自动矢量化循环。否则,潜在的别名可能会使循环在没有错误共享的情况下也会失败;在循环承载的依赖链中,只需额外5个存储转发延迟周期而没有错误共享,这是非常糟糕的)。但是,是的,任何稍微复杂一点的东西都很容易在循环内部产生错误的共享,而不仅仅是在循环结束时。@PeterCordes如果NUM_线程不是一个常量呢?我不认为编译器可以在这种情况下分配寄存器case@tuket:那没关系。它所要证明的是,
    foo[bar]
    在循环中每次都是相同的地址,写入它不会影响我们从
    x[i]
    y[i]
    读取的内容。(例如,
    sum_local[]
    x[]
    y[]
    不重叠)。由于
    me
    是循环不变的,而
    sum\u local
    是一个数组,因此编译器可以在循环内的寄存器中保留一个和,并在最后使用运行时变量值
    me
    存储一次。每个线程都有自己的
    me
    ,并且知道C99 VLA的基址
    double sum_local[NUM_THREADS]
    @tuket:无论哪种方式,理想情况下都可以让每个线程只使用自己的私有标量变量,但本示例的重点是演示错误共享,因此故意将其写得“糟糕”。我仍然认为您必须在禁用优化的情况下编译,以阻止编译器将其优化到寄存器中。或
    易失性双和本地[NUM_线程]是一个关于虚假分享的视频,希望对大家有所帮助。如果没有50%的声誉,我不能添加评论,这真的很尴尬。