C++ 使用“英特尔OpenMP”查找最佳线程数:只有1个线程的结果比许多线程的结果更好

C++ 使用“英特尔OpenMP”查找最佳线程数:只有1个线程的结果比许多线程的结果更好,c++,multithreading,optimization,openmp,intel,C++,Multithreading,Optimization,Openmp,Intel,在我的代码中使用倍数乘以以下类型的循环: #pragma omp parallel for schedule(dynamic, num_threads) for(int i=0; i<F_matrix_A.size(); i++){ for(int j=0; j<F_matrix_A.size(); j++){ F_previous_T[i][j] = F_previous[j][i]; } } #pragma omp parallel for sc

在我的代码中使用倍数乘以以下类型的循环:

#pragma omp parallel for schedule(dynamic, num_threads)
for(int i=0; i<F_matrix_A.size(); i++){
    for(int j=0; j<F_matrix_A.size(); j++){
        F_previous_T[i][j] = F_previous[j][i];
    }
}

#pragma omp parallel for schedule(dynamic, num_threads)
for(int i=0; i<F_matrix_A.size(); i++){
    for(int k=0; k<F_matrix_A.size(); k++){
        for(int j=0; j<=i; j++){
            if(F_previous_T[i][k] != 0 && F_previous[k][j] !=0){
                Fisher_new[i][j] += F_previous_T[i][k]*F_previous[k][j];
            }
        }
    }
}
?

是否有其他方法可以降低运行时间?我在不同的论坛上看到,与相当多的进程一起使用可能会导致巨大的开销

我的循环的大小通常为1700x1700

如果有人有一个想法,这将是很好的告诉它

更新1:我的代码有两个版本,一个是GNU g++版本,另一个是英特尔icpc

1) 我正在使用以下“通用”生成文件:

ifneq "$(MAKECMDGOALS)" "clean"
include $(MAKECMDGOALS).make
endif

OBJECTS = $(SOURCES:.cpp=.o)

$(MAKECMDGOALS): $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CXX) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
    $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@

clean:
    rm -f *.o
2) 对于
Intel icpc
,我使用
Intel.make
文件进行编译:

CXX = g++ -std=c++11 -O3 -fopenmp
CXXFLAGS = -Wall -c
LDFLAGS = -march=native
LDFLAGS =
SOURCES = main.cpp TSAF_gnu.cpp
EXECUTABLE = main_gnu.exe
CXX = icpc -std=c++11 -O3 -xHost -qopenmp
CXXFLAGS = -Wall -c -I${MKLROOT}/include
LDFLAGS  = -mkl=parallel
LDFLAGS += -L${MKLROOT}/lib/intel64_lin -Wl,-rpath,${MKLROOT}/lib/intel64_lin -lmkl_intel_lp64 -lmkl_intel_thread \
          -lmkl_core -liomp5 -lpthread
SOURCES = main.cpp TSAF_intel.cpp
EXECUTABLE = main_intel.exe
标准运行大约需要3分钟。

计划(动态、num_线程)
可能会导致可伸缩性问题

实际上,对于大小为1700和64个线程的矩阵,动态调度策略的块大小是64。因此,块的数量是
floor(1700/64)=26
,这太小了,无法容纳64个线程! 即使有32个线程,工作平衡也不是很好。我认为每个线程至少有3-4个块是很重要的

随着线程数量的增加而增加粒度是很奇怪的。根据输入大小设置粒度可能更相关。我建议使用
schedule(guided)
schedule(dynamic,chunksize)
将chunksize设置为
max(F_matrix\u A.size()/(num_threads*4),1)
(尽管如果不添加
折叠,使用
schedule(dynamic,1)
应该不会太糟糕)

或者,可以使用task和taskloops指令

还请注意,如果您在具有多个NUMA节点的机器上工作(可能是这种情况,因为有64个内核),那么在动态调度时应该非常小心,因为线程访问远程NUMA内存节点可能会显著降低性能(这显然是内存绑定代码中不希望出现的情况)

更新:您可以同时在阵列的两个垂直面上工作,以显著减少内环计算时间的变化。结果会是这样的:

#计划的pragma omp并行(静态)
对于(int i=0;i行
调度(动态,num_线程)
可能会导致可伸缩性问题

实际上,对于大小为1700和64个线程的矩阵,动态调度策略的块大小是64。因此,块的数量是
floor(1700/64)=26
,这太小了,无法满足64个线程的需要! 即使有32个线程,工作平衡也不是很好。我认为每个线程至少有3-4个块是很重要的

随着线程数量的增加而增加粒度是很奇怪的。根据输入大小设置粒度可能更相关。我建议使用
调度(引导)
调度(动态,chunksize)
,chunksize设置为
max(F_matrix_a.size()/(num_threads*4),1)
(尽管使用
计划(动态,1)
如果不添加
折叠
,应该不会太糟糕)

或者,可以使用task和taskloops指令

还请注意,如果您在具有多个NUMA节点的机器上工作(可能是这种情况,因为有64个内核),那么在动态调度时应该非常小心,因为线程访问远程NUMA内存节点可能会显著降低性能(这显然是内存绑定代码中不希望出现的情况)

更新:您可以同时在阵列的两个垂直面上工作,以显著减少内部循环计算时间的变化。结果如下:

#计划的pragma omp并行(静态)

对于(int i=0;i)编译标志是什么?您使用哪种优化级别?此外,此大小的矩阵可能太小,无法进行并行处理。单线程运行需要多长时间?@DanielLangr我添加了一个更新1以提供更多信息。快速浏览:(1)通过遍历一个数组行主循环和另一个列主循环,您几乎无法编写更少的缓存友好访问模式。(2)每个循环的“计算负载”都是微不足道的。(3)
如果埋在深循环嵌套底部的
语句是以非常高的速率混淆分支预测的好方法。(4)
调度(动态,*small\u chunk\u size*)
可能是解决此问题的最糟糕的时间表。现在,对于您的应用程序来说,这些都不是可以避免的,但您所写的内容几乎是不使用OpenMP的教科书示例。如果(…)的话,您如何摆脱
在最里面的循环中,还可以得到废弃的
调度
子句?如果这两个因子中的任何一个为零,那么乘积无论如何都将为零,乘法非常快。另外,
调度(动态)
开销巨大。编译标志是什么?您使用哪种优化级别?而且,这种大小的矩阵可能太小,无法进行并行处理。单线程运行需要多长时间?@DanielLangr我添加了一个更新1以提供更多信息。快速浏览:(1)通过遍历一个数组行主循环和另一个列主循环,您几乎无法编写更少的缓存友好访问模式。(2)每个循环的“计算负载”都是微不足道的。(3)
如果埋在深循环嵌套底部的
语句是以非常高的速率混淆分支预测的好方法。(4)
调度(动态,*small\u chunk\u size*)
可能是解决此问题的最糟糕的时间表。现在,对于您的应用程序来说,这些都是无法避免的,但您所编写的内容几乎是不使用OpenMP的教科书示例。如果(…)
在最内部循环中,您可以去掉
,并获得scra
CXX = icpc -std=c++11 -O3 -xHost -qopenmp
CXXFLAGS = -Wall -c -I${MKLROOT}/include
LDFLAGS  = -mkl=parallel
LDFLAGS += -L${MKLROOT}/lib/intel64_lin -Wl,-rpath,${MKLROOT}/lib/intel64_lin -lmkl_intel_lp64 -lmkl_intel_thread \
          -lmkl_core -liomp5 -lpthread
SOURCES = main.cpp TSAF_intel.cpp
EXECUTABLE = main_intel.exe