我有一个例程对小矩阵(50-100 x 1000 元素)执行一些 MKL 调用以拟合模型,然后我调用不同的模型。在伪代码中:
double doModelFit(int model, ...) {
...
while( !done ) {
cblas_dgemm(...);
cblas_dgemm(...);
...
dgesv(...);
...
}
return result;
}
int main(int argc, char **argv) {
...
c_start = 1; c_stop = nmodel;
for(int c=c_start; c<c_stop; c++) {
...
result = doModelFit(c, ...);
...
}
}
调用上面的版本1。由于模型是独立的,所以我可以使用OpenMP线程来并行化模型拟合,如下(版本2):
int main(int argc, char **argv) {
...
int numthreads=omp_max_num_threads();
int c;
#pragma omp parallel for private(c)
for(int t=0; t<numthreads; t++) {
// assuming nmodel divisible by numthreads...
c_start = t*nmodel/numthreads+1;
c_end = (t+1)*nmodel/numthreads;
for(c=c_start; c<c_stop; c++) {
...
result = doModelFit(c, ...);
...
}
}
}
当我在主机上运行版本 1 时,大约需要 11 秒,并且 VTune 报告并行化较差,大部分时间都处于空闲状态。主机上的版本 2 大约需要 5 秒,并且 VTune 报告了出色的并行化(几乎 100% 的时间花费在使用 8 个 CPU 上)。现在,当我编译代码以在本机模式下(使用 -mmic)在 Phi 卡上运行时,在 mic0 上的命令提示符下运行时,版本 1 和 2 都需要大约 30 秒。当我使用 VTune 对其进行分析时:
- 版本 1 大约需要 30 秒,热点分析表明大部分时间都花在了 __kmp_wait_sleep 和 __kmp_static_yield 上。在 7710 秒 CPU 时间中,有 5804 秒用于自旋时间。
- 版本 2 需要 fooooorrrreevvvver... 在 VTune 中运行几分钟后,我将其杀死。热点分析表明,25254s的CPU时间中,有21585s花费在[vmlinux]上。
谁能解释这里发生了什么以及为什么我的表现如此糟糕?我使用 OMP_NUM_THREADS 的默认值并设置 KMP_AFFINITY=compact,granularity=fine(如英特尔推荐的那样)。我是 MKL 和 OpenMP 的新手,所以我确定我犯了新手错误。
谢谢,安德鲁