对于繁重的计算,Fortran是否比C更容易优化?

时间:2008-09-28 16:02:53

标签: c performance fortran

我不时会读到Fortran是或者可以比C更快进行繁重的计算。这是真的吗?我必须承认我几乎不知道Fortran,但到目前为止我看到的Fortran代码并没有表明该语言具有C所没有的功能。

如果确实如此,请告诉我原因。请不要告诉我哪些语言或库对数字运算有好处,我不打算写一个app或lib来做那个,我只是很好奇。

23 个答案:

答案 0 :(得分:424)

这些语言具有相似的功能集。性能差异来自于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编译器假定指针/数组在内存中不重叠。

幸运的是,{C}标准引入了restrict关键字和严格别名以解决此问题。如今,它在大多数C ++编译器中得到了很好的支持。该关键字允许您向编译器提供程序员承诺指针不与任何其他指针进行别名的提示。严格别名意味着程序员承诺不同类型的指针永远不会重叠,例如double*不会与int*重叠(具有char*和{{的特定例外情况1}}可以与任何东西重叠。

如果您使用它们,您将从C和Fortran获得相同的速度。但是,仅将void*关键字用于性能关键功能的能力意味着C(和C ++)程序更安全,更易于编写。例如,考虑一下无效的Fortran代码:restrict,大多数Fortran编译器都会很乐意在没有任何警告的情况下进行编译,但会引入一个只出现在某些编译器,某些硬件和一些优化选项上的错误。

答案 1 :(得分:155)

是的,1980年;在2008?取决于

当我开始专业编程时,Fortran的速度优势正受到挑战。我记得reading about it in Dr. Dobbs并告诉老程序员这篇文章 - 他们笑了。

所以我对此有两点看法,理论和实践。 理论上今天的Fortran对C / C ++甚至任何允许汇编代码的语言都没有内在的优势。 在实践中今天的Fortran仍然享有围绕数字代码优化而建立的历史和文化遗产的好处。

直到并包括Fortran 77,语言设计考虑将优化作为主要关注点。由于编译器理论和技术的状态,这通常意味着限制特性和功能,以便为编译器提供优化代码的最佳机会。一个很好的比喻是将Fortran 77视为专业赛车,为速度牺牲功能。这些天编译器在所有语言中都有所改进,并且程序员生产力的功能更受重视。然而,仍有一些地方人们主要关注科学计算的速度;这些人很可能从他们自己是Fortran程序员的人那里继承了代码,培训和文化。

当一个人开始讨论代码优化问题时,会遇到很多问题,最好的方法是to lurk where people are whose job it is to have fast numerical code。但请记住,这些极其敏感的代码通常只占整个代码行的一小部分并且非常专业:很多Fortran代码与其他语言中的许多其他代码一样“低效”并optimization should not even be a primary concern of such code

开始学习Fortran的历史和文化的一个好地方是维基百科。 The Fortran Wikipedia entry非常棒,我非常感谢那些花时间和精力为Fortran社区创造价值的人。

(这个答案的缩短版本应该是由 Nils 开始的优秀帖子中的评论,但我没有这样做的业力。实际上,我可能不会写什么都没有,但是因为这个帖子有实际的信息内容和分享,而不是火焰战争和语言偏见,这是我对这个主题的主要体验。我不知所措,不得不分享爱情。)

答案 2 :(得分:60)

在某种程度上,Fortran的设计考虑了编译器优化。该语言支持整个阵列操作,其中编译器可以利用并行性(特别是在多核处理器上)。例如,

密集矩阵乘法很简单:

matmul(a,b)

向量x的L2范数是:

sqrt(sum(x**2))

此外,FORALLPURE和&amp; ELEMENTAL程序等进一步帮助优化代码。由于这个简单的原因,即使Fortran中的指针也不像C那样灵活。

即将推出的Fortran标准(2008)具有共阵列,可让您轻松编写并行代码。来自CRAY的G95(开源)和编译器已经支持它。

所以是的,Fortran可以很快,因为编译器可以比C / C ++更好地优化/并行化它。但是,就像生活中的其他一切一样,有很好的编译器和糟糕的编译器。

答案 3 :(得分:37)

很有趣,很多答案来自于不了解语言。对于已经打开旧FORTRAN 77代码并讨论其弱点的C / C ++程序员来说尤其如此。

我认为速度问题主要是C / C ++和Fortran之间的问题。在一个巨大的代码中,它总是取决于程序员。 Fortran的一些功能表现优于C语言的功能。所以,在2011年,没有人能真正说出哪一个更快。

关于语言本身,Fortran现在支持Full OOP功能,并且完全向后兼容。我彻底使用了Fortran 2003,我想说使用它很愉快。在某些方面,Fortran 2003仍然落后于C ++,但让我们来看看它的用法。 Fortran主要用于数值计算,由于速度原因,没有人使用花哨的C ++ OOP功能。在高性能计算中,C ++几乎没有任何地方可去(看看MPI标准,你会发现C ++已被弃用!)。

如今,您可以使用Fortran和C / C ++进行混合语言编程。 Fortran中甚至还有GTK +接口。有免费的编译器(gfortran,g95)和许多优秀的商业编译器。

答案 4 :(得分:28)

Fortran可能更快的原因有多种。然而,它们重要的数量是如此无关紧要或无论如何都可以解决,它应该无关紧要。现在使用Fortran的主要原因是维护或扩展遗留应用程序。

  • 功能上的PURE和ELEMENTAL关键字。这些功能没有副作用。这允许在某些情况下优化,其中编译器知道将使用相同的值调用相同的函数。 注意:GCC实现“纯”作为语言的扩展。其他编译器也可以。模块间分析也可以执行此优化,但很难。

  • 处理数组的标准函数集,而不是单个元素。像sin(),log(),sqrt()这样的东西需要数组而不是标量。这样可以更轻松地优化例程。 如果这些函数是内联函数或内置函数,则自动向量化在大多数情况下都会带来相同的好处

  • 内置复合型。从理论上讲,这可能允许编译器在某些情况下重新排序或消除某些指令,但是你可能会看到结构{double re,im; };在C中使用的成语虽然运算符在fortran中处理复杂类型,但它可以加快开发速度。

答案 5 :(得分:27)

我认为支持Fortran的关键点在于它是一种稍微适合表达基于矢量和数组的数学的语言。上面指出的指针分析问题在实践中是真实的,因为可移植代码不能真正假设您可以告诉编译器一些东西。以更接近域的外观的方式表达计算总是有利的。如果仔细观察,C根本就没有数组,只是像它那样的行为。 Fortran有真正的图纸。这使得编译某些类型的算法变得更容易,特别是对于并行机器。

在运行时系统和调用约定之类的内容中,C和现代Fortran非常相似,很难看出会产生什么影响。请注意,这里的C实际上是基本C:C ++是一个完全不同的问题,具有非常不同的性能特征。

答案 6 :(得分:23)

没有一种语言比另一种语言更快,所以正确的答案是

您真正需要问的是“使用Fortran编译器X编译的代码比使用C编译器Y编译的等效代码快吗?”这个问题的答案当然取决于你选择哪两个编译器。

人们可能会问的另一个问题是“考虑到在编译器中进行优化所需的相同工作量,哪个编译器会产生更快的代码?” 对此的答案实际上是 Fortran 。 Fortran编译器具有certian优势:

  • 当有些人发誓永远不会使用编译器时,Fortran必须与大会竞争,所以它是专为速度而设计的。 C的设计灵活。
  • Fortran的利基已被数字碾压。在此域代码永远不会足够快。因此,保持语言效率始终存在很大的压力。
  • 编译器优化的大部分研究都是由对加速Fortran数字运算代码感兴趣的人完成的,因此优化Fortran代码是一个比优化任何其他编译语言更为人熟知的问题,并且新的创新首先出现在Fortran编译器中。
  • Biggie :C鼓励使用比Fortran更多的指针。这极大地增加了C程序中任何数据项的潜在范围,这使得它们更难以优化。请注意,在这个领域,Ada也比C更好,并且是比常见的Fortran77更现代的OO语言。如果你想要一个可以生成比C更快的代码的OO语言,那么这是一个选项。
  • 再次由于它的数字运算利基,Fortran编译器的客户往往更关心优化而不是C编译器的客户。

但是,没有什么能阻止某人在他们的C编译器优化中花费大量精力,并使其生成比其平台的Fortran编译器更好的代码。事实上,C编译器产生的较大销售额使这种情况非常可行

答案 7 :(得分:22)

还有另一个项目,其中Fortran与C不同 - 并且可能更快。 Fortran具有比C更好的优化规则。在Fortran中,没有定义表达式的评估顺序,这允许编译器对其进行优化 - 如果想要强制某个顺序,则必须使用括号。在C中,顺序要严格得多,但是使用“-fast”选项,它们会更放松,“(...)”也会被忽略。我认为Fortran有一种很好的中间位置。 (好吧,由于某些评估顺序的变化要求不发生溢出,要么必须忽略或妨碍评估),IEEE会使现场变得更加困难。

智慧规则的另一个领域是复数。不仅C到C 99还有它们,在Fortran中管理它们的规则也更好;由于gfortran的Fortran库部分用C语言编写,但实现了Fortran语义,因此GCC获得了选项(也可以与“普通”C程序一起使用):

  

-fcx-FORTRAN规则   复数乘法和除法遵循Fortran规则。范围缩减是作为复杂除法的一部分完成的,但是没有检查复数乘法或除法的结果是否是“NaN + I * NaN”,并试图在这种情况下挽救情况。

上面提到的别名规则是另一个好处,并且 - 至少在原则上 - 全数组操作,如果编译器的优化器正确考虑,可以导致更快的代码。另一方面,某些操作需要更多时间,例如如果对可分配数组进行赋值,则需要进行大量检查(重新分配?[Fortran 2003特性],数组步长等),这使得简单操作在幕后变得更加复杂 - 因此速度较慢,但​​是使语言更强大。另一方面,具有灵活边界和跨度的数组操作使编写代码变得更容易 - 编译器通常比用户更好地优化代码。

总的来说,我认为C和Fortran的速度差不多;选择应该是更多的语言,或者使用Fortran的整个数组操作及其更好的可移植性是否更有用 - 或者更好地连接到C中的系统和图形用户界面库。

答案 8 :(得分:14)

没有任何关于语言 Fortran和C的内容,它们比其他特定目的更快。对于这些语言中的每一种语言都有特定的编译器,这使得某些任务比其他语言更有利。

多年来,Fortran编译器已经存在,可以为您的数字例程做出黑魔法,使得许多重要的计算速度非常快。当代的C编译器也做不到。结果,Fortran中出现了许多优秀的代码库。如果你想使用这些经过良好测试,成熟,精彩的库,你可以打破Fortran编译器。

我的非正式观察表明,现在人们用任何旧语言编写繁重的计算资料,如果需要一段时间,他们会在一些廉价的计算集群上找到时间。摩尔定律使我们所有人都变得愚蠢。

答案 9 :(得分:13)

我将Fortran,C和C ++的速度与netlib的经典Levine-Callahan-Dongarra基准进行比较。使用OpenMP的多语言版本是 http://sites.google.com/site/tprincesite/levine-callahan-dongarra-vectors C更加丑陋,因为它从自动翻译开始,再加上某些编译器的restrict和pragma插入。 在适用的情况下,C ++只是带有STL模板的C.在我看来,STL是否可以提高可维护性。

自动函数内嵌只有极少的运动,以确定它在多大程度上改进了优化,因为这些示例基于传统的Fortran实践,其中很少依赖于内嵌。

目前使用最广泛的C / C ++编译器缺乏自动向量化,这些基准测试依赖于自动向量化。

重新发布之前的帖子:有几个例子,其中在Fortran中使用括号来指示更快或更准确的评估顺序。已知的C编译器没有选项来观察括号而不禁用更重要的优化。

答案 10 :(得分:11)

我是一个业余爱好者程序员,我平均而且#34;两种语言。 我发现编写快速Fortran代码比编写C(或C ++)代码更容易。 Fortran和C都是&#34;历史悠久的&#34;语言(按今天标准),使用频繁,并且支持免费和商业编译器。

我不知道这是否是一个历史事实,但Fortran觉得它的构建是并行/分布式/矢量化/无论多少核心化。而今天它几乎是标准的衡量标准&#34;当我们谈论速度时:&#34;它是否会扩展?&#34;

对于纯粹的cpu运算,我喜欢Fortran。对于与IO相关的任何事情,我发现使用C更容易。(无论如何,它都很困难。)

当然,对于并行数学密集型代码,您可能希望使用GPU。 C和Fortran都有很多或多或少集成的CUDA / OpenCL接口(现在是OpenACC)。

我的客观答案是:如果你同时知道两种语言同样好/那么我认为Fortran更快,因为我发现在Fortran中编写并行/分布式代码比在C中更容易。(一旦你明白你可以写&# 34; freeform&#34; fortran而不仅仅是严格的F77代码)

对于那些愿意贬低我的人来说,这是第二个答案,因为他们不喜欢第一个答案:两种语言都具有编写高性能代码所需的功能。所以它依赖于您正在实施的算法(CPU密集型?密集型?内存密集型?),硬件(单CPU?多核?分配超级计算机?GPGPU?FPGA?),您的技能和最终是编译器本身。 C和Fortran都有很棒的编译器。 (我很惊讶Fortran编译器的先进性,但C编译器也是如此)。

PS:我很高兴您明确地排除了libs,因为我对Fortran GUI库有很多不好的说法。 :)

答案 11 :(得分:11)

我用FORTRAN和C做了几年的广泛数学。根据我自己的经验,我可以说FORTRAN有时候确实比C好,但不是因为它的速度(可以通过使用适当的编码风格使C表现得像FORTRAN一样快),而是因为LAPACK等非常优化的库,并且因为很好的并行化。在我看来,FORTRAN真的很难处理,它的优点还不足以取消这个缺点,所以现在我用C + GSL来做计算。

答案 12 :(得分:10)

我没有听说过Fortan比C快得多,但可以想象在某些情况下它会更快。关键不在于存在的语言特征,而在于(通常)不存在的语言特征。

一个例子是C指针。 C指针几乎无处不在,但指针的问题在于编译器通常无法判断它们是否指向同一数组的不同部分。

例如,如果您编写了一个如下所示的strcpy例程:

strcpy(char *d, const char* s)
{
  while(*d++ = *s++);
}

编译器必须在假设d和s可能是重叠数组的情况下工作。因此,当阵列重叠时,它无法执行会产生不同结果的优化。正如您所期望的那样,这极大地限制了可以执行的优化类型。

[我应该注意到C99有一个“restrict”关键字,它明确告诉编译器指针不重叠。另请注意,Fortran也有指针,其语义与C语言不同,但指针并不像C中那样普遍存在。]

但回到C vs. Fortran问题,可以想象Fortran编译器能够执行某些(直接编写的)C程序可能无法实现的优化。所以我对这个说法不会感到惊讶。但是,我确实希望性能差异不会那么大。 [〜5-10%]

答案 13 :(得分:9)

Fortran和C之间的任何速度差异都将更多地取决于编译器优化和特定编译器使用的基础数学库。 Fortran没有任何内在功能可以使它比C更快。

无论如何,一个优秀的程序员可以用任何语言编写Fortran。

答案 14 :(得分:9)

快速而简单: 两者同样快,但Fortran更简单。 最终真的更快取决于算法,但无论如何都没有相当大的速度差异。这是我在2015年德国Stuttgard高性能计算中心的Fortran研讨会上学到的。我与Fortran和C共同工作并分享了这一观点。

<强>解释

C旨在编写操作系统。因此,它具有比编写高性能代码所需的更多自由度。一般情况下这没有问题,但如果没有仔细编程,可以轻松减慢代码。

Fortran专为科学编程而设计。出于这个原因,它支持在语法上编写快速代码,因为这是Fortran的主要目的。与公众舆论相反,Fortran不是一种过时的编程语言。它的最新标准是2010年,并且定期发布新的编译器,因为大多数高性能代码都是在Fortran中编写的。 Fortran further supports modern features as compiler directives (in C pragmas).

示例: 我们想要一个大型结构作为函数的输入参数(fortran:subroutine)。在函数内,参数不会改变。

C支持两者,通过引用调用和按值调用,这是一个方便的功能。在我们的例子中,程序员可能会偶然使用按值调用。这会大大减慢速度,因为结构需要先在内存中复制。

Fortran只能通过引用调用,这会强制程序员手动复制结构,如果他真的想要通过值操作调用。在我们的例子中,fortran将自动与C版本一样快,并通过引用调用。

答案 15 :(得分:7)

通常FORTRAN比C慢.C可以使用硬件级指针,允许程序员手动优化。 FORTRAN(在大多数情况下)无法访问硬件内存寻址黑客。 (VAX FORTRAN是另一个故事。)自70年代以来,我一直使用FORTRAN。 (真)。

然而,从90年代开始,FORTRAN已经发展到包含特定的语言结构,可以优化为可以在多核处理器上真正尖叫的固有并行算法。例如,自动矢量化允许多个处理器同时处理数据向量中的每个元素。 16个处理器 - 16个元素矢量 - 处理需要1/16的时间。

在C中,您必须管理自己的线程并仔细设计算法以进行多处理,然后使用一堆API调用来确保并行性正确发生。

在FORTRAN中,您只需仔细设计算法以进行多处理。编译器和运行时可以为您处理剩下的事情。

您可以阅读一些关于High Performance Fortran的内容,但是您会发现很多死链接。你最好阅读并行编程(如OpenMP.org)以及FORTRAN如何支持它。

答案 16 :(得分:5)

更快的代码并不真正取决于语言,编译器是如此,你可以看到ms-vb“编译器”生成膨胀,速度较慢和冗余的目标代码,这些代码在“.exe”中绑定在一起,但是powerBasic生成更好的代码。 由C和C ++编译器生成的目标代码在某些阶段(至少2个)生成,但是通过设计,大多数Fortran编译器至少有5个阶段,包括高级优化,因此通过设计,Fortran将始终能够生成高度优化的代码。 所以最后编译器不是你应该要求的语言,我所知道的最好的编译器是英特尔Fortran编译器,因为你可以在LINUX和Windows上获得它,你可以使用VS作为IDE,如果你正在寻找一个便宜的Tigh编译器,你可以随时在OpenWatcom上转发。

有关此内容的更多信息: http://ed-thelen.org/1401Project/1401-IBM-Systems-Journal-FORTRAN.html

答案 17 :(得分:3)

Fortran有更好的I / O例程,例如暗示的设施提供了C标准库无法匹配的灵活性。

Fortran编译器直接处理更复杂的问题 涉及的语法,因此不能轻易减少语法 对于参数传递形式,C无法有效地实现它。

答案 18 :(得分:2)

使用现代标准和编译器,没有!

这里的一些人建议FORTRAN更快,因为编译器不需要担心别名(因此可以在优化期间做出更多假设)。但是,自从包含restrict关键字的C99(我认为)标准以来,这已在C中处理。这基本上告诉编译器,在给定范围内,指针没有别名。此外,C启用了正确的指针算法,其中像别名这样的东西在性能和资源分配方面非常有用。虽然我认为更新版本的FORTRAN可以使用&#34;适当的&#34;指针。

对于现代实现,C general的表现优于FORTRAN(尽管它也非常快)。

http://benchmarksgame.alioth.debian.org/u64q/fortran.html

编辑:

对此的公平批评似乎是基准测试可能存在偏见。这是另一个源(相对于C),它将结果放在更多上下文中:

http://julialang.org/benchmarks/

在大多数情况下,您可以看到C通常优于Fortran(同样请参阅下面的批评也适用于此);正如其他人所说,基准测试是一种不精确的科学,可以很容易地加载到一种语言而不是其他语言。但它确实说明了Fortran和C在性能上是如何相似的。

答案 19 :(得分:2)

Fortran可以非常方便地处理数组,尤其是多维数组。 Fortran中的多维数组切片元素比C / C ++中的切片元素容易得多。 C ++现在拥有可以完成工作的库,例如Boost或Eigen,但是它们毕竟是所有外部库。在Fortran中,这些功能是固有的。

Fortran的开发速度更快或更方便主要取决于您需要完成的工作。作为地球物理学的科学计算人员,我在Fortran中完成了大部分计算工作(我的意思是现代Fortran,> = F90)。

答案 20 :(得分:1)

这在某种程度上是主观的,因为它进入了编译器的质量,而不是其他任何东西。但是,为了更直接地回答你的问题,从语言/编译器的角度来讲,没有任何关于Fortran over C会使它本身比C更快或更好。如果你正在进行繁重的数学运算,它将归结为编译器的质量,每种语言的程序员的技能以及支持这些操作的内在数学支持库,以最终确定哪一个对于给定的实现更快。

编辑:像@Nils这样的其他人提出了关于C中指针使用差异的好处以及混淆的可能性,这可能会使C中最天真的实现变慢。但是,有办法处理在C99中,通过编译器优化标志和/或实际写入C的方式。 @Nils答案以及随后的答案中都有详细介绍。

答案 21 :(得分:0)

大多数帖子已经提出了令人信服的论据,所以我只会在另一方面加上众所周知的2美分。

最终处理能力的速度更快或更慢可能有其重要性,但如果在Fortran开发内容需要5倍的时间,因为:

  • 它缺少任何与纯数字运算不同的任务的好库
  • 它没有任何适当的文档和单元测试工具
  • 这是一种表达能力非常低的语言,代码行数突然增加。
  • 对字符串的处理非常糟糕
  • 它在不同的编译器和架构之间存在大量问题,让你发疯。
  • 它有一个非常糟糕的IO策略(顺序文件的READ / WRITE。是的,存在随机访问文件,但你有没有看到它们被使用过?)
  • 它不鼓励良好的开发实践,模块化。
  • 有效缺乏完全标准的,完全兼容的开源编译器(gfortran和g95都不支持所有内容)
  • 与C的互操作性非常差(错误:一个下划线,两个下划线,没有下划线,通常一个下划线,但如果有另一个下划线,则为两个。并且不要钻研COMMON块...)

那么这个问题就无关紧要了。如果某些事情变得缓慢,大多数时候你无法超越给定的限制。如果您想要更快的速度,请更改算法。最后,电脑时间便宜。人类的时间不是。重视减少人类时间的选择。如果它增加了计算机时间,那么无论如何它都具有成本效益。

答案 22 :(得分:-3)

Fortran传统上不设置-fp:strict等选项(ifort需要启用USE IEEE_arithmetic中的某些功能,这是f2003标准的一部分)。英特尔C ++也没有将-fp:strict设置为默认值,但这是ERRNO处理所必需的,例如,其他C ++编译器不能方便关闭ERRNO或获得优化,如减少simd。 gcc和g ++要求我设置Makefile以避免使用危险的组合-O3 -ffast-math -fopenmp -march = native。 除了这些问题之外,关于相对性能的这个问题变得更加挑剔,并且依赖于有关编译器和选项选择的本地规则。