我不时读到 Fortran 在繁重的计算中比 C 更快或可以更快。这是真的吗?我必须承认我几乎不了解 Fortran,但到目前为止我看到的 Fortran 代码并没有表明该语言具有 C 所没有的特性。
如果是真的,请告诉我为什么。请不要告诉我哪些语言或库对数字运算有好处,我不打算编写应用程序或库来做到这一点,我只是好奇。
我不时读到 Fortran 在繁重的计算中比 C 更快或可以更快。这是真的吗?我必须承认我几乎不了解 Fortran,但到目前为止我看到的 Fortran 代码并没有表明该语言具有 C 所没有的特性。
如果是真的,请告诉我为什么。请不要告诉我哪些语言或库对数字运算有好处,我不打算编写应用程序或库来做到这一点,我只是好奇。
这些语言具有相似的功能集。性能差异来自这样一个事实,即 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 编译器假定指针/数组在内存中不重叠。
幸运的是,restrict
C99 标准中引入了关键字和严格别名来解决这个问题。如今,大多数 C++ 编译器也很好地支持它。关键字允许您给编译器一个提示,即程序员承诺一个指针不会与任何其他指针产生别名。严格别名意味着程序员承诺不同类型的指针永远不会重叠,例如 adouble*
不会与 an 重叠(除了可以与任何东西重叠int*
的特定例外)。char*
void*
如果您使用它们,您将从 C 和 Fortran 获得相同的速度。但是,restrict
仅将关键字用于性能关键函数的能力意味着 C(和 C++)程序更安全、更容易编写。例如,考虑无效的 Fortran 代码:CALL TRANSFORM(A(1, 30), A(2, 31), A(3, 32), 30)
,大多数 Fortran 编译器会在没有任何警告的情况下愉快地编译它,但会引入一个仅在某些编译器、某些硬件和一些优化选项上出现的错误。
当我开始专业编程时,Fortran 的速度优势正受到挑战。我记得在 Dobbs 博士那里读到过这篇文章,并把这篇文章告诉了年长的程序员——他们笑了。
所以我对此有两种观点,理论和实践。从理论上讲,今天的 Fortran 对 C/C++ 甚至任何允许汇编代码的语言都没有内在优势。在实践中,今天的 Fortran 仍然享有围绕数字代码优化构建的历史和文化遗产的好处。
直到并包括 Fortran 77,语言设计考虑都将优化作为主要焦点。由于编译器理论和技术的现状,这通常意味着限制特性和功能,以便为编译器提供优化代码的最佳机会。一个很好的类比是将 Fortran 77 视为牺牲速度的专业赛车。这些天来,编译器在所有语言中都变得更好,并且程序员生产力的功能更受重视。但是,在科学计算中,人们主要关心速度的地方还是有的;这些人很可能从自己是 Fortran 程序员的人那里继承了代码、培训和文化。
当一个人开始谈论代码优化时,会有很多问题,而了解这一点的最佳方式是潜伏在人们的工作所在的地方,他们的工作就是拥有快速的数字代码。但请记住,这种极其敏感的代码通常只占整个代码行的一小部分,而且非常专业:许多 Fortran 代码与其他语言中的许多其他代码一样“低效”,甚至不应该进行优化此类代码的主要关注点。
维基百科是了解 Fortran 历史和文化的一个很好的起点。Fortran Wikipedia 条目非常棒,我非常感谢那些花时间和精力使其对 Fortran 社区有价值的人。
(这个答案的缩短版本将是Nils开始的优秀线程中的评论,但我没有业力这样做。实际上,我可能根本不会写任何东西,但因为这个线程有实际信息内容和分享,而不是火焰战争和语言偏见,这是我对这个主题的主要体验。我不知所措,不得不分享爱。)
在某种程度上,Fortran 的设计考虑了编译器优化。该语言支持编译器可以利用并行性的整个数组操作(特别是在多核处理器上)。例如,
密集矩阵乘法很简单:
matmul(a,b)
向量 x 的 L2 范数为:
sqrt(sum(x**2))
此外,诸如FORALL
, PURE
&ELEMENTAL
过程等语句进一步有助于优化代码。由于这个简单的原因,即使是 Fortran 中的指针也不像 C 那样灵活。
即将推出的 Fortran 标准 (2008) 具有协同数组,可让您轻松编写并行代码。G95(开源)和 CRAY 的编译器已经支持它。
所以是的,Fortran 可以很快,因为编译器可以比 C/C++ 更好地优化/并行化它。但是就像生活中的其他一切一样,有好的编译器和坏的编译器。
有趣的是,这里有很多不懂语言的答案。对于已经打开旧 FORTRAN 77 代码并讨论其弱点的 C/C++ 程序员来说尤其如此。
我想速度问题主要是 C/C++ 和 Fortran 之间的问题。在巨大的代码中,它总是取决于程序员。Fortran 语言的某些功能优于 C 语言的某些功能。所以,在 2011 年,没有人能真正说出哪一个更快。
关于语言本身,Fortran 现在支持完整的 OOP 功能,并且完全向后兼容。我已经彻底使用过 Fortran 2003,我会说使用它非常愉快。在某些方面,Fortran 2003 仍然落后于 C++,但让我们看看用法。Fortran 主要用于数值计算,由于速度原因,没有人使用花哨的 C++ OOP 功能。在高性能计算中,C++ 几乎无处可去(查看 MPI 标准,您会发现 C++ 已被弃用!)。
如今,您可以简单地使用 Fortran 和 C/C++ 进行混合语言编程。Fortran 中甚至还有 GTK+ 的接口。有免费的编译器(gfortran、g95)和许多优秀的商业编译器。
Fortran 速度更快的原因有很多。然而,它们的重要性是如此无关紧要,或者无论如何都可以解决,这不重要。现在使用 Fortran 的主要原因是维护或扩展遗留应用程序。
函数上的 PURE 和 ELEMENTAL 关键字。这些是没有副作用的功能。这允许在编译器知道将使用相同值调用相同函数的某些情况下进行优化。注意:GCC 实现“纯”作为语言的扩展。其他编译器也可以。模块间分析也可以执行这种优化,但很难。
处理数组而不是单个元素的标准函数集。sin()、log()、sqrt() 之类的东西采用数组而不是标量。这使得优化例程变得更容易。如果这些函数是内联的或内置的,则自动矢量化在大多数情况下会带来相同的好处
内置复杂类型。从理论上讲,这可以允许编译器在某些情况下重新排序或消除某些指令,但您可能会看到与struct { double re; double im; };
C 中使用的习语相同的好处。尽管运算符在 Fortran 中处理复杂类型,但它可以加快开发速度。
我认为支持 Fortran 的关键在于它是一种更适合表达基于向量和数组的数学的语言。上面指出的指针分析问题在实践中是真实存在的,因为可移植代码不能真正假设您可以告诉编译器一些事情。以更接近域外观的方式表达计算总是有优势的。C 根本没有数组,如果你仔细观察,就会发现它的行为类似于它。Fortran 有真正的 arrawys。这使得编译某些类型的算法更容易,尤其是并行机器。
在运行时系统和调用约定等方面,C 和现代 Fortran 非常相似,很难看出有什么不同。请注意,这里的 C 实际上是基础 C:C++ 是一个完全不同的问题,具有非常不同的性能特征。
没有一种语言比另一种更快,所以正确的答案是no。
您真正要问的是“使用 Fortran 编译器 X 编译的代码是否比使用 C 编译器 Y 编译的等效代码更快?” 这个问题的答案当然取决于您选择哪两个编译器。
人们可能会问的另一个问题是“考虑到在他们的编译器中进行优化的相同数量的努力,哪个编译器会产生更快的代码?” 答案实际上是Fortran。Fortran 编译器具有一定的优势:
然而,没有什么能阻止人们在 C 编译器的优化上投入大量精力,并使其生成比他们平台的 Fortran 编译器更好的代码。事实上,C 编译器产生的更大销量使这种情况非常可行
Fortran 与 C 不同,还有一个项目可能更快。Fortran 比 C 有更好的优化规则。在 Fortran 中,表达式的求值顺序是没有定义的,这允许编译器对其进行优化——如果想要强制某个顺序,则必须使用括号。在 C 中,顺序要严格得多,但使用“-fast”选项时,它们会更加宽松,并且“(...)”也会被忽略。我认为 Fortran 有一种很好的方法。(嗯,IEEE 使实时变得更加困难,因为某些评估顺序更改要求不发生溢出,这要么必须被忽略,要么妨碍评估)。
另一个更智能的规则领域是复数。不仅 C 直到 C 99 才拥有它们,而且管理它们的规则在 Fortran 中也更好;由于 gfortran 的 Fortran 库部分用 C 编写,但实现了 Fortran 语义,因此 GCC 获得了选项(也可以与“普通”C 程序一起使用):
-fcx-fortran-rules 复数乘法和除法遵循 Fortran 规则。范围缩小是作为复数除法的一部分完成的,但没有检查复数乘法或除法的结果是否为“NaN + I*NaN”,试图挽救这种情况。
上面提到的别名规则是另一个好处,而且 - 至少在原则上 - 整个数组操作,如果编译器的优化器适当考虑,可以导致更快的代码。另一方面是某些操作需要更多时间,例如,如果对可分配数组进行分配,则需要进行大量检查(重新分配?[Fortran 2003 功能],具有数组步幅等),这使得简单的操作在幕后更复杂 - 因此速度较慢,但使语言更强大。另一方面,具有灵活边界和跨步的数组操作使得编写代码更容易——而且编译器通常比用户更好地优化代码。
总的来说,我认为 C 和 Fortran 的速度差不多。选择应该更多是更喜欢哪种语言,或者使用 Fortran 的整个数组操作及其更好的可移植性是否更有用——或者更好地与 C 中的系统和图形用户界面库接口。
我将 Fortran、C 和 C++ 的速度与 netlib 中的经典 Levine-Callahan-Dongarra 基准进行了比较。带有 OpenMP 的多语言版本是 http://sites.google.com/site/tprincesite/levine-callahan-dongarra-vectors C 更丑,因为它从自动翻译开始,加上某些限制和 pragma 的插入编译器。C++ 只是在适用的情况下带有 STL 模板的 C。在我看来,STL 是否能提高可维护性是好坏参半。
自动函数内联只需要很少的练习即可查看它在多大程度上改进了优化,因为这些示例基于传统的 Fortran 实践,几乎不依赖内联。
迄今为止使用最广泛的 C/C++ 编译器缺乏自动矢量化,这些基准测试严重依赖自动矢量化。
重新发布之前的帖子:有几个示例在 Fortran 中使用括号来指示更快或更准确的评估顺序。已知的 C 编译器没有选项来观察括号而不禁用更重要的优化。
Fortran 和 C 之间的任何速度差异将更多地取决于编译器优化和特定编译器使用的底层数学库。Fortran 本身没有任何东西可以使它比 C 更快。
无论如何,一个好的程序员可以用任何语言编写 Fortran。
Fortran和 C语言在特定目的上比另一种更快。对于这些语言中的每一种,都有一些关于特定编译器的事情,这使得某些任务比其他语言更适合某些任务。
多年来,Fortran 编译器一直存在,它们可以对您的数字例程施展魔法,使许多重要的计算变得异常快速。当代的 C 编译器也做不到这一点。结果,在 Fortran 中出现了许多出色的代码库。如果您想使用这些经过良好测试、成熟、出色的库,您可以使用 Fortran 编译器。
我的非正式观察表明,如今人们用任何旧语言编写繁重的计算内容,如果需要一段时间,他们会在一些廉价的计算集群上找到时间。摩尔定律让我们所有人都傻了眼。
我是一个业余程序员,我在两种语言上都“平均”。我发现编写快速的 Fortran 代码比编写 C(或 C++)代码更容易。Fortran 和 C 都是“历史”语言(按照今天的标准),被大量使用,并且有很好的免费和商业编译器支持。
我不知道这是否是一个历史事实,但 Fortran 感觉它是为并行/分布式/矢量化/whatever-many-cores-ized 而构建的。今天,当我们谈论速度时,它几乎是“标准指标”:“它可以扩展吗?”
对于纯 CPU 运算,我喜欢 Fortran。对于任何与 IO 相关的事情,我发现使用 C 更容易。(无论如何这两种情况都很难)。
当然,对于并行数学密集型代码,您可能希望使用 GPU。C 和 Fortran 都有很多或多或少集成良好的 CUDA/OpenCL 接口(现在是 OpenACC)。
我的客观答案是:如果你对这两种语言的了解同样好/很差,那么我认为 Fortran 更快,因为我发现用 Fortran 编写并行/分布式代码比 C 更容易。(一旦你明白你可以编写“自由格式”fortran 和不仅仅是严格的 F77 代码)
对于那些愿意对我投反对票的人,这是第二个答案,因为他们不喜欢第一个答案:两种语言都具有编写高性能代码所需的功能。因此,它取决于您正在实施的算法(cpu 密集型?io 密集型?内存密集型?),硬件(单 cpu ?多核?分布式超级计算机?GPGPU ?FPGA ?),您的技能以及最终的编译器本身。C 和 Fortran 都有很棒的编译器。(我对 Fortran 编译器的先进程度感到非常惊讶,但 C 编译器也是如此)。
PS:我很高兴你特别排除了库,因为我有很多关于 Fortran GUI 库的坏话要说。:)
快速简单: 两者都同样快,但 Fortran 更简单。 到底什么更快取决于算法,但无论如何速度没有相当大的差异。这是我 2015 年在德国斯图加德高性能计算中心的 Fortran 研讨会上学到的。我同时使用 Fortran 和 C 并分享这一观点。
解释:
C 旨在编写操作系统。因此,它比编写高性能代码所需的自由更多。一般来说,这没有问题,但如果不仔细编程,很容易减慢代码速度。
Fortran 专为科学编程而设计。出于这个原因,它支持以语法方式编写快速代码,因为这是 Fortran 的主要目的。与舆论相反,Fortran 并不是一种过时的编程语言。它的最新标准是 2010 年,并且会定期发布新的编译器,因为大多数高性能代码都是用 Fortran 编写的。Fortran 进一步支持现代功能作为编译器指令(在 C 编译指示中)。
示例: 我们希望将一个大型结构作为函数的输入参数(fortran:子例程)。在函数内,参数不会改变。
C 支持按引用调用和按值调用,这是一个方便的特性。在我们的例子中,程序员可能会不小心使用按值调用。这大大减慢了速度,因为需要首先将结构复制到内存中。
Fortran 仅使用按引用调用,如果程序员真的想要按值调用操作,这会强制程序员手动复制结构。在我们的例子中,fortran 将自动与 C 版本一样快,通过引用调用。
几年来,我一直在用 FORTRAN 和 C 做一些广泛的数学。根据我自己的经验,我可以说 FORTRAN 有时确实比 C 更好,但不是因为它的速度(通过使用适当的编码风格可以使 C 的性能与 FORTRAN 一样快),而是因为 LAPACK 等优化得非常好的库,并且因为伟大的并行化。在我看来,FORTRAN 真的不好用,它的优点还不足以抵消这个缺点,所以现在我用 C+GSL 来做计算。
我没有听说 Fortan 比 C 快得多,但可以想象在某些情况下它会更快。关键不在于存在的语言特征,而在于(通常)不存在的语言特征。
一个例子是 C 指针。C 指针几乎无处不在,但指针的问题是编译器通常无法判断它们是否指向同一个数组的不同部分。
例如,如果您编写了一个如下所示的 strcpy 例程:
strcpy(char *d, const char* s)
{
while(*d++ = *s++);
}
编译器必须在 d 和 s 可能是重叠数组的假设下工作。因此,当数组重叠时,它无法执行会产生不同结果的优化。正如您所料,这极大地限制了可以执行的优化类型。
[我应该注意到 C99 有一个“restrict”关键字,它明确告诉编译器指针不重叠。另请注意,Fortran 也有指针,其语义与 C 不同,但指针不像 C 中那样无处不在。]
但是回到 C 与 Fortran 的问题,可以想象 Fortran 编译器能够执行一些优化,而这些优化对于(直接编写的)C 程序来说可能是不可能的。所以我不会对这个说法感到太惊讶。但是,我确实希望性能差异不会那么大。[~5-10%]
通常 FORTRAN 比 C 慢。C 可以使用允许程序员手动优化的硬件级指针。FORTRAN(在大多数情况下)无法访问硬件内存寻址黑客。(VAX FORTRAN 是另一回事。)自 70 年代以来,我一直在使用 FORTRAN。(真的。)
然而,从 90 年代开始,FORTRAN 已经发展到包含特定的语言结构,这些结构可以优化成固有的并行算法,可以真正在多核处理器上尖叫。例如,自动向量化允许多个处理器同时处理数据向量中的每个元素。16 个处理器——16 个元素向量——处理时间仅为 1/16。
在 C 语言中,您必须管理自己的线程并仔细设计算法以进行多处理,然后使用一堆 API 调用来确保并行性正确发生。
在 FORTRAN 中,您只需为多处理仔细设计算法。编译器和运行时可以为您处理其余部分。
您可以阅读一些有关高性能 Fortran的信息,但您会发现很多死链接。您最好阅读有关并行编程(如OpenMP.org)以及 FORTRAN 如何支持它的信息。
更快的代码实际上并不取决于语言,而是编译器,因此您可以看到 ms-vb“编译器”生成臃肿、缓慢和冗余的目标代码,这些代码在“.exe”中捆绑在一起,但 powerBasic 生成方式太更好的代码。由 C 和 C++ 编译器生成的目标代码在某些阶段(至少 2 个)生成,但根据设计,大多数 Fortran 编译器至少有 5 个阶段,包括高级优化,因此 Fortran 将始终具有生成高度优化代码的能力。所以最后是编译器不是你应该要求的语言,我知道的最好的编译器是英特尔 Fortran 编译器,因为你可以在 LINUX 和 Windows 上得到它,你可以使用 VS 作为 IDE,如果你正在寻找一个廉价紧凑的编译器,您可以随时在 OpenWatcom 上转发。
有关此的更多信息:http: //ed-thelen.org/1401Project/1401-IBM-Systems-Journal-FORTRAN.html
Fortran 具有更好的 I/O 例程,例如隐含的 do 工具提供了 C 标准库无法比拟的灵活性。
Fortran 编译器直接处理涉及的更复杂的语法,并且由于这种语法不能轻易地简化为参数传递形式,C 不能有效地实现它。
使用现代标准和编译器,不!
这里的一些人建议 FORTRAN 更快,因为编译器不需要担心别名(因此可以在优化期间做出更多假设)。但是,自 C99(我认为)标准以来,这已在 C 中处理,并包含了 restrict 关键字。这基本上告诉编译器,在给定范围内,指针没有别名。此外,C 支持正确的指针算法,其中别名之类的东西在性能和资源分配方面非常有用。虽然我认为最新版本的 FORTRAN 可以使用“正确”指针。
对于现代实现,C 一般优于 FORTRAN(尽管它也非常快)。
http://benchmarksgame.alioth.debian.org/u64q/fortran.html
编辑:
对此的公平批评似乎是基准测试可能存在偏见。这是另一个将结果放在更多上下文中的来源(相对于 C):
http://julialang.org/benchmarks/
您可以看到,在大多数情况下,C 通常优于 Fortran(再次参见下面的批评,也适用于此处);正如其他人所说,基准测试是一门不精确的科学,可以轻松加载以偏爱一种语言而不是其他语言。但它确实说明了 Fortran 和 C 如何具有相似的性能。
Fortran 可以非常方便地处理数组,尤其是多维数组。在 Fortran 中对多维数组的元素进行切片比在 C/C++ 中要容易得多。C++ 现在有库可以完成这项工作,例如 Boost 或 Eigen,但它们毕竟是外部库。在 Fortran 中,这些函数是固有的。
Fortran 是否更快或更方便开发主要取决于您需要完成的工作。作为地球物理学的科学计算人员,我在 Fortran 中进行了大部分计算(我的意思是现代 Fortran,>=F90)。
这有点主观,因为它涉及编译器的质量等等。但是,为了更直接地回答您的问题,从语言/编译器的角度来看,Fortran 与 C 相比没有任何东西可以使其固有地比 C 更快或更好。如果您正在进行繁重的数学运算,它将归结为编译器的质量,每种语言程序员的技能以及支持这些操作的内在数学支持库最终确定对于给定实现哪个更快。
编辑:@Nils 等其他人提出了关于在 C 中使用指针的差异以及可能使 C 中最幼稚的实现变慢的别名的可能性的好点。但是,在 C99 中有一些方法可以解决这个问题,通过编译器优化标志和/或 C 的实际编写方式。这在@Nils 的回答和他的回答之后的后续评论中得到了很好的介绍。
大多数帖子已经提出了令人信服的论点,所以我只是将众所周知的 2 美分添加到不同的方面。
最终,在处理能力方面使 fortran 更快或更慢可能有其重要性,但如果在 Fortran 中开发某些东西需要多 5 倍的时间,因为:
那么这个问题就无关紧要了。如果某些东西很慢,大多数时候你无法将它改进到超出给定的限制。如果您想要更快的速度,请更改算法。最后,计算机时间很便宜。人类时间不是。重视减少人力时间的选择。如果它增加了计算机时间,无论如何它是具有成本效益的。
Fortran 传统上不设置诸如 -fp:strict 之类的选项(ifort 需要它来启用 USE IEEE_arithmetic 中的某些功能,这是 f2003 标准的一部分)。英特尔 C++ 也没有将 -fp:strict 设置为默认值,但这是处理 ERRNO 所必需的,例如,其他 C++ 编译器无法方便地关闭 ERRNO 或获得优化,例如减少 simd。gcc 和 g++ 要求我设置 Makefile 以避免使用危险的组合 -O3 -ffast-math -fopenmp -march=native。除了这些问题之外,这个关于相对性能的问题变得更加挑剔,并且依赖于有关编译器和选项选择的本地规则。