我不时地读到,对于繁重的计算,Fortran比C更快,甚至可以更快。是真的吗 我必须承认,我几乎不了解Fortran,但是到目前为止,我所看到的Fortran代码并未表明该语言具有C所没有的功能。
如果是真的,请告诉我原因。请不要告诉我哪种语言或库最适合数字运算,我不打算编写应用程序或库来做到这一点,我很好奇。
我不时地读到,对于繁重的计算,Fortran比C更快,甚至可以更快。是真的吗 我必须承认,我几乎不了解Fortran,但是到目前为止,我所看到的Fortran代码并未表明该语言具有C所没有的功能。
如果是真的,请告诉我原因。请不要告诉我哪种语言或库最适合数字运算,我不打算编写应用程序或库来做到这一点,我很好奇。
Answers:
这些语言具有相似的功能集。性能差异源于以下事实: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
关键字和strict-aliasing已引入C99标准以解决此问题。如今,大多数C ++编译器也都很好地支持它。关键字允许您向编译器提供提示,表明程序员保证指针不会与任何其他指针混叠。严格混叠意味着程序员保证不同类型的指针永远不会重叠,例如a double*
将不会与an重叠int*
(特定的例外是,char*
并且void*
可以与任何东西重叠)。
如果使用它们,则C和Fortran的速度相同。但是,restrict
仅将关键字与对性能有严格要求的功能一起使用的能力意味着C(和C ++)程序更安全,更易于编写。例如,考虑无效的Fortran代码:CALL TRANSFORM(A(1, 30), A(2, 31), A(3, 32), 30)
,大多数Fortran编译器将在没有任何警告的情况下愉快地进行编译,但是会引入仅在某些编译器,某些硬件和某些优化选项上显示的错误。
call transform
示例没有多大意义。
当我开始专业编程时,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)具有协同数组,使您可以轻松编写并行代码。CRAY的G95(开源)和编译器已经支持它。
因此,是的,Fortran可以快速实现,仅仅是因为编译器比C / C ++更好地优化/并行化它。但是,就像生活中的其他一切一样,有好的编译器和坏的编译器。
forall
不建议使用该构造,因为编译器无法很好地优化代码。替换为do concurrent
。而且,代码sqrt(sum(x**2))
看起来效率很低,因为编译器可能会构造整个vector x**2
。我猜想循环会更好,但是无疑最好调用内在norm2
函数。
有趣的是,这里的许多答案来自于不懂语言。对于已打开并使用旧的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,im; }; C中使用的惯用语。尽管操作员在fortran中处理复杂类型,但它可以加快开发速度。
{ double re, im; };
C语言中使用的struct 习惯用法具有相同的好处”。C编译器最有可能以sret形式返回该结构,并通过调用方堆栈分配空间,将指针传递给被填充的被调用方。这比在Fortran编译器中在寄存器中返回多个值要慢几倍。请注意,在复杂的特殊情况下,C99修复了此问题。
我认为支持Fortran的关键点是它是一种稍微适合于表达基于矢量和基于数组的数学的语言。上面指出的指针分析问题在实践中是真实存在的,因为可移植代码无法真正假设您可以告诉编译器某些信息。以更接近域外观的方式表达计算总是有一个优势。如果仔细观察,C根本没有数组,只是某种类似的行为。Fortran具有真正的优势。这使得更容易针对某些类型的算法进行编译,尤其是对于并行机。
在运行时系统和调用约定之类的内容中,C和现代的Fortran非常相似,因此很难看出会有什么不同。请注意,这里的C实际上是基础C:C ++是一个完全不同的问题,具有非常不同的性能特征。
没有一种语言比另一种语言快,所以正确的答案是“ 否”。
您真正要问的是“使用Fortran编译器X编译的代码是否比使用C编译器Y编译的等效代码快?” 这个问题的答案当然取决于您选择哪两个编译器。
一个人可能会问的另一个问题是:“考虑到在编译器中进行相同的优化工作,哪个编译器会产生更快的代码?” 答案实际上是Fortran。Fortran编译器具有一定的优势:
但是,没有什么可以阻止某人花费大量精力进行C编译器的优化,并使其生成比其平台的Fortran编译器更好的代码。实际上,C编译器产生的较大销售额使此方案相当可行
还有另一个项目,其中Fortran与C不同-并且可能更快。Fortran比C具有更好的优化规则。在Fortran中,未定义表达式的求值顺序,这使编译器可以对其进行优化-如果要强制执行某个顺序,则必须使用括号。在C语言中,顺序要严格得多,但是使用“ -fast”选项时,它们会更宽松,并且“(...)”也将被忽略。我认为Fortran的中间位置很好。(好吧,由于某些评估顺序的更改要求没有溢出发生,因此IEEE会使工作变得更加困难,这必须被忽略或妨碍评估)。
更智能的规则的另一个领域是复数。直到C 99才有了C,并且在Fortran中控制它们的规则也更好。由于gfortran的Fortran库部分用C编写,但是实现了Fortran语义,因此GCC获得了该选项(也可以与“常规” C程序一起使用):
-fcx-fortran-rules复杂的乘法和除法遵循Fortran规则。范围缩小是复数除法的一部分,但是没有检查复数乘法或除法的结果是否为“ NaN + I * NaN”,从而试图挽救这种情况。
上面提到的别名规则是另一个好处,而且-至少在原则上-整个数组操作,如果编译器的优化程序将其适当考虑在内,则可以导致更快的代码。相反,某些操作会花费更多时间,例如,如果将一个操作分配给可分配的数组,则需要进行大量检查(重新分配?[Fortran 2003功能],是否具有数组步长,等等),这使得简单的操作在幕后变得更加复杂-从而速度较慢,但使语言更强大。另一方面,具有灵活边界和步幅的数组操作使编写代码更加容易-而且编译器通常比用户更能优化代码。
总的来说,我认为C和Fortran的速度差不多。选择应该更多是使用哪种语言,或者使用Fortran的全数组操作及其更好的可移植性是否更有用-还是更好地与C语言中的系统和图形用户界面库接口。
对于特定的目的,Fortran和C 语言并没有什么比另一种更快。关于每种语言的特定编译器,有些事情比某些任务对某些任务的帮助更大。
多年以来,Fortran编译器就可以对您的数字例程产生魔力,从而使许多重要的计算速度异常之快。当代的C编译器也做不到。结果,Fortran中增加了许多出色的代码库。如果要使用这些经过良好测试的,成熟的,出色的库,可以使用Fortran编译器。
我的非正式观察表明,这些天人们用任何旧的语言编写繁重的计算内容,如果花了一些时间,他们会花一些时间在廉价的计算集群上。摩尔定律使我们所有人都变得愚蠢。
我将Fortran,C和C ++的速度与netlib的经典Levine-Callahan-Dongarra基准进行了比较。带有OpenMP的多语言版本为 http://sites.google.com/site/tprincesite/levine-callahan-dongarra-vectors C语言较为丑陋,因为它以自动翻译开始,并且为某些语言插入了限制和注释编译器。在适用的情况下,C ++只是带有STL模板的C语言。在我看来,STL能否提高可维护性是一个参差不齐的问题。
自动内联函数只有很少的练习,以了解它在多大程度上改善了优化效果,因为这些示例基于传统的Fortran惯例,因此很少依赖内联函数。
迄今为止使用最广泛的C / C ++编译器缺少自动矢量化功能,这些基准测试在很大程度上依赖于此。
回顾一下之前的文章:有两个示例,其中在Fortran中使用括号指示更快或更准确的求值顺序。已知的C编译器在不禁用更重要的优化的情况下没有观察括号的选项。
我是一个业余程序员,在两种语言中我都“平均”。我发现编写快速的Fortran代码比C(或C ++)代码容易。Fortran和C都是“历史性”语言(按今天的标准),已被大量使用,并且得到了自由和商业编译器的良好支持。
我不知道这是否是一个历史事实,但是Fortran感觉它是并行/分布式/矢量化/多核化的。今天,当我们谈论速度时,它几乎已成为“标准指标”:“它可扩展吗?”
对于纯cpu运算,我喜欢Fortran。对于任何与IO相关的问题,我发现使用C会更容易(无论如何,这都是困难的)。
当然,现在,对于并行数学密集型代码,您可能希望使用GPU。C和Fortran都具有或多或少的集成良好的CUDA / OpenCL接口(以及现在的OpenACC)。
我的客观回答是:如果您对两种语言的了解都很好/很差,那么我认为Fortran会更快,因为我发现用Fortran编写并行/分布式代码比使用C更为容易。不只是严格的F77代码)
对于那些因为我不喜欢第一个答案而愿意拒绝我的人,这是第二个答案:两种语言都有编写高性能代码所需的功能。因此,它取决于您要实现的算法(CPU密集型还是io密集型还是内存密集型),硬件(单cpu,多核,分布式超级计算机,GPGPU,FPGA),您的技能以及最终的编译器本身。C和Fortran都有出色的编译器。(我对Fortran编译器的先进程度感到非常惊讶,但C编译器也是如此)。
PS:很高兴您特别排除了库,因为关于Fortran GUI库,我有很多不好的话要说。:)
Fortran和C之间的任何速度差异将更多地取决于编译器优化和特定编译器使用的基础数学库。Fortran没有内在的东西可以使它比C更快。
无论如何,优秀的程序员可以用任何语言编写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更简单。 最终真正更快的速度取决于算法,但是无论如何速度差异不大。这就是我2015年在德国斯图加德高性能计算中心的Fortran研讨会上所学到的。我与Fortran和C一起工作,并分享了这一观点。
说明:
C被设计为编写操作系统。因此,它比编写高性能代码所需的自由更大。通常,这没有问题,但是如果不仔细编程,则很容易降低代码速度。
Fortran专为科学编程而设计。因此,它支持按语法编写快速代码,因为这是Fortran的主要目的。与公众意见相反,Fortran并不是一种过时的编程语言。它的最新标准是2010年,并且定期发布新的编译器,因为大多数高性能代码都是用Fortran编写的。Fortran进一步支持现代功能作为编译器指令(在C编译指示中)。
示例: 我们想给函数提供一个大的结构作为输入参数(fortran:子例程)。在函数中,参数不会更改。
C支持按引用调用和按值调用,这是一个方便的功能。在我们的情况下,程序员可能会偶然使用按值调用。这会大大减慢速度,因为需要先将结构复制到内存中。
Fortran仅与按引用调用一起使用,如果程序员确实想要按值调用,它将迫使程序员手动复制该结构。在我们的情况下,通过引用调用,fortran将自动与C版本一样快。
通常,FORTRAN比C慢。C可以使用硬件级别的指针,从而允许程序员进行手动优化。FORTRAN(在大多数情况下)无权访问硬件内存来解决黑客问题。(VAX FORTRAN是另一个故事。)自70年代以来,我一直在使用FORTRAN。(真。)
但是,从90年代开始,FORTRAN已发展为包括特定的语言结构,这些结构可以优化为固有的并行算法,这些算法实际上可以在多核处理器上大放异彩。例如,自动向量化允许多个处理器同时处理数据向量中的每个元素。16个处理器-16个元素向量-处理需要1/16的时间。
在C语言中,您必须管理自己的线程并仔细设计算法以进行多处理,然后使用一堆API调用来确保并行发生正确。
在FORTRAN中,您只需要仔细设计算法即可进行多重处理。编译器和运行时可以为您处理其余的工作。
您可以阅读有关高性能Fortran的一些文章,但是会发现很多死链接。您最好阅读有关并行编程(例如OpenMP.org)以及FORTRAN如何支持它的知识。
较快的代码并不能完全由语言决定,而是编译器,因此您可以看到ms-vb“ compiler”生成ated肿,较慢且冗余的目标代码,这些目标代码在“ .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更快,因为编译器不需要担心别名(因此可以在优化过程中做出更多假设)。但是,自C99(我认为)标准以来,这已在C中处理,其中包含了strict关键字。基本上告诉编译器,在给定范围内,指针没有别名。此外,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)。
这在某种程度上是主观的,因为它影响到编译器的质量,而比其他任何事情都重要。但是,从语言/编译器的角度来讲,要更直接地回答您的问题,关于C语言的Fortran并没有使它本质上比C更快或更好的方法。如果您要执行大量的数学运算,它将归结为编译器的质量,每种语言的程序员的技能以及内在的数学支持库,这些库支持这些操作,以最终确定对于给定的实现哪种方法更快。
编辑:@Nils之类的其他人提出了关于C中使用指针的区别以及别名的可能性的好处,这可能会使C中最幼稚的实现变慢。但是,在C99中有很多方法可以解决,通过编译器优化标志和/或C的实际编写方式。@Nils答案以及其答案中的后续注释对此进行了详细介绍。
大多数帖子已经提出了令人信服的论点,因此我将在另一方面加上众所周知的2美分。
最终,在处理能力方面更快或更慢的fortran可能具有其重要性,但是如果在Fortran中开发某些东西要花费5倍的时间,原因是:
那么这个问题就无关紧要了。如果进展缓慢,大多数时候您将无法超出给定的限制进行改进。如果您想要更快的速度,请更改算法。最后,计算机时间很便宜。人的时间不是。重视减少人员时间的选择。如果增加了计算机时间,无论如何它都具有成本效益。
I will just add the proverbial 2 cents to a different aspect
传统上,Fortran不会设置-fp:strict之类的选项(ifort要求启用USE IEEE_arithmetic(f2003标准的一部分)中的某些功能)。英特尔C ++也未将-fp:strict设置为默认值,但是例如,这对于ERRNO处理是必需的,并且其他C ++编译器无法方便地关闭ERRNO或获取诸如simd减少之类的优化。gcc和g ++要求我设置Makefile以避免使用危险的组合-O3 -ffast-math -fopenmp -march = native。除了这些问题之外,有关相对性能的问题更加挑剔,并且取决于有关编译器和选项选择的本地规则。