问一个并行计算的问题

l
llaalways
楼主 (未名空间)

没找到更合适的版面讨论这个问题,就在这里问吧.

我发现OpenMP的reduction效率很差,做同样的两个向量的内积,MPI要比OpenMP还快。MPI是分布内存,需要信息交换才能完成reduction。 结果比使用共享内存的OpenMP还
快。
这说明OpenMP的reduction没有搞好。我不知道是我使用的系统的问题还是普遍问题。
有人有类似的经验吗? 怎么解决?

我提供了一个code,然后再大家的反馈后做了修改。 现在把code也帖再这里。
省得大家爬楼。
现在的code 还是模仿dot product, 只不过 element都是compute on the fly.
所以code中没有大的数组,减少cache missing。
我还特意使用了一个sin function,
这样是的新的dot product 跟前面的 dot product的关系更复杂,
以免在element计算中的sum被优化掉。
这样做是为了让大家的注意力集中在reduction上。
请各位帮忙测试一下performance, 或者找到优化方法。多谢。

我的测试结果
OMP
sum = 6992.953984, time = 0.409850 sec, np = 1
sum = 6992.953984, time = 0.270875 sec, np = 2
sum = 6992.953984, time = 0.186024 sec, np = 4
sum = 6992.953984, time = 0.144010 sec, np = 8
sum = 6992.953984, time = 0.115188 sec, np = 16
sum = 6992.953984, time = 0.195485 sec, np = 32

MPI
sum = 6992.953984, time = 0.381701 sec, np = 1
sum = 6992.953984, time = 0.243513 sec, np = 2
sum = 6992.953984, time = 0.158326 sec, np = 4
sum = 6992.953984, time = 0.102489 sec, np = 8
sum = 6992.953984, time = 0.063975 sec, np = 16
sum = 6992.953984, time = 0.044748 sec, np = 32

很明显,MPI的reduction比OMP效率高,但我认为OMP应该能做到比MPI好。

这是MPI的code,

#include
#include
#include
#include
#include

int main(int argc, char* argv[])
{
int np, my_rank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &np);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

int n = 10000;
int repeat = 10000;

int sublength = (int)(ceil((double)(n) / (double)(np)));
int nstart = my_rank * sublength;
int nend = nstart + sublength;
if (nend >n )
{
nend = n;
sublength = nend - nstart;
}

double dot = 0;
double loc_dot = 0;
double sum = 1;

int j, k;
double t, time = -omp_get_wtime();
for (j = 0; j < repeat; j++)
{
for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}
MPI_Allreduce(&loc_dot, &dot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
sum += (dot/(double)(n));
loc_dot = 0;
}
time += omp_get_wtime();
MPI_Allreduce(&time, &t, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
if (my_rank == 0)
printf("sum = %f, time = %f sec, np = %d\n", sum, t, np);
return 0;
}

这是OMP的code
#include
#include
#include
#include

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

int n = 10000;
int repeat = 10000;

int np = 1;
if (argc > 1)
{
np = atoi(argv[1]);
}
omp_set_num_threads(np);

int nstart =0;
int sublength =n;

double loc_dot = 0;
double sum = 1;
#pragma omp parallel
{
int i, j, k;

double time = -omp_get_wtime();

for (j = 0; j < repeat; j++)
{
#pragma omp for reduction(+: loc_dot)
for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}
#pragma omp single
{
sum += (loc_dot/(double)(n));
loc_dot =0;
}
}
time += omp_get_wtime();
#pragma omp single nowait
printf("sum = %f, time = %f sec, np = %d\n", sum, time, np);
}

return 0;
}

 

🔥 最新回帖

g
guvest
149 楼

n=100,R=1.14。多核對小任務幾乎沒有提高。
n=10萬時,比數為3.51
n=百萬,比數為3.52

以100,1000,1萬,十萬,百萬的比數而言。規律似乎是明顯的。
【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: timeX = 24748,sum = 4663.721598, time = 0.024740 sec, np = 1
: timeX = 17010,sum = 4663.721598, time = 0.016904 sec, np = 2
: timeX = 14825,sum = 4663.721598, time = 0.013061 sec, np = 4
: timeX = 206982,sum = 4663.721598, time = 0.205362 sec, np = 8
: timeX = 658506,sum = 4663.721598, time = 0.656862 sec, np = 16
: timeX = 2003551,sum = 4663.721598, time = 2.003309 sec, np = 32
: timeX = 5061292,sum = 4663.721598, time = 5.060614 sec, np = 64
: 另外你看下n=1000,而不是一萬時,我機器的結果。
: 明顯4核心之提高遠小於n=10000的情況。
: 多線程最好的情況,其比數遠遠不到3.11。(此為n=1萬的比數。)

g
guvest
148 楼

timeX = 24748,sum = 4663.721598, time = 0.024740 sec, np = 1
timeX = 17010,sum = 4663.721598, time = 0.016904 sec, np = 2
timeX = 14825,sum = 4663.721598, time = 0.013061 sec, np = 4
timeX = 206982,sum = 4663.721598, time = 0.205362 sec, np = 8
timeX = 658506,sum = 4663.721598, time = 0.656862 sec, np = 16
timeX = 2003551,sum = 4663.721598, time = 2.003309 sec, np = 32
timeX = 5061292,sum = 4663.721598, time = 5.060614 sec, np = 64

另外你看下n=1000,而不是一萬時,我機器的結果。比率只有1.8.
明顯4核心之提高遠小於n=10000的情況。
多線程最好的情況,其比數遠遠不到3.11。(此為n=1萬的比數。)

【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 我覺得你的機器理論最快速度=單線程時間/核心數×調度損耗係數。
: 核心越多,調度損耗係數越大。我猜用別的問題可以測量出來調度損耗係數。
: 0.24是單線程之結果。我的4核心laptop此係數為4/3.2。
: 問題規模大,則越接近:
: 單線程時間/核心數×調度損耗係數

g
guvest
147 楼

我覺得你的機器理論最快速度=單線程時間/核心數×調度損耗係數。
核心越多,調度損耗係數越大。我猜用別的問題可以測量出來調度損耗係數。
0.24是單線程之結果。我的4核心laptop此係數為4/3.2。

問題規模大,則越接近:
單線程時間/核心數×調度損耗係數

【 在 llaalways (熊大) 的大作中提到: 】
: 已经实施了这个tree-base reduce,结果稍好一点,但还是不理想。
: 写code并调试,搞了一整天。
: np = 1 nt = 1 sum = 6992.953984, loop time = 0.246602 sec, total time = 0.: 261854
: np = 1 nt = 2 sum = 6992.953984, loop time = 0.150202 sec, total time = 0.: 165216
: np = 1 nt = 4 sum = 6992.953984, loop time = 0.101883 sec, total time = 0.: 116406
: np = 1 nt = 8 sum = 6992.953984, loop time = 0.070848 sec, total time = 0.: 085032
: ...................

l
llaalways
146 楼

已经实施了这个tree-base reduce,结果稍好一点,但还是不理想。
写code并调试,搞了一整天。
np = 1 nt = 1 sum = 6992.953984, loop time = 0.246602 sec, total time = 0.
261854
np = 1 nt = 2 sum = 6992.953984, loop time = 0.150202 sec, total time = 0.
165216
np = 1 nt = 4 sum = 6992.953984, loop time = 0.101883 sec, total time = 0.
116406
np = 1 nt = 8 sum = 6992.953984, loop time = 0.070848 sec, total time = 0.
085032
np = 1 nt = 16 sum = 6992.953984, loop time = 0.053922 sec, total time = 0.
070401
np = 1 nt = 32 sum = 6992.953984, loop time = 0.048791 sec, total time = 0.
104547

【 在 llaalways (熊大) 的大作中提到: 】
: 我正在写一个log(np)的code, 完全不需要barrier, 也不要隐式barrier.
: 希望能有更好的performance.
: 250978
: 164939
: 110154
: 080252
: 057083
: 109425

c
chebyshev
145 楼

哦。循環變量一次加8這個辦法我也試了。
確實有一致的提高。
【 在 bihai (学得不好) 的大作中提到: 】
: 就是循环内部进行8个乘法,而不是1个,那么循环变量要每次加8。这样做,循环次数
: 可以减少,那么循环变量增加且比较那个指令相对于加法就少了。这个技巧前面几个帖
: 子说过了。

 

🛋️ 沙发板凳

m
magagop

你用的什麼CPU?openmp只適合單機多cpu多核架構,不適合多機器架構。一般我們mpi
和openmp同時用,mpi用來跨機器,openmp是單機內部

l
llaalways

我是多機器架構,mpi和omp同时使用。但基本上只用mpi的效果往往超过同时使用。
所以我把问题简化成单机多cpu的情况, 可以直接对比mpi和omp。
也就是说,我只用多机中的一个share memory的node来测试。
【 在 magagop (magagop) 的大作中提到: 】
: 你用的什麼CPU?openmp只適合單機多cpu多核架構,不適合多機器架構。一般我們
mpi
: 和openmp同時用,mpi用來跨機器,openmp是單機內部

m
magagop

單機可以用Linux perf一下看看哪裡是瓶頸,event可以是cycle或者cache miss。如果是intel,可以vtune,如果是amd,可以uprof。Linux perf也可以看branch miss rate,先搞清楚是frontend還是backend pressure。

【 在 llaalways (熊大) 的大作中提到: 】
: 我是多機器架構,mpi和omp同时使用。但基本上只用mpi的效果往往超过同时使用。
: 所以我把问题简化成单机多cpu的情况, 可以直接对比mpi和omp。
: 也就是说,我只用多机中的一个share memory的node来测试。
: mpi

l
llaalways

机器是Intel(R) Xeon(R) Gold 6152 CPU @ 2.10GHz
OS是 CentOS-7.
没用过perf, 怎么用?
【 在 magagop (magagop) 的大作中提到: 】
: 單機可以用Linux perf一下看看哪裡是瓶頸,event可以是cycle或者cache miss。如果
: 是intel,可以vtune,如果是amd,可以uprof。Linux perf也可以看branch miss
rate
: ,先搞清楚是frontend還是backend pressure。

m
magagop
https://www.brendangregg.com/perf.html 如果了解CPU架構和PMU的需要一個月上手。
不了解CPU內部的,回去複習一下計算機微架構:https://en.wikichip.org/wiki/
intel/microarchitectures/skylake_(server)
估計一年後可以搞定。
l
llaalways

可以请帮忙在你的机器上测试一下MPI 和OMP的reduction的效率?
我给你提供code, 帮我测试一下1,2,4,8,16 个processors的运行时间。
看看是不是跟我得到的近似。多谢。
下面的code 在看到大家的回复后做了一些修改。
所以中间帖子里的timing可能不一致。但规律都是一样的

这是MPI的code,

#include
#include
#include
#include
#include

int main(int argc, char* argv[])
{
int np, my_rank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &np);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

int n = 10000;
int repeat = 10000;

