这些语言具有相似的功能集。性能差异来自于 Fortran 不允许使用别名,除非使用 EQUIVALENCE 语句。任何具有别名的代码都不是有效的 Fortran,但要由程序员而不是编译器来检测这些错误。因此,Fortran 编译器会忽略内存指针可能存在的别名,并允许它们生成更高效的代码。看看这个 C 语言的小例子:
void transform (float *output, float const * input, float const * matrix, int *n)
{
int i;
for (i=0; i<*n; i++)
{
float x = input[i*2+0];
float y = input[i*2+1];
output[i*2+0] = matrix[0] * x + matrix[1] * y;
output[i*2+1] = matrix[2] * x + matrix[3] * y;
}
}
优化后,该函数的运行速度将比 Fortran 函数慢。为什么这样?如果将值写入输出数组,则可以更改矩阵的值。毕竟,指针可以重叠并指向同一块内存(包括int
指针!)。 C 编译器被迫从内存中重新加载四个矩阵值以进行所有计算。
在 Fortran 中,编译器可以加载一次矩阵值并将它们存储在寄存器中。它可以这样做是因为 Fortran 编译器假设指针/数组在内存中不重叠。
幸运的是,restrictC99 标准中引入了关键字和严格别名来解决这个问题。现在大多数 C++ 编译器也都很好地支持它。该关键字允许您向编译器提供一个提示,即程序员承诺指针不会与任何其他指针别名。严格别名意味着程序员承诺不同类型的指针永远不会重叠,例如double*
不会与int*
(具体例外是char*
and void*
可以与任何东西重叠)。
如果您使用它们,您将获得与 C 和 Fortran 相同的速度。然而,能够使用restrict
仅具有性能关键函数的关键字意味着 C(和 C++)程序更安全且更容易编写。例如,考虑无效的 Fortran 代码:CALL TRANSFORM(A(1, 30), A(2, 31), A(3, 32), 30)
,大多数 Fortran 编译器都会在没有任何警告的情况下愉快地进行编译,但会引入一个错误,该错误仅在某些编译器、某些硬件和某些优化选项中出现。