【发布时间】:2011-11-27 15:18:49
【问题描述】:
我想编写一个广泛使用 BLAS 和 LAPACK 线性代数功能的程序。由于性能是一个问题,我做了一些基准测试,想知道我采用的方法是否合法。
可以这么说,我有三个参赛者,想用简单的矩阵-矩阵乘法来测试他们的表现。参赛选手是:
- Numpy,仅使用
dot的功能。 - Python,通过共享对象调用 BLAS 功能。
- C++,通过共享对象调用 BLAS 功能。
场景
我为不同维度实现了矩阵-矩阵乘法i。 i 从 5 运行到 500,增量为 5,矩阵 m1 和 m2 设置如下:
m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
1。麻木
使用的代码如下所示:
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))
2。 Python,通过共享对象调用BLAS
具有功能
_blaslib = ctypes.cdll.LoadLibrary("libblas.so")
def Mul(m1, m2, i, r):
no_trans = c_char("n")
n = c_int(i)
one = c_float(1.0)
zero = c_float(0.0)
_blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref(n), byref(n),
byref(one), m1.ctypes.data_as(ctypes.c_void_p), byref(n),
m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(zero),
r.ctypes.data_as(ctypes.c_void_p), byref(n))
测试代码如下所示:
r = numpy.zeros((i,i), numpy.float32)
tBlas = timeit.Timer("Mul(m1, m2, i, r)", "import numpy; from __main__ import i, m1, m2, r, Mul")
rBlas.append((i, tBlas.repeat(20, 1)))
3。 c++,通过共享对象调用BLAS
现在 c++ 代码自然会长一点,所以我将信息减少到最少。
我用
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
我用gettimeofday 测量时间是这样的:
gettimeofday(&start, NULL);
f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &zero, Return, &dim);
gettimeofday(&end, NULL);
dTimes[j] = CalcTime(start, end);
j 是一个运行 20 次的循环。我计算了经过的时间
double CalcTime(timeval start, timeval end)
{
double factor = 1000000;
return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor;
}
结果
结果如下图所示:
问题
- 您认为我的方法是否公平,或者我可以避免一些不必要的开销?
- 您是否期望结果会显示 c++ 和 python 方法之间存在如此巨大的差异?两者都使用共享对象进行计算。
- 由于我更愿意在我的程序中使用 python,在调用 BLAS 或 LAPACK 例程时我可以做些什么来提高性能?
下载
完整的基准测试可以下载here。 (J.F. Sebastian 使该链接成为可能^^)
【问题讨论】:
-
在您的 ctypes 方法中,您在测量函数内部分配了内存。您的 c++ 代码是否遵循这种方法?但是与矩阵乘法相比,这应该没有太大区别......
-
@rocksportrocker 你是对的。
r矩阵的内存分配是不公平的。我现在正在解决“问题”并发布新结果。 -
1.确保数组具有相同的内存布局
np.ascontiguousarray()(考虑 C 与 Fortran 顺序)。 2.确保np.dot()使用相同的libblas.so。 -
@Woltan:不要使用 filefactory 服务太糟糕了。我已将您的基准添加到 github:woltan-benchmark。如果您使用 github,我可以将您添加为合作者。
-
我跑了your benchmarks under Python 3,得到了similar discrepancy between Python and C++。虽然我知道 numpy 的性能取决于它链接的 blas 库,但我不知道为什么 Python/BLAS 和 C++/BLAS 之间存在差异,因为它们都链接到同一个库。你是如何解决这个问题的?
标签: c++ python numpy benchmarking blas