int sublength = (int)(ceil((double)(n) / (double)(np)));
int nstart = my_rank * sublength;
int nend = nstart + sublength;
if (nend >n )
{
nend = n;
sublength = nend - nstart;
}

double dot = 0;
double loc_dot = 0;
double sum = 1;

int j, k;
double t, time = -omp_get_wtime();
for (j = 0; j < repeat; j++)
{
for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}
MPI_Allreduce(&loc_dot, &dot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
sum += (dot/(double)(n));
loc_dot = 0;
}
time += omp_get_wtime();
MPI_Allreduce(&time, &t, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
if (my_rank == 0)
printf("sum = %f, time = %f sec, np = %d\n", sum, t, np);
return 0;
}

这是OMP的code
#include
#include
#include
#include

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

int n = 10000;
int repeat = 10000;

int np = 1;
if (argc > 1)
{
np = atoi(argv[1]);
}
omp_set_num_threads(np);

int nstart =0;
int sublength =n;

double loc_dot = 0;
double sum = 1;
#pragma omp parallel
{
int i, j, k;

double time = -omp_get_wtime();

for (j = 0; j < repeat; j++)
{
#pragma omp for reduction(+: loc_dot)
for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}
#pragma omp single
{
sum += (loc_dot/(double)(n));
loc_dot =0;
}
}
time += omp_get_wtime();
#pragma omp single nowait
printf("sum = %f, time = %f sec, np = %d\n", sum, time, np);
}

return 0;
}

m
magagop

你建立一個github吧,把makefile也放上,我有時間試試
l
llaalways

不用makefile
这code只有一个文件。
假如mpi code文件名是 reduct_mpi.c
编译命令 mpiicc -qopenmp reduct_mpi.c -o reduct_mpi
执行方式 mpiexec -n 4 ./reduct_mpi
其中4是 number of processor, 可以改成 1、2、8、16等

假如omp code文件名是 reduct_omp.c
编译命令 mpiicc -qopenmp reduct_omp.c -o reduct_omp
执行方式 ./reduct_omp 4
其中4是 number of processor, 可以改成 1、2、8、16等

【 在 magagop (magagop) 的大作中提到: 】
: 你建立一個github吧,把makefile也放上,我有時間試試

n
netghost

你這個算dot product的,第一件事應該是去用blas的ddot,然後才去想並行的事。

【 在 llaalways (熊大) 的大作中提到: 】
: 可以请帮忙在你的机器上测试一下MPI 和OMP的reduction的效率?
: 我给你提供code, 帮我测试一下1,2,4,8,16 个processors的运行时间。
: 看看是不是跟我得到的近似。多谢。
: 这是MPI的code,
: #include
: #include
: #include
: #include
: #include
: int main(int argc, char* argv[])
: ...................

l
llaalways

这不是我实用的code,只是用了测试reduction的。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你這個算dot product的,第一件事應該是去用blas的ddot,然後才去想並行的事。

n
netghost

不是你要跑的code,調性能其實就沒什麼用,因爲並行和任務關係太大。

比如說你這個程序,你試着把n加兩個0,把repeat減兩個0你就知道我的意思了。

【 在 llaalways (熊大) 的大作中提到: 】
: 这不是我实用的code,只是用了测试reduction的。

l
llaalways

我知道。 所以我选的n有代表性, repeat选足够大来使得测试时间比较稳定。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 不是你要跑的code,調性能其實就沒什麼用,因爲並行和任務關係太大。
: 比如說你這個程序,你試着把n加兩個0,把repeat減兩個0你就知道我的意思了。

n
netghost

你有意選N讓CPU一件事情沒幹多久就停掉然後不停地create 線程?你最後基本上在測
create線程的速度,沒point吧。
【 在 llaalways (熊大) 的大作中提到: 】
: 我知道。 所以我选的n有代表性, repeat选足够大来使得测试时间比较稳定。

l
llaalways

这是我的最简单版本。我有一个改进版本,可以不用不停地create 線程,
看下面的code。因为稍复杂一些,所以没有在前帖中给出。
我需要的就是有很多dotproduct,但不是简单的loop。
想要对每个dotproduct 并行。结果OMP的效果没有MPI好。
这个改进版,比前面最简单的版本好一点,但还是不如MPI。
所以除了create线程的问题外,最主要的问题还是reduction。
如果去掉reduction,基本上运行时间可以达到 1/np

#include

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

int n = 10000;
int repeat = 100000;

int np = 1;
if (argc > 1)
{
np = atoi(argv[1]);
}
omp_set_num_threads(np);

int nstart =0;
int sublength =n;

double* a = malloc(sublength * sizeof(double));
double* b = malloc(sublength * sizeof(double));

double loc_dot = 0;
double sum = 0;
#pragma omp parallel
{
int i, j, k;
#pragma omp for
for (k = 0; k < sublength; k++)
{
i = k + nstart;
double temp = sin(i / 3.1415926 + 0.01);
a[k] = temp;
b[k] = temp;
}

double time = -omp_get_wtime();

for (j = 0; j < repeat; j++)
{
#pragma omp for reduction(+: loc_dot)
for (k = 0; k < sublength; k++)
{
loc_dot += a[k] * b[k];
}
#pragma omp single nowait
{
sum += loc_dot;
loc_dot =0;
}
}
time += omp_get_wtime();
#pragma omp single nowait
printf("sum = %f, time = %f sec, np = %dn", sum, time, np);
}

return 0;
}

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你有意選N讓CPU一件事情沒幹多久就停掉然後不停地create 線程?你最後基本上在測
: create線程的速度,沒point吧。

m
magagop

Linking error,你看看你用了什麼lib,需不需要lm
l
llaalways

我没有用特殊的lib,只load了一个module:intel/18.0.1
其他的head file 也可以不需要。只要下面这两个。
#include
#include

【 在 magagop (magagop) 的大作中提到: 】
: Linking error,你看看你用了什麼lib,需不需要lm

m
magagop

你的測試輸出時間不對,單機64核實際運行時間:
mpi是4.28秒,omp是1.44秒。
但是你輸出的mpi結果是0.61秒,omp是1.436秒。
perf顯示mpi佔用更多資源,只有2% frontend idle,相比之下omp有50%frontend idle,只有七分之一的branches,十分之一的context-switches。這個測試是frontend
bounded。
l
llaalways

我编译的时候需要这个option, -qopenmp
你可能要改成你编译openmp code需要的option 可能是 -fopenmp
还有 omp.h 所在的路径。其他就没什么需要注意的了

【 在 magagop (magagop) 的大作中提到: 】
: Linking error,你看看你用了什麼lib,需不需要lm

n
netghost

你具體貼一下你的時間唄。

