对于繁重的计算,Fortran是否比C更容易优化?
我不时地读到,对于繁重的计算,Fortran比C快,或者比C快。真的是这样吗?我必须承认,我几乎不了解Fortran,但是到目前为止,我所看到的Fortran代码并未表明该语言具有C所没有的功能。
如果是这样,请告诉我原因。请不要告诉我哪些语言或者库最适合进行数字运算,我无意编写应用程序或者库来做到这一点,我只是很好奇。
解决方案
Fortran和C之间的任何速度差异将更多地取决于编译器优化和特定编译器使用的基础数学库。 Fortran没有内在的东西可以使它比C更快。
无论如何,一个好的程序员可以用任何语言编写Fortran。
这在某种程度上是主观的,因为它影响到编译器的质量,而比其他任何事情都重要。但是,从语言/编译器的角度来讲,要更直接地回答问题,关于C的Fortran并没有使它本质上比C更快或者更好的方法。编译器的质量,每种语言的程序员的技能以及内在的数学支持库,这些库支持这些操作,以最终确定对于给定的实现哪种方法更快。
编辑:@Nils之类的其他人提出了关于C中使用指针的区别以及别名的可能性的好点,这可能会使C中最幼稚的实现变慢。但是,在C99中有很多方法可以解决,通过编译器优化标志和/或者C的实际编写方式。 @Nils答案以及他的答案中的后续注释对此进行了很好的介绍。
这些语言具有相似的功能集。性能差异来自以下事实: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编译器假定指针/数组在内存中不重叠。
幸运的是,C99标准引入了strict关键字和strict-aliasing来解决此问题。如今,大多数C ++编译器也都很好地支持它。关键字使我们可以向编译器提供提示,表明程序员保证一个指针不会与任何其他指针混叠。严格混叠意味着程序员保证不同类型的指针永远不会重叠,例如," double *"不会与" int *"重叠(具体例外是" char *"和" void *"可以与任何东西重叠)。
如果使用它们,则C和Fortran的速度相同。但是,仅对性能关键的函数使用限制关键字的能力意味着C(和C ++)程序更安全,更易于编写。例如,考虑无效的Fortran代码:CALL TRANSFORM(A(1,30),A(2,31),A(3,32),30)
,大多数Fortran编译器将在没有任何警告的情况下愉快地进行编译,但是引入了仅在某些编译器,某些硬件和某些优化选项上显示的错误。
我还没有听说过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%]
通常,FORTRAN比C慢。C可以使用硬件级别的指针,使程序员可以进行手动优化。 FORTRAN(在大多数情况下)无权访问用于解决黑客问题的硬件内存。 (VAX FORTRAN是另一个故事。)自70年代以来,我一直在使用FORTRAN。 (真的。)
但是,从90年代开始,FORTRAN已发展为包括特定的语言结构,这些结构可以优化为固有的并行算法,从而真正在多核处理器上大放异彩。例如,自动向量化允许多个处理器同时处理数据向量中的每个元素。 16个处理器-16元素向量处理需要1/16的时间。
在C语言中,我们必须管理自己的线程并仔细设计算法以进行多处理,然后使用一堆API调用来确保并行发生正确。
在FORTRAN中,我们只需要仔细设计算法即可进行多重处理。编译器和运行时可以为我们处理其余的工作。
我们可以阅读有关高性能Fortran的一些文章,但是会发现很多死链接。我们最好阅读有关并行编程(例如OpenMP.org)以及FORTRAN如何支持它的知识。
对于特定的目的,Fortran和C语言并没有什么比另一种语言更快。关于每种语言的特定编译器,都有一些事情比某些任务对某些任务的帮助更大。
多年以来,Fortran编译器就可以对数字例程产生魔力,从而使许多重要的计算速度异常之快。当代的C编译器也无法做到这一点。结果,Fortran中增加了许多出色的代码库。如果要使用这些经过良好测试的,成熟的,出色的库,可以使用Fortran编译器。
我的非正式观察表明,这些天来,人们用任何旧的语言编写繁重的计算内容,如果花了一些时间,他们会花一些时间在廉价的计算集群上。摩尔定律使我们所有人都变得愚蠢。
Fortran可能更快的原因有很多。但是,它们所影响的数量无关紧要,或者无论如何都可以解决,这无关紧要。如今使用Fortran的主要原因是维护或者扩展旧版应用程序。
- 函数上的PURE和ELEMENTAL关键字。这些功能没有副作用。这在某些情况下允许优化,在这种情况下,编译器知道将使用相同的值调用相同的函数。注意:GCC将" pure"实现为该语言的扩展。其他编译器也可以。模块间分析也可以执行此优化,但这很困难。
- 处理数组而不是单个元素的标准函数集。像sin(),log(),sqrt()之类的东西采用数组而不是标量。这样可以更轻松地优化例程。如果这些函数是内联函数或者内置函数,则在大多数情况下,自动矢量化将带来相同的好处
- 内置复杂类型。从理论上讲,这可以允许编译器在某些情况下重新排序或者消除某些指令,但是使用struct {double re,im; }; C中使用的惯用语。尽管操作员在fortran中处理复杂类型,但它可以加快开发速度。
我认为支持Fortran的关键点是,它是一种稍微适合于表达基于向量和基于数组的数学的语言。上面指出的指针分析问题在实践中是真实存在的,因为可移植代码无法真正假设我们可以告诉编译器某些信息。以更接近域外观的方式来表达计算总是有一个优势。如果仔细观察,C根本就没有数组,只是某种类似的行为。 Fortran具有真正的优势。这使得更容易针对某些类型的算法进行编译,尤其是对于并行机。
在运行时系统和调用约定之类的内容中,C和现代的Fortran足够相似,以至于很难看出会有什么不同。请注意,这里的C实际上是基础C:C ++是一个完全不同的问题,具有非常不同的性能特征。
是的,在1980年;在2008?要看
当我开始专业编程时,Fortran的速度优势才刚刚受到挑战。我记得在Dobbs博士那里读过这篇文章,并向年长的程序员讲述了这篇文章-他们笑了。
因此,我对此有两种观点,即理论上的和实践上的。从理论上讲,今天的Fortran对C / C ++甚至任何允许汇编代码的语言都没有内在的优势。实际上,在今天,Fortran仍然享受着围绕数字代码优化而建立的历史和文化遗留的好处。
直到包括Fortran 77在内,语言设计方面的考虑都以优化为主要重点。由于编译器理论和技术的现状,这通常意味着限制功能和特性,以便使编译器在优化代码方面获得最佳效果。一个很好的类比是将Fortran 77视为牺牲速度的专业赛车。这些天来,编译器在所有语言上都变得更好,并且使程序员工作效率的功能受到更多重视。但是,仍然有一些地方人们主要关心科学计算的速度。这些人很可能已经从自己是Fortran程序员的人那里继承了代码,培训和文化。
当人们开始谈论代码优化时,会遇到很多问题,对此有一种最好的感受是潜伏在需要快速数字代码的人们所在的地方。但是请记住,这种非常敏感的代码通常只占整个代码行的一小部分,并且非常专业:许多Fortran代码与其他语言的许多其他代码一样"效率低下",并且优化甚至不应这种代码的主要关注点。
Wikipedia是开始学习Fortran历史和文化的绝佳去处。 Fortran Wikipedia条目非常棒,我非常感谢那些花时间和精力使它对Fortran社区有价值的人。
(此答案的简化版应该是Nils发起的优秀线程中的注释,但我没有因果关系。实际上,我可能什么都没写,但因为该线程具有实际意义信息内容和共享,而不是火焰战争和语言偏执,这是我在这方面的主要经验。我不知所措,不得不分享爱。)
还有另一个项目,其中Fortran与C不同,并且可能更快。 Fortran具有比C更好的优化规则。在Fortran中,未定义表达式的求值顺序,这使编译器可以在需要强制执行特定顺序(必须使用括号)的情况下对其进行优化。在C语言中,顺序要严格得多,但是使用" -fast"选项时,它们会更宽松,并且"(...)"也将被忽略。我认为Fortran的中间位置很好。 (嗯,由于某些评估顺序的更改要求没有溢出发生,因此IEEE会使工作变得更加困难,这必须被忽略或者妨碍评估)。
更智能的规则的另一个领域是复数。直到C 99才有了C,并且在Fortran中控制它们的规则也更好。由于gfortran的Fortran库部分用C编写,但是实现了Fortran语义,因此GCC获得了该选项(也可以与"常规" C程序一起使用):
-fcx-fortran-rules Complex multiplication and division follow Fortran rules. Range reduction is done as part of complex division, but there is no checking whether the result of a complex multiplication or division is "NaN + I*NaN", with an attempt to rescue the situation in that case.
上面提到的别名规则是另一个好处,并且至少在原则上也是对整个数组进行操作,如果编译器的优化程序将其适当考虑在内,则可以导致更快的代码。相反,某些操作会花费更多时间,例如如果对一个可分配的数组进行赋值,则需要进行大量检查(重新分配?[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语言比较丑陋,因为它是从自动翻译开始的,并且为某些编译器加上了limit和pragma。
在适用的情况下,C ++只是带有STL模板的C语言。在我看来,STL能否提高可维护性是一个参差不齐的问题。
自动内联函数只有极少的练习,以了解它在多大程度上改善了优化效果,因为这些示例基于传统的Fortran实践,而对内联函数的依赖很少。
迄今为止使用最广泛的C / C ++编译器缺少自动矢量化功能,这些基准测试在很大程度上依赖于此。
回顾一下之前的文章:有两个示例,其中在Fortran中使用括号来指示更快或者更准确的求值顺序。已知的C编译器在不禁用更重要的优化的情况下没有观察括号的选项。
没有一种语言比另一种语言快,所以正确的答案是"否"。
我们真正要问的是"用Fortran编译器X编译的代码是否比用C编译器Y编译的等效代码快?"这个问题的答案当然取决于我们选择哪两个编译器。
一个人可能会问的另一个问题是:"考虑到在编译器中进行相同的优化工作,哪个编译器会产生更快的代码?"
答案实际上是Fortran。 Fortran编译器具有证书优势:
- 在某些人发誓永远不使用编译器的那一天,Fortran不得不与Assembly竞争,因此它是为提高速度而设计的。 C被设计为灵活的。
- Fortran的利基市场一直是数字紧缩。在这个领域中,代码永远不会足够快。因此,保持语言高效始终存在着很大的压力。
- 编译器优化的大多数研究都是由对加速Fortran数字处理代码感兴趣的人员完成的,因此,与优化其他任何编译语言相比,优化Fortran代码是一个众所周知的问题,而新的创新首先出现在Fortran编译器中。
- Biggie:与Fortran相比,C鼓励使用更多的指针。这极大地增加了C程序中任何数据项的潜在范围,这使它们难以优化。请注意,Ada在这个领域也比C更好,并且比常见的Fortran77更现代的OO语言。如果我们想要一种可以生成比C语言更快的代码的OO语言,那么这是一个选择。
- 再次由于其数量紧缺的利基,Fortran编译器的客户往往比C编译器的客户更关心优化。
但是,没有什么可以阻止某人花费大量精力进行C编译器的优化,并使其生成比其平台的Fortran编译器更好的代码。实际上,C编译器产生的较大销售额使此方案相当可行
在某种程度上,Fortran的设计考虑了编译器的优化。该语言支持整个数组操作,编译器可以在其中利用并行性(特别是在多核处理器上)。例如,
密集矩阵乘法很简单:
matmul(a,b)
向量x的L2范数是:
sqrt(sum(x**2))
此外,诸如" FORALL"," PURE"和" ELEMENTAL"过程之类的语句还有助于优化代码。由于这个简单的原因,即使是Fortran中的指针也没有C一样灵活。
即将到来的Fortran标准(2008)具有协同数组,使我们可以轻松编写并行代码。 G95(开源)和CRAY的编译器已支持它。
因此,是的,Fortran可以快速实现,仅仅是因为编译器可以比C / C ++更好地优化/并行化它。但是,就像生活中的其他一切一样,有好的编译器和坏的编译器。
大多数职位已经提出了令人信服的论点,因此我只将谚语2美分添加到另一个方面。
最终,在处理能力方面更快或者更慢地进行fortran可能很重要,但是如果在Fortran中开发某些东西要花费5倍以上的时间,原因是:
- 它缺少用于处理不同于纯数字运算的任何好的库
- 它缺少用于文档和单元测试的任何不错的工具
- 这是一种表达能力很低的语言,使代码行数猛增。
- 它对字符串的处理非常差
- 在不同的编译器和体系结构中,它存在着无数疯狂的问题,这使我们发疯。
- 它的IO策略非常差(对顺序文件进行读/写操作。是的,存在随机访问文件,但我们见过使用它们吗?)
- 它不鼓励良好的开发实践,模块化。
- 有效缺乏完全标准,完全兼容的开源编译器(gfortran和g95都不支持所有功能)
- 与C的互操作性非常差(调整:一个下划线,两个下划线,没有下划线,通常一个下划线,但如果有另一个下划线则两个下划线。并且不要钻研COMMON块...)
那么这个问题就无关紧要了。如果进展缓慢,大多数时候我们将无法超出给定的限制进行改进。如果我们想要更快的速度,请更改算法。最后,计算机时间很便宜。人的时间不是。重视减少人员时间的选择。如果增加了计算机时间,无论如何它都具有成本效益。
更快的代码并不能完全由语言决定,而是编译器,因此我们可以看到ms-vb" compiler"生成"肿,缓慢且冗余的目标代码,这些目标代码在" .exe"内捆绑在一起,但是powerBasic也会生成更好的代码。
由C和C ++编译器生成的目标代码是在某些阶段(至少2个阶段)生成的,但是通过设计,大多数Fortran编译器至少有5个阶段(包括高级优化),因此,按设计,Fortran将始终具有生成高度优化的代码的能力。
因此,最后是编译器而不是我们应该使用的语言,我所知道的最好的编译器是Intel Fortran编译器,因为我们可以在LINUX和Windows上获得它,并且可以将VS用作IDE,如果我们正在寻找一个便宜的编译器,我们可以随时在OpenWatcom上转播。
有关此的更多信息:
http://ed-thelen.org/1401Project/1401-IBM-Systems-Journal-FORTRAN.html
我在FORTRAN和C上做了几年广泛的数学研究。根据我自己的经验,我可以说出FORTRAN有时确实比C更好,但不是因为它的速度(通过使用适当的编码样式,它可以使C的性能与FORTRAN一样快),而是因为LAPACK这样的优化库非常好,并且由于高度并行化。我认为,FORTRAN的使用确实很笨拙,并且其优点还不足以消除该缺点,所以现在我使用C + GSL进行计算。