LAPACK/BLAS与简单的“for”循环相比如何?

8
我希望将涉及多个向量和矩阵计算的代码迁移到C或C++,目标是尽可能加速代码。
在C代码中使用for循环进行线性代数计算是否能像使用LAPACK/BLAS库那样快,还是说使用这些库有一些独特的加速优势?
换句话说,简单的C代码(使用for循环等)能否像利用LAPACK/BLAS的代码一样快地执行线性代数计算?

8
如果自己实现一个和那些库一样快的程序很容易,那为什么这些库会被广泛使用呢? - Anon.
很明显,这取决于手头的应用程序。 - Ludi
1
@Anon,“...为什么这些库会被广泛使用?”- 为了(1)统一用户之间的代码风格,让他们不必关心(2)数组大小检查(如果是在C中),(3)边角情况等等。 - dizcza
4个回答

17
供应商提供的 LAPACK / BLAS 库(例如 Intel 的 IPP/MKL,但还有 AMD 的 ACML,以及其他 CPU 厂商如 IBM/Power 或 Oracle/SPARC 也提供类似的库),通常针对特定的 CPU 特性高度优化,这将显著提高处理大型数据集的性能。
然而,通常情况下,你需要处理非常具体的小数据(例如 4x4 矩阵或 4D 点积,即在三维几何处理中使用的操作),对于这些情况,由于这些子程序所进行的初始测试会选择不同的代码路径,因此BLAS/LAPACK是过度的。在这种情况下,简单的 C/C++ 源代码,可能使用 SSE2...4 内嵌函数和/或编译器生成的向量化,可能会击败 BLAS/LAPACK。例如,Intel 具有两个库 - MKL 用于大型线性代数数据集,IPP 用于小型 (图形向量) 数据集。
因此,
  • 你的数据集确切是什么?
  • 矩阵/向量大小是多少?
  • 所需的线性代数操作是什么?
另外,关于“简单的 for 循环”:给编译器一个机会来进行向量化。例如,像这样的代码:
for (i = 0; i < DIM_OF_MY_VECTOR; i += 4) {
    vecmul[i] = src1[i] * src2[i];
    vecmul[i+1] = src1[i+1] * src2[i+1];
    vecmul[i+2] = src1[i+2] * src2[i+2];
    vecmul[i+3] = src1[i+3] * src2[i+3];
}
for (i = 0; i < DIM_OF_MY_VECTOR; i += 4)
    dotprod += vecmul[i] + vecmul[i+1] + vecmul[i+2] + vecmul[i+3];

可能更适合用于向量化编译器的馈送,而不是普通的。

for (i = 0; i < DIM_OF_MY_VECTOR; i++) dotprod += src1[i]*src2[i];

表达式。在某些方面,您使用for循环进行计算所表示的意思将有重大影响。
但是,如果您的向量维数足够大,则会使用BLAS版本进行计算。

dotprod = CBLAS.ddot(DIM_OF_MY_VECTOR, src1, 1, src2, 1);
将会更加简洁且可能更快。关于参考资料方面,以下内容可能会有所帮助:

7
也许不需要。人们投入了大量工作来确保lapack/BLAS例程的优化和数值稳定性。虽然代码通常有些复杂,但通常是有原因的。
根据您的目标,您可能需要查看英特尔数学核心库。至少如果您的目标是英特尔处理器,那么它可能是您能找到的最快的选择。

2
+1. ATLAS是一个不错的免费便携式替代品,没有特定的许可问题。GoToBLAS很棒,但他们的许可证糟糕。 - Alexandre C.

4
数值分析很难。至少,你需要深入了解浮点算术的限制,并知道如何安排操作顺序,以便在速度和数值稳定性之间达到平衡。这是一项非常棘手的任务。
你需要对所需速度和稳定性之间的平衡有所了解。在更普遍的软件开发中,过早优化是万恶之源。在数值分析中,它却是关键。如果第一次没有正确地平衡好,你将不得不重写大部分代码。
当你尝试将线性代数证明转换为算法时,它变得更加困难。你需要真正理解代数,这样才能将其重构为稳定(或足够稳定)的算法。
如果我是你,我会针对LAPACK/BLAS API,并寻找适用于你的数据集的库。
你有很多选择:LAPACK/BLAS、GSL和其他自我优化库、供应商库等等。

1

我对这些库不是很熟悉。但你应该考虑到,库通常会在参数方面进行一些测试,它们有一个“通信系统”来处理错误,甚至在调用函数时分配新变量... 如果计算是琐碎的,也许你可以尝试自己做,根据你的需求进行适应...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接