但是你並沒有搞懂我的意思,你這個東西的關鍵點在於任務切太碎了,好比每個cpu沒
有餵飽就把碗拿走了。這個你不管用MPI還是OPENMP都沒用的。
【 在 llaalways (熊大) 的大作中提到: 】
: 这是我的最简单版本。我有一个改进版本,可以不用不停地create 線程,
: 看下面的code。因为稍复杂一些,所以没有在前帖中给出。
: 我需要的就是有很多dotproduct,但不是简单的loop。
: 想要对每个dotproduct 并行。结果OMP的效果没有MPI好。
: 这个改进版,比前面最简单的版本好一点,但还是不如MPI。
: 所以除了create线程的问题外,最主要的问题还是reduction。
: 如果去掉reduction,基本上运行时间可以达到 1/np
: #include
: int main(int argc, char* argv[])
: {
: ...................

l
llaalways

输出的是100Kdot product的时间
1 processor, mpi=0.396, omp1=0.537,omp2=0.523;
2 processors,mpi=0.309, omp1=0.430,omp2=0.399;
4 processors,mpi=0.231, omp1=0.375,omp2=0.339;
8 processors,mpi=0.167, omp1=0.378,omp2=0.305;
16 processors,mpi=0.163, omp1=0.595,omp2=0.357

你是说omp_get_wtime()测试不准吗?
omp_get_wtime()的resolution是 1E-7 秒。
【 在 magagop (magagop) 的大作中提到: 】
: 你的測試輸出時間不對,單機64核實際咝袝r間:
: mpi是4.28秒,omp是1.44秒。
: 但是你輸出的mpi結果是0.61秒,omp是1.436秒。
: perf顯示mpi佔用更多資源,只有2% frontend idle,相比之下omp有50%frontend
idle
: ,只有七分之一的branches,十分之一的context-switches。這個測試是frontend
: bounded。

l
llaalways

我知道这个问题,但我想要OMP至少可以达到MPI的速度。
这样我可以找到最优的thread number。
比如说同样是做10000个element的向量的dotproduct, MPI可以用到16个process,
但OMP最多只能用8个。再多速度就下降了。

我的code里有很多dot product,dot product可以并行,但dot product之间不能并行。

OMP在其他方面比MPI好,但reduction比较差,我只想知道是不是只有我遇到这个问题。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你具體貼一下你的時間唄。
: 但是你並沒有搞懂我的意思,你這個東西的關鍵點在於任務切太碎了,好比每個cpu沒
: 有餵飽就把碗拿走了。這個你不管用MPI還是OPENMP都沒用的。

l
llaalways

我也用 perf stat 输出运行时间。
程序执行总时间比我在程序里输出的时间稍多一点,看起来测试时间是对的。
比如
perf stat mpiexec -n 16 reduction_mpi
sum = 499907924.429987, time = 0.160385 sec, np = 16

Performance counter stats for 'mpiexec -n 16 reduction_mpi':

2,970.27 msec task-clock:u # 12.556 CPUs utilized
0 context-switches:u # 0.000 K/sec
0 cpu-migrations:u # 0.000 K/sec
38,117 page-faults:u # 0.013 M/sec
5,232,710,936 cycles:u # 1.762 GHz
3,932,532,053 instructions:u # 0.75 insn per cycle
549,130,804 branches:u # 184.876 M/sec
2,682,456 branch-misses:u # 0.49% of all branches

0.236558883 seconds time elapsed

2.684250000 seconds user
0.297384000 seconds sys

这是16核MPI, 总时间是0.236558883 seconds, 我的code输出100K内积时间是0.
160385 sec

perf stat reduction_omp 16
sum = 499907924.429987, time = 0.446216 sec, np = 16

Performance counter stats for 'reduction_omp 16':

7,133.38 msec task-clock:u # 15.584 CPUs utilized
0 context-switches:u # 0.000 K/sec
0 cpu-migrations:u # 0.000 K/sec
820 page-faults:u # 0.115 K/sec
19,093,137,129 cycles:u # 2.677 GHz
6,447,752,697 instructions:u # 0.34 insn per cycle
1,264,314,844 branches:u # 177.239 M/sec
3,294,979 branch-misses:u # 0.26% of all branches

0.457738534 seconds time elapsed

7.131633000 seconds user
0.005982000 seconds sys

这是16核OMP, 总时间是0.457738534 seconds, 我的code输出100K内积时间是0.
446216 sec

不知道你说的实际时间是怎么测得的.
【 在 llaalways (熊大) 的大作中提到: 】
: 输出的是100Kdot product的时间
: 1 processor, mpi=0.396, omp1=0.537,omp2=0.523;
: 2 processors,mpi=0.309, omp1=0.430,omp2=0.399;
: 4 processors,mpi=0.231, omp1=0.375,omp2=0.339;
: 8 processors,mpi=0.167, omp1=0.378,omp2=0.305;
: 16 processors,mpi=0.163, omp1=0.595,omp2=0.357
: 你是说omp_get_wtime()测试不准吗?
: omp_get_wtime()的resolution是 1E-7 秒。
: idle

l
llaalways

请在64核的机子上测一下只用32核或更少核的时间。
用满64核可能performence会很差。
另外,你测的OMP是我15楼的改进版本吗?
【 在 magagop (magagop) 的大作中提到: 】
: 你的測試輸出時間不對,單機64核實際咝袝r間:
: mpi是4.28秒,omp是1.44秒。
: 但是你輸出的mpi結果是0.61秒,omp是1.436秒。
: perf顯示mpi佔用更多資源,只有2% frontend idle,相比之下omp有50%frontend
idle
: ,只有七分之一的branches,十分之一的context-switches。這個測試是frontend
: bounded。

g
guvest

你這個程序。跑一百次得到的時間之波動大嗎?

若我計時。會紀錄一個開始時間,一個結束時間。
中間loop一萬次。然後 時間/10k。

此法比簡單強壯。不涉及計算時間之overhead等細節。
【 在 llaalways(熊大) 的大作中提到: 】
<br>: 我也用 perf stat 输出运行时间。
<br>: 程序执行总时间比我在程序里输出的时间稍多一点,看起来测试时间是对的。
<br>: 比如
<br>: perf stat mpiexec -n 16 reduction_mpi
<br>: sum = 499907924.429987, time = 0.160385 sec, np = 16
<br>: Performance counter stats for 'mpiexec -n 16 reduction_mpi':
<br>: 2,970.27 msec task-clock:u # 12.556
CPUs
utilized
<br>: 0 context-switches:u # 0.000 K/
sec
<br>: 0 cpu-migrations:u # 0.000 K/
sec
<br>: 38,117 page-faults:u # 0.013 M/
sec
: ...................
<br>

n
netghost

你這個的n和repeat是你程序里的?
應該不是gcc的編譯吧。
【 在 llaalways (熊大) 的大作中提到: 】
: 输出的是100Kdot product的时间
: 1 processor, mpi=0.396, omp1=0.537,omp2=0.523;
: 2 processors,mpi=0.309, omp1=0.430,omp2=0.399;
: 4 processors,mpi=0.231, omp1=0.375,omp2=0.339;
: 8 processors,mpi=0.167, omp1=0.378,omp2=0.305;
: 16 processors,mpi=0.163, omp1=0.595,omp2=0.357
: 你是说omp_get_wtime()测试不准吗?
: omp_get_wtime()的resolution是 1E-7 秒。
: idle

l
llaalways

波动比较大,std/avg 大约能到10%。
我已经loop了100K次了,可以改成1M次,运行时间也就是几秒钟。
立等可取
【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 你這個程序。跑一百次得到的時間之波動大嗎?
: 若我計時。會紀錄一個開始時間,一個結束時間。
: 中間loop一萬次。然後 時間/10k。
:
: 我也用 perf stat 输出运行时间。
:
: 程序执行总时间比我在程序里输出的时间稍多一点,看起来测试时间是对
: 的。
:
: 比如
:
: perf stat mpiexec -n 16 reduction_mpi
:
: sum = 499907924.429987, time = 0.160385 sec, np = 16
:
: Performance counter stats for 'mpiexec -n 16 reduction_mpi':
: ...................

l
llaalways

n是我要用的size, repeat只是用来得到可靠的测试时间。
在我实用的程序里也会出现100K次,只是每次数据都不一样。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你這個的n和repeat是你程序里的?
: 應該不是gcc的編譯吧。

n
netghost

大部分人沒有理解並行計算需要的不是找到最優的thread number,而是需要找到最優
的工作量。像你這種cpu bound的計算,工作量找對了應該一直是linear scale的。

你把10000改成1000000,你就知道我說的是啥了。

【 在 llaalways (熊大) 的大作中提到: 】
: 我知道这个问题,但我想要OMP至少可以达到MPI的速度。
: 这样我可以找到最优的thread number。
: 比如说同样是做10000个element的向量的dotproduct, MPI可以用到16个process,
: 但OMP最多只能用8个。再多速度就下降了。
: 我的code里有很多dot product,dot product可以并行,但dot product之间不能并
行。
: OMP在其他方面比MPI好,但reduction比较差,我只想知道是不是只有我遇到这个问
题。

g
guvest

不用perf,就naive start time , end time呢?
可能你得先把波動原因找到。這可能是計算時間的問題。
若單次時間波動大。則結論是否沒啥意義。

【 在 llaalways(熊大) 的大作中提到: 】
<br>: 波动比较大,std/avg 大约能到10%。
<br>: 我已经loop了100K次了,可以改成1M次,运行时间也就是几秒钟。
<br>: 立等可取
<br>

l
llaalways

并行计算找最优工作量才是搞计算的人骗钱的伎俩。
为了并行效率而增加工作量毫无意义。

现在问题是工作量是固定的,假设你有足够的计算资源,怎么能算的最快。
或者在现有的计算资源条件下,怎么算的最快。
而不是任意增加工作量。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 大部分人沒有理解並行計算需要的不是找到最優的thread number,而是需要找到最優
: 的工作量。像你這種cpu bound的計算,工作量找對了應該一直是linear scale的。
: 你把10000改成1000000,你就知道我說的是啥了。
: 行。
: 题。

g
guvest

38,117 page-faults:u # 0.013 M/sec

page faults應是各項裡最cost的。改下數組size應能提速。

【 在 llaalways(熊大) 的大作中提到: 】

: 我也用 perf stat 输出运行时间。

: 程序执行总时间比我在程序里输出的时间稍多一点,看起来测试时间是对的。

: 比如

: perf stat mpiexec -n 16 reduction_mpi

: sum = 499907924.429987, time = 0.160385 sec, np = 16

: Performance counter stats for 'mpiexec -n 16 reduction_mpi':

: 2,970.27 msec task-clock:u # 12.556 CPUs
utilized

: 0 context-switches:u # 0.000 K/sec

: 0 cpu-migrations:u # 0.000 K/sec

: 38,117 page-faults:u # 0.013 M/sec
: ...................

l
llaalways

并行计算中的波动时不可避免的。
我觉得10%还好。我现在时100K次平均值还有10%的波动,
单次的波动可能更大。
但总体规律还是有用的。
【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 不用perf,就naive start time , end time呢?
: 可能你得先把波動原因找到。這可能是計算時間的問題。
: 若單次時間波動大。則結論是否沒啥意義。
:
: 波动比较大,std/avg 大约能到10%。
:
: 我已经loop了100K次了,可以改成1M次,运行时间也就是几秒钟。
:
: 立等可取
:

g
guvest

給定硬件,如何計算最快?
這問題實際上是不確定的。沒有固定答案。原因是
現有之OS里有大批的啟發式算法。開機硬件就根據硬件情況建立各種表格/數據結構。
例如計算物理內存地址之用的表格。然後後續軟件還會更新這些東西的。

極大數組,極小數組。相對來說結果一致性會好些。

【 在 llaalways(熊大) 的大作中提到: 】

: 并行计算找最优工作量才是搞计算的人骗钱的伎俩。

: 为了并行效率而增加工作量毫无意义。

: 现在问题是工作量是固定的,假设你有足够的计算资源,怎么能算的最快。

: 或者在现有的计算资源条件下,怎么算的最快。

: 而不是任意增加工作量。

g
guvest

Conclusion的一致性如何?是否永遠一法好過另一法?

【 在 llaalways(熊大) 的大作中提到: 】

: 并行计算中的波动时不可避免的。

: 我觉得10%还好。我现在时100K次平均值还有10%的波动,

: 单次的波动可能更大。

: 但总体规律还是有用的。

l
llaalways

数组的size变化没有意义。
但是可以改变omp for schedule, 也就是各个thread处理的k值范围。
也就是把数组分成块。

但目前用缺省的好像最优。

实际上, 通过我的测试page missing并不是最大问题,问题在于reduction,
如果我去掉reduction, 马上变成linear scalable。
【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 38,117 page-faults:u # 0.013 M/sec
: page faults應是各項裡最cost的。改下數組size應能提速。
:
: 我也用 perf stat 输出运行时间。
:
: 程序执行总时间比我在程序里输出的时间稍多一点,看起来测试时间是对的。:
: 比如
:
: perf stat mpiexec -n 16 reduction_mpi
:
: sum = 499907924.429987, time = 0.160385 sec, np = 16
:
: Performance counter stats for 'mpiexec -n 16 reduction_mpi':
:
: 2,970.27 msec task-clock:u # 12.556 CPUs
: utilized
: ...................

g
guvest

數組尺寸會影響memory page 算法。影響到access 數組元素之速度。
小程序應能測出是否問題在於access time。

【 在 llaalways(熊大) 的大作中提到: 】
<br>: 数组的size变化没有意义。
<br>: 但是可以改变omp for schedule, 也就是各个thread处理的k值范围。
<br>: 也就是把数组分成块。
<br>: 但目前用缺省的好像最优。
<br>

l
llaalways

是的。但你刚才看见的有很多page fault 是MPI,OMP 的page fault 很少,但还是比
MPI慢,原因就是reduction没做好。
【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 數組尺寸會影響memory page 算法。影響到access 數組元素之速度。
: 小程序應能測出是否問題在於access time。
:
: 数组的size变化没有意义。
:
: 但是可以改变omp for schedule, 也就是各个thread处理的k值范围。
:
: 也就是把数组分成块。
:
: 但目前用缺省的好像最优。
:

m
magagop

64核 2.2-2.7GHz 單路CPU,openmpi 4.0.6 with ucx,Ubuntu 20.04.2
mpi時間沒有加入系統啟動時間,sys wall clock實際顯示mpi更慢。

mpi perf time / mpi time:
1 4.87秒 / 2.67秒
2 3.57秒 / 1.36秒
4 2.00秒 / 0.79秒
8 0.85秒 / 0.57秒
16 0.97秒 / 0.47秒
24 3.13秒 / 0.47秒
32 3.30秒 / 0.46秒
48 3.77秒 / 0.52秒
64 4.21秒 / 0.61秒

omp perf time:
1 2.68秒
2 1.46秒
4 0.93秒
8 0.72秒
16 0.93秒
24 1.00秒
32 0.98秒
48 1.18秒
64 1.40秒

omp2 perf time:
1 2.68秒
2 1.39秒
4 0.77秒
8 0.53秒
16 0.52秒
24 0.48秒
32 0.49秒
48 0.50秒
64 0.57秒
j
jmrf

用全部核performence变差很可能是schedule有问题, 不同的schedule对速度影响很大
intel的编译器曾经需要强制
export KMP_AFFINITY=compact
如果用满64核时,仍有cpu是idle的,就说明这个问题还存在。

openmp 默认的schedule 会disalbe SIMD, 而你的代码明显可以SIMD加速

【 在 llaalways (熊大) 的大作中提到: 】
: 请在64核的机子上测一下只用32核或更少核的时间。
: 用满64核可能performence会很差。
: 另外,你测的OMP是我15楼的改进版本吗?
: idle

j
jmrf

n太小,也就够喂报2个线程,线程多了以后就是线程之间在抢资源。
【 在 magagop (magagop) 的大作中提到: 】
: 64核 2.2-2.7GHz 單路CPU,openmpi 4.0.6 with ucx,Ubuntu 20.04.2
: mpi時間沒有加入系統啟動時間,sys wall clock實際顯示mpi更慢。
: mpi perf time / mpi time:
: 1 4.87秒 / 2.67秒
: 2 3.57秒 / 1.36秒
: 4 2.00秒 / 0.79秒
: 8 0.85秒 / 0.57秒
: 16 0.97秒 / 0.47秒
: 24 3.13秒 / 0.47秒
: 32 3.30秒 / 0.46秒
: ...................

l
llaalways

我测的时间不太一样
mpi perf time / dotp time /perf-dotp time:
1 0.485秒 / 0.418秒 /0.067
2 0.325秒 / 0.267秒 /0.058
4 0.282秒 / 0.233秒 /0.049
8 0.225秒 / 0.165秒 /0.060
16 0.253秒 / 0.170秒 /0.083
32 0.284秒 / 0.188秒 /0.096

OMP2 perf time / dotp time:
1 0.473秒 / 0.460秒 /0.013
2 0.386秒 / 0.373秒 /0.013
4 0.338秒 / 0.325秒 /0.013
8 0.372秒 / 0.358秒 /0.014
16 0.538秒 / 0.512秒 /0.016
32 0.683秒 / 0.662秒 /0.021

MPI的dotproduct之外的时间确实比OMP多,但不影响结论。
MPI是8核时间最短,OMP是4核。
其实上dotproduct之外的时间对我的研究没影响。
因为其他还有很多并行效率较高的工作量。我都省略了。
只研究dot product,特别是reduction的效率。

你的运行时间有两点比较奇怪。
第一: 时间基本上是我的运行时间的5倍, 但机器的配置差不多。
第二: MPI的启动时间在8核以下基本跟dotproduct时间相当。而我的只有1/6,左右。

我怀疑跟编译系统有关。 我用的是 intel 编译系统 18.0.1.
你测的时间一个好的地方是OMP一直到24核时间还在减少,而我机器上8核就回头了。

下面是机器的配置
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 44
On-line CPU(s) list: 0-43
Thread(s) per core: 1
Core(s) per socket: 22
Socket(s): 2
NUMA node(s): 2
Vendor ID: GenuineIntel
CPU family: 6
Model: 85
Model name: Intel(R) Xeon(R) Gold 6152 CPU @ 2.10GHz
Stepping: 4
CPU MHz: 1000.012
CPU max MHz: 3700.0000
CPU min MHz: 1000.0000
BogoMIPS: 4200.00
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 30976K

【 在 magagop (magagop) 的大作中提到: 】
: 64核 2.2-2.7GHz 單路CPU,openmpi 4.0.6 with ucx,Ubuntu 20.04.2
: mpi時間沒有加入系統啟動時間,sys wall clock實際顯示mpi更慢。
: mpi perf time / mpi time:
: 1 4.87秒 / 2.67秒
: 2 3.57秒 / 1.36秒
: 4 2.00秒 / 0.79秒
: 8 0.85秒 / 0.57秒
: 16 0.97秒 / 0.47秒
: 24 3.13秒 / 0.47秒
: 32 3.30秒 / 0.46秒
: ...................

l
llaalways

你用的是gcc + openmpi吗?
【 在 magagop (magagop) 的大作中提到: 】
: 64核 2.2-2.7GHz 單路CPU,openmpi 4.0.6 with ucx,Ubuntu 20.04.2
: mpi時間沒有加入系統啟動時間,sys wall clock實際顯示mpi更慢。
: mpi perf time / mpi time:
: 1 4.87秒 / 2.67秒
: 2 3.57秒 / 1.36秒
: 4 2.00秒 / 0.79秒
: 8 0.85秒 / 0.57秒
: 16 0.97秒 / 0.47秒
: 24 3.13秒 / 0.47秒
: 32 3.30秒 / 0.46秒
: ...................

n
netghost

你完全沒搞明白我的意思。

工作總量不變,每個分片多大是關鍵,懂了?

還是那句話,你把你的每個分片加大,repeat減少,你的工作總量不會變,就知道我的意思了。
【 在 llaalways (熊大) 的大作中提到: 】
: 并行计算找最优工作量才是搞计算的人骗钱的伎俩。
: 为了并行效率而增加工作量毫无意义。
: 现在问题是工作量是固定的,假设你有足够的计算资源,怎么能算的最快。
: 或者在现有的计算资源条件下,怎么算的最快。
: 而不是任意增加工作量。

n
netghost

你要真care這個,就應該先回到如何一個CPU跑得更快的事情上面來。

sum = 499999156.515837, time = 0.028685 sec, np = 1n

這個是我用一個core,一模一樣的cpu跑你的程序的結果。

【 在 llaalways (熊大) 的大作中提到: 】
: 并行计算找最优工作量才是搞计算的人骗钱的伎俩。
: 为了并行效率而增加工作量毫无意义。
: 现在问题是工作量是固定的,假设你有足够的计算资源,怎么能算的最快。
: 或者在现有的计算资源条件下,怎么算的最快。
: 而不是任意增加工作量。

l
llaalways

都说了,能并行的就是一个个的dot product. 怎么增大。能增大早就做了。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你完全沒搞明白我的意思。
: 工作總量不變,每個分片多大是關鍵,懂了?
: 還是那句話,你把你的每個分片加大,repeat減少,你的工作總量不會變,就知道我的
: 意思了。

n
netghost

OK,首先知道能增大是關鍵,我們再說怎麼增大。

第一,我還是建議你先優化單CPU,沒有這個別的都是扯蛋的。

第二,你這個情況需要另外寫一個pipeline來sched你的work,把你的dot product的任務都合併掉。

實際上爲什麼MPI再你的情況下會快,我most likely覺得是因爲MPI implicitly做了合併工作,屬於用延遲換帶寬的做法。但是畢竟是亂撞的結果,所以和有意識的調整還是有很大差距的。
【 在 llaalways (熊大) 的大作中提到: 】
: 都说了,能并行的就是一个个的dot product. 怎么增大。能增大早就做了。

l
llaalways

我用gcc 得到了跟你的类似的结果, 但比intel 慢。
gcc compiler
OMP2 perf time / dotp time:
1 2.65秒 / 2.64秒
2 1.60秒 / 1.59秒
4 0.877秒 / 0.870秒
8 0.688秒 / 0.680秒
16 0.594秒 / 0.587秒
32 0.846秒 / 0.837秒

intel compiler
OMP2 perf time / dotp time:
1 0.473秒 / 0.460秒 /0.013
2 0.386秒 / 0.373秒 /0.013
4 0.338秒 / 0.325秒 /0.013
8 0.372秒 / 0.358秒 /0.014
16 0.538秒 / 0.512秒 /0.016
32 0.683秒 / 0.662秒 /0.021
【 在 magagop (magagop) 的大作中提到: 】
: 64核 2.2-2.7GHz 單路CPU,openmpi 4.0.6 with ucx,Ubuntu 20.04.2
: mpi時間沒有加入系統啟動時間,sys wall clock實際顯示mpi更慢。
: mpi perf time / mpi time:
: 1 4.87秒 / 2.67秒
: 2 3.57秒 / 1.36秒
: 4 2.00秒 / 0.79秒
: 8 0.85秒 / 0.57秒
: 16 0.97秒 / 0.47秒
: 24 3.13秒 / 0.47秒
: 32 3.30秒 / 0.46秒
: ...................

n
netghost

你如果搞懂我的意思,你看到的應該是這個。還是和你一模一樣的CPU。這個並沒有對
單CPU做優化,只是修改掉調度單位而已。

./b sum = 499999156.515833, time = 1.436360 sec, np = 1n
./b 2 sum = 499999156.515837, time = 0.719677 sec, np = 2n
./b 4 sum = 499999156.515837, time = 0.360384 sec, np = 4n
./b 8 sum = 499999156.515838, time = 0.182354 sec, np = 8n
./b 16 sum = 499999156.515837, time = 0.093795 sec, np = 16n
./b 32 sum = 499999156.515837, time = 0.053239 sec, np = 32n

此外,你應該用了Intel的編譯器吧,這個優化做還是很好的,但是抵不過你調度開銷
。所以一比擴展性你就沒優勢了。

【 在 llaalways (熊大) 的大作中提到: 】
: 我测的时间不太一样
: mpi perf time / dotp time /perf-dotp time:
: 1 0.485秒 / 0.418秒 /0.067
: 2 0.325秒 / 0.267秒 /0.058
: 4 0.282秒 / 0.233秒 /0.049
: 8 0.225秒 / 0.165秒 /0.060
: 16 0.253秒 / 0.170秒 /0.083
: 32 0.284秒 / 0.188秒 /0.096
: OMP2 perf time / dotp time:
: 1 0.473秒 / 0.460秒 /0.013
: ...................

j
jmrf

如果就是dot product你就应该用库,而不是自己写。里面涉及的寄存器,一级缓存,
二级缓存,SIMD, 都不是新手搞得定的。

我收回我的建议,dot product太简单了,SIMD手工优化可以比ATLAS好https://blog.theincredibleholk.org/blog/2012/12/10/optimizing-dot-product/
【 在 llaalways (熊大) 的大作中提到: 】
: 都说了,能并行的就是一个个的dot product. 怎么增大。能增大早就做了。

m
magagop

我不是intel的cpu,所以不能用icc 18.0.1,我用的是gcc 9.3.0 + avx2 + openmpi 4.0.6在ubuntu 20.04.2上,不是centos7。實際咝袝r間跟很多因素相關,但我htop看的結果是mpi需要幾秒鐘調用大量syscalls和kernel space處理,而omp完全是user space。基本上64線程都單獨使用一個核心,沒有碰撞,這點mpi和omp一樣。

服務器cpu核心越少單核turbo頻率越高。我的單核最高2.7GHz,你的單核最高3.7GHz,但是全核我的2.2GHz,你的2.1GHz,這個解釋了為啥你的4-8核就到頂峰,而我的16-24核才到頂峰。

我有機會再用GPU跑跑看。

【 在 llaalways (熊大) 的大作中提到: 】
: 我用gcc 得到了跟你的类似的结果, 但比intel 慢。
: gcc compiler
: OMP2 perf time / dotp time:
: 1 2.65秒 / 2.64秒
: 2 1.60秒 / 1.59秒
: 4 0.877秒 / 0.870秒
: 8 0.688秒 / 0.680秒
: 16 0.594秒 / 0.587秒
: 32 0.846秒 / 0.837秒
: intel compiler
: ...................

c
chebyshev

程序裡的計算時間的方法似有問題?

不如將malloc放在repeat loop裡面。
最後你再free。在malloc與free之間的時間點不用變。

如此,每次repeat loop裡面取a[k],b[k]計算時,
OS與機器之狀態相對乾淨。

你現在的計算時間方法似乎有兩個缺點:
第一,
因為各種調度算法均有記憶效果(各種調度用的數據結構裡會保留用了多次的東
西,或者most recent計算用到的一些東西)。
這會造成第一次repeat慢。後面可以越來越快(這點應可以簡單測試出來)。

第二,
你下面的計算時間方法不切合真實應用的實際情況。
真實應用時,os與機器應無任務之歷史記憶。

【 在 llaalways (熊大) 的大作中提到: 】
: 可以请帮忙在你的机器上测试一下MPI 和OMP的reduction的效率?
: 我给你提供code, 帮我测试一下1,2,4,8,16 个processors的运行时间。
: 看看是不是跟我得到的近似。多谢。
: 这是MPI的code,
: #include
: #include
: int main(int argc, char* argv[])
: {
: int np, my_rank;
: MPI_Init(&argc, &argv);
: ...................

l
llaalways

你的單CPU優化怎么做的?

另外,你试试下面程序。看看單CPU優化或修改掉調度單位管用吗?

c
chebyshev

我看還是用庫吧。這個link似乎只考慮了數據已經到了cpu之cache之後的事情。

【 在 jmrf (daimao) 的大作中提到: 】
: 如果就是dot product你就应该用库,而不是自己写。里面涉及的寄存器,一级缓存,
: 二级缓存,SIMD, 都不是新手搞得定的。
: 我收回我的建议,dot product太简单了,SIMD手工优化可以比ATLAS好
: https://blog.theincredibleholk.org/blog/2012/12/10/optimizing-dot-product/

c
chebyshev

我還是覺得應把
for (j = 0; j < repeat; j++)
放在最前面。不然每次repeat你已經借用了之前一次循環所建立之各種OS狀態。
【 在 llaalways (熊大) 的大作中提到: 】
: 你的單CPU優化怎么做的?
: 另外,你试试下面程序。看看單CPU優化或修改掉調度單位管用吗?
: #include
: #include
: #include
: #include
: int main(int argc, char* argv[])
: {
: int n = 10000;
: int repeat = 10000;
: ...................

n
netghost

單CPU就是用blas.像別人說的,這種簡單的手動匯編也不難。

你這個程序我沒看懂想幹嘛,所以不便評論。但是你這個程序原封不動跑了一下,似乎線程數不等的情況下結果不一致。

sum = 49973945.014750, time = 0.524683 sec, np = 1
sum = 49987748.554291, time = 0.268209 sec, np = 2
sum = 49985725.078290, time = 0.142735 sec, np = 4
sum = 49993121.367840, time = 0.090428 sec, np = 8
sum = 49992154.040189, time = 0.086849 sec, np = 16
sum = 49990524.608644, time = 0.106662 sec, np = 32

一般來說如果是比較複雜的程序用MPI或者OPENMP最好是針對程序的某個sequential
pipeline裏面的一個環節只拆一次,比如說程序有P1,P2,P3三個環節組成,P2必須要P1完成才能繼續,那P1用MPI這種調度最好只拆一次,你這個拆得有點太細了,overhead
太大。除非你手動調度線程不依賴MPI和OPENMP這種大路貨。

上述情況一般只適用於CPU bound的任務,如果是I/O bound,還要複雜很多。

【 在 llaalways (熊大) 的大作中提到: 】
: 你的單CPU優化怎么做的?
: 另外,你试试下面程序。看看單CPU優化或修改掉調度單位管用吗?
: #include
: #include
: #include
: #include
: int main(int argc, char* argv[])
: {
: int n = 10000;
: int repeat = 10000;
: ...................

l
llaalways

结果不一致是因为round off error.
我稍改一下,下面这个程序中round off error不影响结果。
重点是每个repeat循环都跟前一循环的结果相关,
所以不能把reduction集中在一起。
我的目的就是要test reduction peformance。
#include
#include
#include
#include

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

int n = 10000;
int repeat = 10000;

int np = 1;
if (argc > 1)
{
np = atoi(argv[1]);
}
omp_set_num_threads(np);

int nstart =0;
int sublength =n;

double loc_dot = 0;
double sum = 1;
#pragma omp parallel
{
int i, j, k;

double time = -omp_get_wtime();

for (j = 0; j < repeat; j++)
{
#pragma omp for reduction(+: loc_dot)
for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}
#pragma omp single
{
sum += (loc_dot/(double)(n));
loc_dot =0;
}
}
time += omp_get_wtime();
#pragma omp single nowait
printf("sum = %f, time = %f sec, np = %d\n", sum, time, np);
}

return 0;
}

l
llaalways

sum = 15774.047975, time = 0.131546 sec, np = 1
sum = 15774.047975, time = 0.096653 sec, np = 2
sum = 15774.047975, time = 0.076053 sec, np = 4
sum = 15774.047975, time = 0.077671 sec, np = 8
sum = 15774.047975, time = 0.128394 sec, np = 16
sum = 15774.047975, time = 0.192070 sec, np = 32

【 在 llaalways (熊大) 的大作中提到: 】
: 结果不一致是因为round off error.
: 我稍改一下,下面这个程序中round off error不影响结果。
: 重点是每个repeat循环都跟前一循环的结果相关,
: 所以不能把reduction集中在一起。
: 我的目的就是要test reduction peformance。
: #include
: #include
: #include
: #include
: int main(int argc, char* argv[])
: ...................

C
Caravel

你这个非常适合SIMD的运算,比如INTEL AVX,AVX512,你试用过MKL么?

【 在 llaalways (熊大) 的大作中提到: 】
: sum = 15774.047975, time = 0.131546 sec, np = 1
: sum = 15774.047975, time = 0.096653 sec, np = 2
: sum = 15774.047975, time = 0.076053 sec, np = 4
: sum = 15774.047975, time = 0.077671 sec, np = 8
: sum = 15774.047975, time = 0.128394 sec, np = 16
: sum = 15774.047975, time = 0.192070 sec, np = 32

g
guvest

我的理解是。他不完全是想找最优解。主要是用简单的例子测一下openMP之reduction clause。

看其属于下面哪个范畴:
A. 很烂。无用
B. 有用。但是不知如何选参数以及设计程序

一个重要的问题是,为何求内积这样的简单操作。为何线程增加之后,reduction
clause性能没有继续增加?我猜是调度之overhead太高了。

所以我有个猜想,假如单机用Golang求内积,因为其调度粒度更细。可能更线性些。例如下面求卷积的代码,可改为内积跑跑看。
https://yourbasic.org/golang/efficient-parallel-computation/

那种语言之并行计算,可以有更线性?
这是个很有意思的问题。
等改天我有空单机单cpu比较下看看。除了go,
也可与java比较下看看。

【 在 Caravel(克拉维尔) 的大作中提到: 】
<br>: 你这个非常适合SIMD的运算,比如INTEL AVX,AVX512,你试用过MKL么?<br>

l
llaalways

我重新修改了57楼的code,
现在的code 还是模仿dot product, 只不过 element都是compute on the fly.
所以code中没有大的数组,减少cache missing。
我还特意使用了一个sin function,
这样是的新的dot product 跟前面的 dot product的关系更复杂,
以免在element计算中的sum被优化掉。
这样做是为了让大家的注意力集中在reduction上。
请各位帮忙测试一下performance, 或者找到优化方法。多谢。

我的测试结果
OMP
sum = 6992.953984, time = 0.409850 sec, np = 1
sum = 6992.953984, time = 0.270875 sec, np = 2
sum = 6992.953984, time = 0.186024 sec, np = 4
sum = 6992.953984, time = 0.144010 sec, np = 8
sum = 6992.953984, time = 0.115188 sec, np = 16
sum = 6992.953984, time = 0.195485 sec, np = 32

MPI
sum = 6992.953984, time = 0.381701 sec, np = 1
sum = 6992.953984, time = 0.243513 sec, np = 2
sum = 6992.953984, time = 0.158326 sec, np = 4
sum = 6992.953984, time = 0.102489 sec, np = 8
sum = 6992.953984, time = 0.063975 sec, np = 16
sum = 6992.953984, time = 0.044748 sec, np = 32

很明显,MPI的reduction比OMP效率高,但我认为OMP应该能做到比MPI好。

【 在 llaalways (熊大) 的大作中提到: 】
: 结果不一致是因为round off error.
: 我稍改一下,下面这个程序中round off error不影响结果。
: 重点是每个repeat循环都跟前一循环的结果相关,
: 所以不能把reduction集中在一起。
: 我的目的就是要test reduction peformance。
: #include
: #include
: #include
: #include
: int main(int argc, char* argv[])
: ...................

l
llaalways

你说的对, 我的主贴就在问reduction效率问题。
可是大家都focus on怎么优化repeated dot-product。
我最初的code设计的不好,太容易被优化了,使得大家都不管我在关心什么。
最初的code中每次dot product 都是完全重复的,数值多是一样。
所以在完全优化后只用做一次dot product 后乘以repeat就可以了。
我重新设计了code。 可能还是成在优化的可能,但已经不明显了。

要求OMP的reduction 比MPI有效应该不是无理要求吧?

【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 我的理解是。他不完全是想找最优解。主要是用简单的例子测一下openMP之
reduction
: clause。
: 看其属于下面哪个范畴:
: A. 很烂。无用
: B. 有用。但是不知如何选参数以及设计程序
: 一个重要的问题是,为何求内积这样的简单操作。为何线程增加之后,reduction
: clause性能没有继续增加?我猜是调度之overhead太高了。
: 所以我有个猜想,假如单机用Golang求内积,因为其调度粒度更细。可能更线性些。例
: 如下面求卷积的代码,可改为内积跑跑看。
: https://yourbasic.org/golang/efficient-parallel-computation/
: ...................

n
netghost

你肯定你的實際運算真的是你下面這個邏輯還是你人爲造出來的case?你這個等於一個強相關的算法,本來並行效果就不會好的。

【 在 llaalways (熊大) 的大作中提到: 】
: 结果不一致是因为round off error.
: 我稍改一下,下面这个程序中round off error不影响结果。
: 重点是每个repeat循环都跟前一循环的结果相关,
: 所以不能把reduction集中在一起。
: 我的目的就是要test reduction peformance。
: #include
: #include
: #include
: #include
: int main(int argc, char* argv[])
: ...................

B
BacktoMars

OMP和MPI相差这么大,很可能是CPU affinity的问题。对于Intel OMP,试试
把环境变量KMP_AFFINITY设成scatter。
另外,一般的MPI implementation都对intra node有所优化,大部分是用了shared
memory,所以在你的单机上效率应该和OMP差不多。如果有明显差别,要么是时间计算
不一样(比如是否考虑了各种overhead),要么是scheduler的问题(比如affinity)。
【 在 llaalways (熊大) 的大作中提到: 】
: 你说的对, 我的主贴就在问reduction效率问题。
: 可是大家都focus on怎么优化repeated dot-product。
: 我最初的code设计的不好,太容易被优化了,使得大家都不管我在关心什么。
: 最初的code中每次dot product 都是完全重复的,数值多是一样。
: 所以在完全优化后只用做一次dot product 后乘以repeat就可以了。
: 我重新设计了code。 可能还是成在优化的可能,但已经不明显了。
: 要求OMP的reduction 比MPI有效应该不是无理要求吧?
: reduction

l
llaalways

这种强相关的case很多。比如Krylov linear solver 里面要用到的正交化。
基本上就是这样。每一个dot product用到的element value 都跟前面的dot product
有关。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你肯定你的實際咚阏娴氖悄阆旅孢@個邏輯還是你人爲造出來的case?你這個等於一個
: 強相關的算法,本來並行效果就不會好的。

l
llaalways

我试了修改KMP_AFFINITY, 基本没差别。
export KMP_AFFINITY=scatter

export KMP_AFFINITY scatter
这个命令对吗? 还有其他设置需要调试吗?
改后:
sum = 6992.953984, time = 0.416691 sec, np = 1
sum = 6992.953984, time = 0.270838 sec, np = 2
sum = 6992.953984, time = 0.187582 sec, np = 4
sum = 6992.953984, time = 0.140101 sec, np = 8
sum = 6992.953984, time = 0.118161 sec, np = 16
sum = 6992.953984, time = 0.125198 sec, np = 32

修改前:
sum = 6992.953984, time = 0.409850 sec, np = 1
sum = 6992.953984, time = 0.270875 sec, np = 2
sum = 6992.953984, time = 0.186024 sec, np = 4
sum = 6992.953984, time = 0.144010 sec, np = 8
sum = 6992.953984, time = 0.115188 sec, np = 16
sum = 6992.953984, time = 0.195485 sec, np = 32

【 在 BacktoMars (supercalifragilisticexpiadocious) 的大作中提到: 】
: OMP和MPI相差这么大,很可能是CPU affinity的问题。对于Intel OMP,试试
: 把环境变量KMP_AFFINITY设成scatter。
: 另外,一般的MPI implementation都对inter node有所优化,大部分是用了shared
: memory,所以在你的单机上效率应该和OMP差不多。如果有明显差别,要么是时间计算
: 不一样(比如是否考虑了各种overhead),要么是scheduler的问题(比如affinity)。

c
chebyshev

你最初那程序,下面這幾行與內積無關吧?就是統計目的?

#pragma omp single
{
sum += (loc_dot/(double)(n));
loc_dot =0;
}

【 在 llaalways (熊大) 的大作中提到: 】
: 这种强相关的case很多。比如Krylov linear solver 里面要用到的正交化。
: 基本上就是这样。每一个dot product用到的element value 都跟前面的dot product
: 有关。

c
chebyshev

絕對速度只是一方面。也許linear scale 問題更重要。
我剛才在我的game laptop上面跑了一下你第一頁的內積程序。
也是thread 8以上後,耗時開始增加。

【 在 llaalways (熊大) 的大作中提到: 】
: 你说的对, 我的主贴就在问reduction效率问题。
: 可是大家都focus on怎么优化repeated dot-product。
: 我最初的code设计的不好,太容易被优化了,使得大家都不管我在关心什么。
: 最初的code中每次dot product 都是完全重复的,数值多是一样。
: 所以在完全优化后只用做一次dot product 后乘以repeat就可以了。
: 我重新设计了code。 可能还是成在优化的可能,但已经不明显了。
: 要求OMP的reduction 比MPI有效应该不是无理要求吧?
: reduction

l
llaalways

export KMP_AFFINITY=compact
这个效果最好。

sum = 6992.953984, time = 0.408360 sec, np = 1
sum = 6992.953984, time = 0.261517 sec, np = 2
sum = 6992.953984, time = 0.172954 sec, np = 4
sum = 6992.953984, time = 0.119487 sec, np = 8
sum = 6992.953984, time = 0.092054 sec, np = 16
sum = 6992.953984, time = 0.113060 sec, np = 32

【 在 llaalways (熊大) 的大作中提到: 】
: 我试了修改KMP_AFFINITY, 基本没差别。
: export KMP_AFFINITY=scatter
: 或
: export KMP_AFFINITY scatter
: 这个命令对吗? 还有其他设置需要调试吗?
: 改后:
: sum = 6992.953984, time = 0.416691 sec, np = 1
: sum = 6992.953984, time = 0.270838 sec, np = 2
: sum = 6992.953984, time = 0.187582 sec, np = 4
: sum = 6992.953984, time = 0.140101 sec, np = 8
: ...................

l
llaalways

是的,只是为了避免过度优化。
【 在 chebyshev (......) 的大作中提到: 】
: 你最初那程序,下面這幾行與內積無關吧?就是統計目的?
: #pragma omp single
: {
: sum += (loc_dot/(double)(n));
: loc_dot =0;
: }

j
jmrf


【 在 guvest (我爱你老婆Anna) 的大作中提到: 】
: 我的理解是。他不完全是想找最优解。主要是用简单的例子测一下openMP之
reduction
: clause。
: 看其属于下面哪个范畴:
: A. 很烂。无用
: B. 有用。但是不知如何选参数以及设计程序
: 一个重要的问题是,为何求内积这样的简单操作。为何线程增加之后,reduction
: clause性能没有继续增加?我猜是调度之overhead太高了。
: 所以我有个猜想,假如单机用Golang求内积,因为其调度粒度更细。可能更线性些。例
: 如下面求卷积的代码,可改为内积跑跑看。
: https://yourbasic.org/golang/efficient-parallel-computation/
: ...................

应该算reduction有用但他的参数不合适,工作量太小不够分
我用atomic把reduction替换以后,小np变差,大np变好.
给for加上 nowait, 大np还可以加速,虽然结果不对但计算量是一样的
np>=16 时间波动很大,不可靠

gcc t2nr.c -fopenmp -o t2nr.x -O2
atomic
sum = 15774.047975, time = 0.478993 sec, np = 1
sum = 15773.415164, time = 0.307828 sec, np = 2
sum = 15773.170634, time = 0.161034 sec, np = 4
sum = 15773.114328, time = 0.097337 sec, np = 8
sum = 15773.023627, time = 0.101083 sec, np = 16
sum = 15773.019270, time = 0.099818 sec, np = 32
reduction
sum = 15774.047975, time = 0.449608 sec, np = 1
sum = 15774.047975, time = 0.251523 sec, np = 2
sum = 15774.047975, time = 0.140971 sec, np = 4
sum = 15774.047975, time = 0.121877 sec, np = 8
sum = 15774.047975, time = 0.127682 sec, np = 16
sum = 15774.047975, time = 0.128808 sec, np = 32
atomic nowait
sum = 15774.047975, time = 0.443214 sec, np = 1
sum = 15750.483609, time = 0.240029 sec, np = 2
sum = 15749.893158, time = 0.130304 sec, np = 4
sum = 15783.550422, time = 0.073210 sec, np = 8
sum = 15553.170144, time = 0.071791 sec, np = 16
sum = 15914.384916, time = 0.060435 sec, np = 32
reduction nowait
sum = 15774.047975, time = 0.447009 sec, np = 1
sum = 15769.952058, time = 0.276017 sec, np = 2
sum = 12160.053164, time = 0.136759 sec, np = 4
sum = 13280.812545, time = 0.104769 sec, np = 8
sum = 14847.901391, time = 0.098864 sec, np = 16
sum = 14218.002954, time = 0.107371 sec, np = 32

icc t2nr.c -qopenmp -o t2nr.x -O2
atomic
sum = 15774.047975, time = 0.263992 sec, np = 1
sum = 15773.459889, time = 0.141759 sec, np = 2
sum = 15773.290539, time = 0.088290 sec, np = 4
sum = 15773.076301, time = 0.058803 sec, np = 8
sum = 15773.076528, time = 0.086343 sec, np = 16
sum = 15773.001462, time = 0.476611 sec, np = 32
reduction
sum = 15774.047975, time = 0.222486 sec, np = 1
sum = 15774.047975, time = 0.126074 sec, np = 2
sum = 15774.047975, time = 0.078066 sec, np = 4
sum = 15774.047975, time = 0.055310 sec, np = 8
sum = 15774.047975, time = 0.059027 sec, np = 16
sum = 15774.047975, time = 0.913732 sec, np = 32
atomic nowait
sum = 15774.047975, time = 0.222162 sec, np = 1
sum = 15680.474010, time = 0.118676 sec, np = 2
sum = 15653.529188, time = 0.070725 sec, np = 4
sum = 15493.733726, time = 0.045788 sec, np = 8
sum = 15771.120010, time = 0.036478 sec, np = 16
sum = 16206.380510, time = 0.054956 sec, np = 32
reduction nowait
sum = 15774.047975, time = 0.221729 sec, np = 1
sum = 14294.544590, time = 0.123898 sec, np = 2
sum = 12886.392032, time = 0.076451 sec, np = 4
sum = 15613.464179, time = 0.054847 sec, np = 8
sum = 15745.009185, time = 0.083885 sec, np = 16
sum = 14903.450378, time = 0.750764 sec, np = 32

Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz

#include
#include
#include
#include

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

int n = 10000;
int repeat = 10000;
double nn= n*n;

int np = 1;
if (argc > 1)
{
np = atoi(argv[1]);
}
omp_set_num_threads(np);

int nstart =0;
int sublength =n;

double sum = 1;
double time = -omp_get_wtime();
#pragma omp parallel
{
int i, j, k;

for (j = 0; j < repeat; j++)
{
double ls;
ls=0.0;
#pragma omp for
for (k = 0; k < sublength; k++)
{
double temp = (sum+ nstart +j +k)/nn;
ls += temp;
}
#pragma omp atomic
sum+=ls;
}
}
time += omp_get_wtime();
printf("sum = %f, time = %f sec, np = %dn", sum, time, np);

return 0;
}
n
netghost

強相關沒有問題,多幾個pipeline而已,問題在於你相關的每個pipeline的size不能改,而且還是10000這麼小的是一個數。

說實話,我這麼多年實際環境從來沒見過一個case,你那裏那個10000相當的數是不能
改的。Linear solver的分片大小都是可以調的,除非矩陣非常小,which隨便怎麼算都沒差了。
【 在 llaalways (熊大) 的大作中提到: 】
: 这种强相关的case很多。比如Krylov linear solver 里面要用到的正交化。
: 基本上就是这样。每一个dot product用到的element value 都跟前面的dot product
: 有关。

n
netghost

你這個算得也實在是慢了點。。我和你一樣的CPU 用OMP,基本上沒改。

sum = 6992.953984, time = 0.153260 sec, np = 1
sum = 6992.953984, time = 0.090700 sec, np = 2
sum = 6992.953984, time = 0.051722 sec, np = 4
sum = 6992.953984, time = 0.042070 sec, np = 8
sum = 6992.953984, time = 0.054564 sec, np = 16
sum = 6992.953984, time = 0.096648 sec, np = 32

問題是即使是空循環;
sum = 1.000000, time = 0.002300 sec, np = 1
sum = 1.000000, time = 0.005608 sec, np = 2
sum = 1.000000, time = 0.011405 sec, np = 4
sum = 1.000000, time = 0.020114 sec, np = 8
sum = 1.000000, time = 0.040158 sec, np = 16
sum = 1.000000, time = 0.055540 sec, np = 32

這個基本上是overhead了,所以你還想要什麼呢?

【 在 llaalways (熊大) 的大作中提到: 】
: 我试了修改KMP_AFFINITY, 基本没差别。
: export KMP_AFFINITY=scatter
: 或
: export KMP_AFFINITY scatter
: 这个命令对吗? 还有其他设置需要调试吗?
: 改后:
: sum = 6992.953984, time = 0.416691 sec, np = 1
: sum = 6992.953984, time = 0.270838 sec, np = 2
: sum = 6992.953984, time = 0.187582 sec, np = 4
: sum = 6992.953984, time = 0.140101 sec, np = 8
: ...................

C
Caravel

你还是回火星吧
【 在 BacktoMars (supercalifragilisticexpiadocious) 的大作中提到: 】
: OMP和MPI相差这么大,很可能是CPU affinity的问题。对于Intel OMP,试试
: 把环境变量KMP_AFFINITY设成scatter。
: 另外,一般的MPI implementation都对inter node有所优化,大部分是用了shared
: memory,所以在你的单机上效率应该和OMP差不多。如果有明显差别,要么是时间计算
: 不一样(比如是否考虑了各种overhead),要么是scheduler的问题(比如affinity)。

C
Caravel

你还是回火星吧
【 在 BacktoMars (supercalifragilisticexpiadocious) 的大作中提到: 】
: OMP和MPI相差这么大,很可能是CPU affinity的问题。对于Intel OMP,试试
: 把环境变量KMP_AFFINITY设成scatter。
: 另外,一般的MPI implementation都对inter node有所优化,大部分是用了shared
: memory,所以在你的单机上效率应该和OMP差不多。如果有明显差别,要么是时间计算
: 不一样(比如是否考虑了各种overhead),要么是scheduler的问题(比如affinity)。

l
llaalways

你说一样的CPU,一样的程序基本没改。
怎么做到速度加倍的?
是compiler不一样? 还是compiling option 不一样?
我想要要让OMP至少不比MPI差。

我的OMP
sum = 6992.953984, time = 0.408360 sec, np = 1
sum = 6992.953984, time = 0.261517 sec, np = 2
sum = 6992.953984, time = 0.172954 sec, np = 4
sum = 6992.953984, time = 0.119487 sec, np = 8
sum = 6992.953984, time = 0.092054 sec, np = 16
sum = 6992.953984, time = 0.113060 sec, np = 32
你的OMP
: sum = 6992.953984, time = 0.153260 sec, np = 1
: sum = 6992.953984, time = 0.090700 sec, np = 2
: sum = 6992.953984, time = 0.051722 sec, np = 4
: sum = 6992.953984, time = 0.042070 sec, np = 8
: sum = 6992.953984, time = 0.054564 sec, np = 16
: sum = 6992.953984, time = 0.096648 sec, np = 32
MPI
sum = 6992.953984, time = 0.381701 sec, np = 1
sum = 6992.953984, time = 0.243513 sec, np = 2
sum = 6992.953984, time = 0.158326 sec, np = 4
sum = 6992.953984, time = 0.102489 sec, np = 8
sum = 6992.953984, time = 0.063975 sec, np = 16
sum = 6992.953984, time = 0.044748 sec, np = 32

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你這個算得也實在是慢了點。。我和你一樣的CPU 用OMP,基本上沒改。
: sum = 6992.953984, time = 0.153260 sec, np = 1
: sum = 6992.953984, time = 0.090700 sec, np = 2
: sum = 6992.953984, time = 0.051722 sec, np = 4
: sum = 6992.953984, time = 0.042070 sec, np = 8
: sum = 6992.953984, time = 0.054564 sec, np = 16
: sum = 6992.953984, time = 0.096648 sec, np = 32
: 問題是即使是空循環;
: sum = 1.000000, time = 0.002300 sec, np = 1
: sum = 1.000000, time = 0.005608 sec, np = 2
: ...................

l
llaalways

atomic的方法我也试过了。基本上跟reduction clause 差不多。
所以没有提出来讨论。
nowait我也试过了,两个地方都可能有nowait option,但都会引起不正确解。
我还试过用counter来代替barrier or wait(implicit barrier),
效果也不明显。

为了简化问题,我只用reduction clause 来跟MPI_Allreduce 比较。

【 在 jmrf (daimao) 的大作中提到: 】
: reduction
: 应该算reduction有用但他的参数不合适,工作量太小不够分
: 我用atomic把reduction替换以后,小np变差,大np变好.
: 给for加上 nowait, 大np还可以加速,虽然结果不对但计算量是一样的
: np>=16 时间波动很大,不可靠
: gcc t2nr.c -fopenmp -o t2nr.x -O2
: atomic
: sum = 15774.047975, time = 0.478993 sec, np = 1
: sum = 15773.415164, time = 0.307828 sec, np = 2
: sum = 15773.170634, time = 0.161034 sec, np = 4
: ...................

c
chebyshev

我把求內積那個程序之n設成int n = 100000;
下面結果看起來就reasonable多了。線程增加到一定程度之後,基本上都是1.2。
不會有性能提高,也不會有性能下降。

數組小的時候,線程大於8個,速度之下降應為各種調度之overhead所引起。
sum = 278.215518, time = 2.877507 sec, np = 1
sum = 278.215518, time = 1.122559 sec, np = 3
sum = 278.215518, time = 1.411872 sec, np = 5
sum = 278.215518, time = 1.200148 sec, np = 7
sum = 278.215518, time = 1.265504 sec, np = 9
sum = 278.215518, time = 1.227077 sec, np = 11
sum = 278.215518, time = 1.225194 sec, np = 13
sum = 278.215518, time = 1.211727 sec, np = 15
sum = 278.215518, time = 1.232751 sec, np = 17
sum = 278.215518, time = 1.224881 sec, np = 19
sum = 278.215518, time = 1.230595 sec, np = 21
sum = 278.215518, time = 1.233759 sec, np = 23
sum = 278.215518, time = 1.226739 sec, np = 25
sum = 278.215518, time = 1.227760 sec, np = 27
sum = 278.215518, time = 1.223140 sec, np = 29
sum = 278.215518, time = 1.220702 sec, np = 31

【 在 jmrf (daimao) 的大作中提到: 】
: reduction
: 应该算reduction有用但他的参数不合适,工作量太小不够分
: 我用atomic把reduction替换以后,小np变差,大np变好.
: 给for加上 nowait, 大np还可以加速,虽然结果不对但计算量是一样的
: np>=16 时间波动很大,不可靠
: gcc t2nr.c -fopenmp -o t2nr.x -O2
: atomic
: sum = 15774.047975, time = 0.478993 sec, np = 1
: sum = 15773.415164, time = 0.307828 sec, np = 2
: sum = 15773.170634, time = 0.161034 sec, np = 4
: ...................

c
chebyshev

你測下下面hypothesis是否正確:

sublength太小,導致空循環與下面循環在一個數量級。導致測出來的時間之一個主要
部分
是調度overhead【其和空循環耗時應在一個數量級】。

for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}

