python单精度和双精度,多核机上单精度与双精度阵列矩阵乘法的性能退化

更新

不幸的是,由于我的疏忽,我有一个旧版本的MKL(11.1)与numpy相关联。更新版本的MKL(11.3.1)在C语言中以及从python调用时都具有相同的性能。在

使事情变得模糊不清的是,即使将编译好的共享库显式地与新的MKL链接起来,并通过LD*variables指向它们,然后在python中执行import numpy,也会使python调用旧的MKL库。只有在python lib文件夹中替换所有libmkl_*.so,我才能够匹配python和C调用的性能。在

背景/库信息。

矩阵乘法是通过sgemm(单精度)和dgemm(双精度)Intel的MKL库调用,通过纽比.dot功能。库函数的实际调用可以用例如oprof进行验证。在

这里使用2x18核的CPU E5-2699 v3,因此总共有36个物理内核。

KMP_AFFINITY=分散。在linux上运行。在

TL;DR

1)为什么纽比.dot,即使它调用相同的MKL库函数,但与C编译的代码相比最多慢两倍?在

2)为什么选择via纽比.dot随着核心数量的增加,性能会下降,而在C代码中(调用相同的库函数)却没有观察到相同的效果。在

问题

我观察到,做单精度/双精度的矩阵乘法纽比.dot,以及直接从编译的C共享库调用cblas sgemm/dgemm,与从纯C代码内部调用相同的MKL cblas sgemm/dgemm函数相比,性能明显较差。在import numpy as np

import mkl

n = 10000

A = np.random.randn(n,n).astype('float32')

B = np.random.randn(n,n).astype('float32')

C = np.zeros((n,n)).astype('float32')

mkl.set_num_threads(3); %time np.dot(A, B, out=C)

11.5 seconds

mkl.set_num_threads(6); %time np.dot(A, B, out=C)

6 seconds

mkl.set_num_threads(12); %time np.dot(A, B, out=C)

3 seconds

mkl.set_num_threads(18); %time np.dot(A, B, out=C)

2.4 seconds

mkl.set_num_threads(24); %time np.dot(A, B, out=C)

3.6 seconds

mkl.set_num_threads(30); %time np.dot(A, B, out=C)

5 seconds

mkl.set_num_threads(36); %time np.dot(A, B, out=C)

5.5 seconds

执行与上述完全相同的操作,但使用双精度A、B和C,可以得到:

3芯20s,6芯10s,12芯5s,18芯4.3s,24芯3s,30芯2.8s,36芯2.8s

单精度浮点的速度增加似乎与缓存未命中有关。

对于28核运行,这里是perf的输出。

对于单精度:

^{pr2}$

双精度:93,087,703 cache-misses # 5.164 % of all cache refs

C共享库,用/opt/intel/bin/icc -o comp_sgemm_mkl.so -openmp -mkl sgem_lib.c -lm -lirc -O3 -fPIC -shared -std=c99 -vec-report1 -xhost -I/opt/intel/composer/mkl/include

#include

#include

#include "mkl.h"

void comp_sgemm_mkl(int m, int n, int k, float *A, float *B, float *C);

void comp_sgemm_mkl(int m, int n, int k, float *A, float *B, float *C)

{

int i, j;

float alpha, beta;

alpha = 1.0; beta = 0.0;

cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,

m, n, k, alpha, A, k, B, n, beta, C, n);

}

Python包装器函数,调用上面编译的库:def comp_sgemm_mkl(A, B, out=None):

lib = CDLL(omplib)

lib.cblas_sgemm_mkl.argtypes = [c_int, c_int, c_int,

np.ctypeslib.ndpointer(dtype=np.float32, ndim=2),

np.ctypeslib.ndpointer(dtype=np.float32, ndim=2),

np.ctypeslib.ndpointer(dtype=np.float32, ndim=2)]

lib.comp_sgemm_mkl.restype = c_void_p

m = A.shape[0]

n = B.shape[0]

k = B.shape[1]

if np.isfortran(A):

raise ValueError('Fortran array')

if m != n:

raise ValueError('Wrong matrix dimensions')

if out is None:

out = np.empty((m,k), np.float32)

lib.comp_sgemm_mkl(m, n, k, A, B, out)

然而,从一个调用MKL的cblas_sgemm/cblas_ggemm的C编译二进制文件中显式调用,以及在C中通过malloc分配的数组,与python代码相比,性能提高了近2倍,即纽比.dot打电话。此外,还没有观察到随核心数量增加而导致性能下降的影响。单精度矩阵乘法的最佳性能为900ms,通过mkl_set_num_cores使用所有36个物理内核,并使用numactl--interleave=all运行C代码时,性能达到最佳。在

也许有什么新奇的工具或建议来进一步分析/检查/了解这种情况?任何阅读材料也很受欢迎。在

更新

遵循@Hristo Iliev的建议,运行numactl--interleave=all./ipython没有更改计时(在noise中),但改进了纯C二进制运行时。在

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值