对齐数组示例中的c ++ openmp false-sharing

Sel*_*can 8 c++ memory multithreading caching openmp

我想看看虚假分享的效果.为此,我试图设计一个小实验,但我得到了意想不到的结果.

我有一个包含100米整数的数组.将其视为mxn矩阵.一个线程改变奇数索引行和其他线程更改甚至索引行.

实验A:列数为16.因此每行为64字节,这正是我的缓存行大小.由于每个线程一次只处理1个高速缓存行,因此不应该存在错误共享.因此,我预计加速将达到100%左右.

实验B:列数为8.每个线程一次更改32个字节,这是高速缓存行的一半.例如,如果线程1处理第33行,则应该从线程0传输数据,因为线程1已经处理了位于同一高速缓存行中的第32行.(反之亦然,顺序无关紧要).由于这种沟通,加速应该很低.

#include <iostream>
#include <omp.h>

using namespace std;

int main(int argc, char** argv) {

    if(argc != 3) {
        cout << "Usage: " << argv[0] << " <iteration> <col_count>" << endl;
        return 1;
    }

    int thread_count = omp_get_max_threads();
    int iteration = atoi(argv[1]);
    int col_count = atoi(argv[2]);
    int arr_size = 100000000;

    int* A = (int*) aligned_alloc(16 * sizeof(int), arr_size * sizeof(int));

    int row_count = arr_size / col_count; 
    int row_count_per_thread = row_count / thread_count;

    #pragma omp parallel
    {
        int thread_id = omp_get_thread_num();

        long long total = 1ll * iteration * row_count_per_thread * col_count;
        printf("%lld\n", total);

        for(int t = 0; t < iteration; t++) {

            for(int i = 0; i < row_count_per_thread; i++) {

                int start = (i * thread_count + thread_id) * col_count;
                for(int j = start; j < start + col_count; j++) {


                    if(A[j] % 2 == 0)
                        A[j] += 3;
                    else
                        A[j] += 1;
                }
            }
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我使用以下方式使用不同的配置运行此代码:

time taskset -c 0-1 ./run 100 16

以下是100次迭代的结果:

Thread      Column      Optimization        Time (secs)
_______________________________________________________
1           16          O3                  7.6
1           8           O3                  7.7
2           16          O3                  7.7
2           8           O3                  7.7

1           16          O0                  35.9
1           8           O0                  34.3
2           16          O0                  19.3
2           8           O0                  18.2
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,尽管O3优化可以获得最佳结果,但它们非常奇怪,因为增加线程数并不能提高速度.对我来说,O0优化结果更容易解释.

真正的问题:看看最后2行.对于这两种情况,我的速度几乎达到了100%,但我希望实验B的执行时间要高得多,因为它存在错误共享问题.我的实验或理解有什么问题?

我用g++ -std=c++11 -Wall -fopenmp -O0 -o run -Iinc $(SOURCE) 和 编译它 g++ -std=c++11 -Wall -fopenmp -O3 -o run -Iinc $(SOURCE)

如果我的问题不明确或需要更多细节,请告诉我.


更新:规格:

MemTotal:        8080796 kB
Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              8
On-line CPU(s) list: 0-7
Thread(s) per core:  2
Core(s) per socket:  4
Socket(s):           1
NUMA node(s):        1
Vendor ID:           GenuineIntel
CPU family:          6
Model:               71
Model name:          Intel(R) Core(TM) i7-5700HQ CPU @ 2.70GHz
Stepping:            1
CPU MHz:             2622.241
CPU max MHz:         3500,0000
CPU min MHz:         800,0000
BogoMIPS:            5387.47
Virtualization:      VT-x
L1d cache:           32K
L1i cache:           32K
L2 cache:            256K
L3 cache:            6144K
NUMA node0 CPU(s):   0-7
Run Code Online (Sandbox Code Playgroud)

更新2:我已经尝试了不同的iteration_countarr_size参数,以便在阵列中的L2配合,L1高速缓存,同时使元件变化恒定的总数.但结果仍然相同.

谢谢.

Kit*_*it. 5

您的-O3时序似乎与处理器的 1通道内存访问速度一致.使用双通道内存配置可能会使速度提高2倍,但不太可能对结果产生任何其他差异.请记住,在您的处理器上,核心之间共享一个L3缓存,因此任何错误共享很可能在L3缓存级别上得到解决,并且不会导致外部存储器总线上的额外负载.

您的代码存在许多问题(而不仅仅是"慢速"内存访问)可能会阻止您看到错误共享的影响.

首先,考虑到线程调度中涉及的时间不可预测性,两个线程都不太可能竞争完全相同的缓存行.

其次,即使它们确实存在冲突,也会是暂时的,因为任何导致非对称减速的因素都会导致"较慢"的线程延迟扫描,直到它超出冲突的内存范围.

第三,如果它们恰好在同一核心的两个硬件线程上运行,它们将访问完全相同的缓存实例,并且不会有缓存冲突.

要"修复"所有这些,您需要更多线程(或绑定到特定内核的线程)和更紧密的内存区域以应对可能的冲突.如果您的线程只竞争一个内存缓存行,那么"最佳"结果将是.