【 在 llaalways (熊大) 的大作中提到: 】
: atomic的方法我也试过了。基本上跟reduction clause 差不多。
: 所以没有提出来讨论。
: nowait我也试过了,两个地方都可能有nowait option,但都会引起不正确解。
: 我还试过用counter来代替barrier or wait(implicit barrier),
: 效果也不明显。
: 为了简化问题,我只用reduction clause 来跟MPI_Allreduce 比较。

l
llaalways

我知道sublength太小,引起OMP reduction效率变差。
我认为这不是调度的问题, 因为在MPI code 里有同样的循环
MPI code
for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}
MPI_Allreduce(&loc_dot, &dot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
sum += (dot/(double)(n));
loc_dot = 0;

OMP code

#pragma omp for reduction(+: loc_dot)
for (k = 0; k < sublength; k++)
{
double temp = sin((sum+ nstart +k +j)/(double)(n));
loc_dot += (temp * temp);
}

#pragma omp single
{
sum += (loc_dot/(double)(n));
loc_dot =0;
}

这两段code实现完全一样的功能。就是每个核累加部分的dotproduct, 然后reduce。
如果两边都取消reduce,performance是一样的。
加上reduce之后,OMP就比MPI差。

注意这两个codes里nstart 和sublength 不一样, 在OMP code 里nstart=0,
sublength=n.
在MPI里sublength=n/np, nstart = my_rank*sublength.
我为了让两个codes计算量相同才吧 +nstart 保留在 temp的计算里。 
如果优化OMP code, 可以去掉+nstart
double temp = sin((sum +k +j)/(double)(n));

【 在 chebyshev (......) 的大作中提到: 】
: 你測下下面hypothesis是否正確:
: sublength太小,導致空循環與下面循環在一個數量級。導致測出來的時間之一個主要
: 部分
: 是調度overhead【其和空循環耗時應在一個數量級】。
: for (k = 0; k < sublength; k++)
: {
: double temp = sin((sum+ nstart +k +j)/(double)(n));
: loc_dot += (temp * temp);
: }

j
jmrf

现在的问题就是overhead太大,不是openmp的理想客户.
如果你数组就这么大还想并行,也可以自己写pthread. pthread入手不难,但保证没有
overhead也不容易. pthread要在完全优化单CPU速度的基础上才有意义
openmpi,可能只是把任务分得更严格,碰巧减小了一些miss

【 在 llaalways (熊大) 的大作中提到: 】
: atomic的方法我也试过了。基本上跟reduction clause 差不多。
: 所以没有提出来讨论。
: nowait我也试过了,两个地方都可能有nowait option,但都会引起不正确解。
: 我还试过用counter来代替barrier or wait(implicit barrier),
: 效果也不明显。
: 为了简化问题,我只用reduction clause 来跟MPI_Allreduce 比较。

c
chebyshev

MPI code裡,
int sublength = (int)(ceil((double)(n) / (double)(np)));
其為一變數?

OMP裡, int sublength =n;
sublength為常數?

【 在 llaalways (熊大) 的大作中提到: 】
: 我知道sublength太小,引起OMP reduction效率变差。
: 我认为这不是调度的问题, 因为在MPI code 里有同样的循环
: MPI code
: for (k = 0; k < sublength; k++)
: {
: double temp = sin((sum+ nstart +k +j)/(double)(n));
: loc_dot += (temp * temp);
: }
: MPI_Allreduce(&loc_dot, &dot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
: sum += (dot/(double)(n));
: ...................

n
netghost

我vectorize你的loop了,你這個屬於單CPU的很容易vectorize的情況。實際上我後來
發現基本上新點的GCC都可以通過制定m用上avx指令,代碼都不用改。

但是這個並不是關鍵,這種自動並行的線程機制的核心問題都是你控制不了線程的創建和調度,當然這也是不想手動的代價,不過既然用了別人的東西,就不能說我要多大的work size就多大了。

具體到MPI和OMP,你看到的差異恰恰是這個你控制不了的情況在這個具體問題上的體現。MPI應該是開始建立一堆線程就不管了。OMP是自己在編譯器裏面更具需要create和
join的,所以這個overhead在你這個情況下比MPI大。

【 在 llaalways (熊大) 的大作中提到: 】
: 你说一样的CPU,一样的程序基本没改。
: 怎么做到速度加倍的?
: 是compiler不一样? 还是compiling option 不一样?
: 我想要要让OMP至少不比MPI差。
: 我的OMP
: sum = 6992.953984, time = 0.408360 sec, np = 1
: sum = 6992.953984, time = 0.261517 sec, np = 2
: sum = 6992.953984, time = 0.172954 sec, np = 4
: sum = 6992.953984, time = 0.119487 sec, np = 8
: sum = 6992.953984, time = 0.092054 sec, np = 16
: ...................

n
netghost

你始終在強調reduce,這東西只是一個high level概念,下面可以有一堆實現,並不是你去掉了reduce結果變化了,就是reduce的implementation造成的。

【 在 llaalways (熊大) 的大作中提到: 】
: 我知道sublength太小,引起OMP reduction效率变差。
: 我认为这不是调度的问题, 因为在MPI code 里有同样的循环
: MPI code
: for (k = 0; k < sublength; k++)
: {
: double temp = sin((sum+ nstart +k +j)/(double)(n));
: loc_dot += (temp * temp);
: }
: MPI_Allreduce(&loc_dot, &dot, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
: sum += (dot/(double)(n));
: ...................

c
chebyshev

他的mpi code你能跑嗎?
似乎不全?
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你始終在強調reduce,這東西只是一個high level概念,下面可以有一堆實現,並不是
: 你去掉了reduce結果變化了,就是reduce的implementation造成的。

j
jmrf

还是比大佬你的差好多,还要点啥?

Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz
gcc t2.c -march=native -ffast-math -fassociative-math -ftree-vectorize -msse -msse2 -mavx2 -fopenmp -o t2.x -O2 -lm

sum = 6992.953984, time = 0.247078 sec, np = 1
sum = 6992.953984, time = 0.160590 sec, np = 2
sum = 6992.953984, time = 0.085497 sec, np = 4
sum = 6992.953984, time = 0.087919 sec, np = 8
sum = 6992.953984, time = 0.093634 sec, np = 16
sum = 6992.953984, time = 0.108651 sec, np = 32

flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx
est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe
popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti intel_ppin ssbd ibrs ibpb stibp tpr_shadow vnmi
flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid
cqm xsaveopt cqm_llc cqm_occup_llc dtherm ida arat pln pts flush_l1d

【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 你這個算得也實在是慢了點。。我和你一樣的CPU 用OMP,基本上沒改。
: sum = 6992.953984, time = 0.153260 sec, np = 1
: sum = 6992.953984, time = 0.090700 sec, np = 2
: sum = 6992.953984, time = 0.051722 sec, np = 4
: sum = 6992.953984, time = 0.042070 sec, np = 8
: sum = 6992.953984, time = 0.054564 sec, np = 16
: sum = 6992.953984, time = 0.096648 sec, np = 32
: 問題是即使是空循環;
: sum = 1.000000, time = 0.002300 sec, np = 1
: sum = 1.000000, time = 0.005608 sec, np = 2
: ...................

n
netghost

恩,你能搞的基本上也搞了,差就差了一個avx512f。
【 在 jmrf (daimao) 的大作中提到: 】
: 还是比大佬你的差好多,还要点啥?
: Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz
: gcc t2.c -march=native -ffast-math -fassociative-math -ftree-vectorize -
msse
: -msse2 -mavx2 -fopenmp -o t2.x -O2 -lm
: sum = 6992.953984, time = 0.247078 sec, np = 1
: sum = 6992.953984, time = 0.160590 sec, np = 2
: sum = 6992.953984, time = 0.085497 sec, np = 4
: sum = 6992.953984, time = 0.087919 sec, np = 8
: sum = 6992.953984, time = 0.093634 sec, np = 16
: sum = 6992.953984, time = 0.108651 sec, np = 32
: ...................

n
netghost

好像是能跑的。中間有沒有做點小改動不記得了。
【 在 chebyshev (......) 的大作中提到: 】
: 标 题: Re: 问一个并行计算的问题
: 发信站: BBS 未名空间站 (Thu Jul 8 13:14:48 2021, 美东)
:
: 他的mpi code你能跑嗎?
: 似乎不全?
: 【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: : 你始終在強調reduce,這東西只是一個high level概念,下面可以有一堆實現,並不是
: : 你去掉了reduce結果變化了,就是reduce的implementation造成的。
:
:
:
: --
n
netghost

樓主想換成openmp,which我可以理解,因爲這東西比mpi透明,寫起來省事。mpi有點
點半自動的效果。

但是這種情況下,你overhead在任務本身分片很小的情況下,就很明顯了。實際上這個事情在你把單cpu任務開銷優化到足夠小的時候已經一目瞭然了。

【 在 jmrf (daimao) 的大作中提到: 】
: 现在的问题就是overhead太大,不是openmp的理想客户.
: 如果你数组就这么大还想并行,也可以自己写pthread. pthread入手不难,但保证没有: overhead也不容易. pthread要在完全优化单CPU速度的基础上才有意义
: openmpi,可能只是把任务分得更严格,碰巧减小了一些miss

c
chebyshev

用O3試下。
gcc p3.c -fopenmp -lm -O3
比沒有O3快一倍多。
【 在 jmrf (daimao) 的大作中提到: 】
: 还是比大佬你的差好多,还要点啥?
: Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz
: gcc t2.c -march=native -ffast-math -fassociative-math -ftree-vectorize -
msse
: -msse2 -mavx2 -fopenmp -o t2.x -O2 -lm
: sum = 6992.953984, time = 0.247078 sec, np = 1
: sum = 6992.953984, time = 0.160590 sec, np = 2
: sum = 6992.953984, time = 0.085497 sec, np = 4
: sum = 6992.953984, time = 0.087919 sec, np = 8
: sum = 6992.953984, time = 0.093634 sec, np = 16
: sum = 6992.953984, time = 0.108651 sec, np = 32
: ...................

c
chebyshev

mpi code那sublength是從np求出來的。
是一個變數。如此,似乎他做的並非apple to apple之比較。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 好像是能跑的。中間有沒有做點小改動不記得了。
: 不是

j
jmrf

看来是CPU太老不支持 avx512f,
加了直接,Illegal instruction
加【在 netghost (Up to Isomorphism) 的大作中提到: 】
: 恩,你能搞的基本上也搞了,差就差了一個avx512f。
: msse

n
netghost

他都上那一堆開關了,這個O3啥的他顯然試過了。
【 在 chebyshev (......) 的大作中提到: 】
: 标 题: Re: 问一个并行计算的问题
: 发信站: BBS 未名空间站 (Thu Jul 8 13:54:45 2021, 美东)
:
: 用O3試下。
: gcc p3.c -fopenmp -lm -O3
: 比沒有O3快一倍多。
: 【 在 jmrf (daimao) 的大作中提到: 】
: : 还是比大佬你的差好多,还要点啥?
: : Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz
: : gcc t2.c -march=native -ffast-math -fassociative-math -ftree-vectorize -: msse
: : -msse2 -mavx2 -fopenmp -o t2.x -O2 -lm
: : sum = 6992.953984, time = 0.247078 sec, np = 1
: : sum = 6992.953984, time = 0.160590 sec, np = 2
: : sum = 6992.953984, time = 0.085497 sec, np = 4
: : sum = 6992.953984, time = 0.087919 sec, np = 8
: : sum = 6992.953984, time = 0.093634 sec, np = 16
: : sum = 6992.953984, time = 0.108651 sec, np = 32
: : ...................
:
:
:
:
: --
n
netghost

對的,稍微老了一點。但是idea没差,这种代码稍微优化一下,立刻就看出overhead在哪里了。

你可以把march=native那个去了试试,gcc加那个有时候会发神经。
【 在 jmrf (daimao) 的大作中提到: 】
: 标 题: Re: 问一个并行计算的问题
: 发信站: BBS 未名空间站 (Thu Jul 8 14:01:19 2021, 美东)
:
: 看来是CPU太老不支持 avx512f,
: 加了直接,Illegal instruction
: 加【在 netghost (Up to Isomorphism) 的大作中提到: 】
: : 恩,你能搞的基本上也搞了,差就差了一個avx512f。
: : msse
:
:
:
: --
n
netghost

没本质区别,就是手动算一个分片。

MPI这东西我上一次用至少是20年前了, 结果基本上好像也没什么变化。

但是这种东西,自己pthread create一下很难吗,那真是一个手艺学了可以用30年。
【 在 chebyshev (......) 的大作中提到: 】
: mpi code那sublength是從np求出來的。
: 是一個變數。如此,似乎他做的並非apple to apple之比較。

c
chebyshev

-Ofast比-O3又快很多。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 他都上那一堆開關了,這個O3啥的他顯然試過了。

c
chebyshev

loop次數不一樣。乘法和加法,sin次數不同了。
OMP loop次數為n。
MPI程序 loop數等於int sublength = (int)(ceil((double)(n) / (double)(np)));
np=8時,sin少算8次。這是兩個不同的程序了。
【 在 netghost (Up to Isomorphism) 的大作中提到: 】
: 没本质区别,就是手动算一个分片。
: MPI这东西我上一次用至少是20年前了, 结果基本上好像也没什么变化。
: 但是这种东西,自己pthread create一下很难吗,那真是一个手艺学了可以用30年。

j
jmrf

用O3要快一点.
你把编译指令写code里了吗? 只用 gcc p3.c -fopenmp -lm -O3 直接打回原形,要1.3 秒
sum = 6992.953984, time = 0.216588 sec, np = 1
sum = 6992.953984, time = 0.118749 sec, np = 2
sum = 6992.953984, time = 0.104176 sec, np = 4
sum = 6992.953984, time = 0.082472 sec, np = 8
sum = 6992.953984, time = 0.092022 sec, np = 16
sum = 6992.953984, time = 0.109844 sec, np = 32

【 在 chebyshev (......) 的大作中提到: 】
: 用O3試下。
: gcc p3.c -fopenmp -lm -O3
: 比沒有O3快一倍多。
: msse