问题已经从术语“表达式模板(ET)”开始。不知道有没有准确的定义。但在它的常见用法中,它以某种方式将“如何编码线性代数表达式”和“如何计算它”结合在一起。例如:
你编写向量操作
v = 2*x + 3*y + 4*z; // (1)
它由循环计算
for (int i=0; i<n; ++i) // (2)
v(i) = 2*x(i) + 3*y(i) + 4*z(i);
在我看来,这是两个不同的东西,需要解耦:(1)是一个接口,(2)一个可能的实现。我的意思是这是编程中的常见做法。当然(2)可能是一个很好的默认实现,但总的来说,我希望能够利用一个专门的、专用的实现。例如,我想要一个像
myGreatVecSum(alpha, x, beta, y, gamma, z, result); // (3)
当我编码时被调用(1)。也许(3)只是在内部使用了一个像(2)一样的循环。但根据向量大小,其他实现可能更有效。无论如何,一些高性能专家可以尽可能地实现和调整(3)。因此,如果 (1) 不能映射到 (3) 的调用,那么我宁愿避免使用 (1) 的语法糖并直接调用 (3)。
我所描述的并不是什么新鲜事。相反,这是 BLAS/LPACK 背后的理念:
- LAPACK 中的所有性能关键操作都是通过调用 BLAS 函数来完成的。
- BLAS 只是为那些常用的线性代数表达式定义了一个接口。
- 对于 BLAS,存在不同的优化实现。
如果 BLAS 的范围不够用(例如,它没有提供类似 (3) 的功能),那么可以扩展 BLAS 的范围。所以这个 60 和 70 年代的恐龙用它的石器时代工具实现了界面和实现的干净和正交的分离。(大多数)数字 C++ 库没有达到这种软件质量水平,这有点有趣。尽管编程语言本身要复杂得多。因此,BLAS/LAPACK 仍然活跃并积极开发也就不足为奇了。
所以在我看来,外星人本身并不邪恶。但是它们在数字 C++ 库中的普遍使用方式在科学计算界赢得了非常糟糕的声誉。