
体验。呵呵。体验。
我因此砸烂过一支键盘。具体情形,请参照“德国 boy”相关视频,自行脑补。
哪怕知道可能要因此遭受一些人参公鸡,我也还是来学王垠,开一发地图炮吧。
特别声明,以下内容基本都是调侃和玩笑,请看官不要太认真;特别地,这些问题,很大程度上是学界的代码开发模式的问题,Fortran 不过是这些问题的一个替罪羊罢了。
开喷。
Fortran 是一个应该得到恰当的荣誉并下葬安息的亡灵。只是,今日之学界中,这个亡灵喜欢跳出来诈诈尸,口中念叨着些咒语,大致是些“祖宗之法不可破”、“前人智慧的结晶”、“语法严谨、简单易学、运行如风”之类,听着让人心里发毛。就不多说这僵尸还诞下了一个名叫 IDL 的怪胎的事儿了,省得过分地得罪同行。
这里并不全是说 Fortran 本身的不好:(被这帮人视若无物的)C/C++ 中的未定义行为、泛滥的宏定义,也会坑得人生活不能自理。
更多的,是 Fortran 所代表的编程范式的问题,因为一门语言包含了它的编程范式。 Fortran 有编程范式么?若说有,那大概可以用这几个词来形容:粗暴、混乱、不计后果。这时,Fortran 的“语法严谨”便显得有些可笑:这反倒使得程序员不再小心谨慎,结果把自己陷入更多的坑中。
一旦一份代码用上了 Fortran,则往往意味着,这份代码背上了沉重的历史负担——你可以说它融汇了前人的智慧,但你更应该看到前人的局限,以及,由涓滴之流般的偷懒汇成的江河般泛滥的愚蠢和短视。所以,以下几个问题中,至少出现一个的几率,将变得非常大:
1. 更换平台编译时、编译旧代码时,动辄出错,且难以排查——很多时候是因为 gfortran 的 F77 兼容模式不能完全兼容某些写法,剩下则多是 ifort 和 gfortran (以及它们各自的用户)间的恩♂恩♂怨♂怨,少数时候是代码中调用的某些库的 API 做了个不痛不痒的改名(没错 fftw 我说的就是你);
2. 代码中有 goto;
3. 你会看到一个上千行甚至几千行的 subroutine/function;
4. 你会看到一个有一百多个参数的 subroutine/function;
5. 各种不知所云的变量名,以及完全无用的注释;
6. “if a > 0”?恭喜,编译错误,因为,“你那个 0 是个短整形,而 a 是个长整/浮点”;
7, 你得经常跑到一个函数的开头去看一个变量的定义;
8. write 的某个数字超宽了?恭喜你,你得到了一堆星号,谁关心你辛辛苦苦跑出来的数字去哪儿了啊——我当然知道这么做有它固有的原因,不过,这真是“时代的局限”五个字的最好样本之一;
9. 各种没有料到的惊喜,酸爽无比,把 least surprise 原则破坏殆尽。
最重要的,一份用上 Fortran 的代码,很可能有着根本没写过代码、速成培训后大干快上大炼钢铁的半吊子程序猿/程序媛——哪怕,他们可能是非常优秀的科研人员,他们的智商可能是我的两倍左右甚至三倍多,但是,不会写程序就是不会写程序。
请看官不要说数组下标越界检查之类(在足够复杂的代码中几乎从未正确发挥过作用的)算不上优势的优势了,也不要抱着“Fortran 就是比 C/C++ 快、容易优化”、“Fortran 的数值计算库丰富、容易使用、高度优化”(我真的不信有啥东西的优化的高度能比过 -O3 下的 STL)的迷思不放了。知乎上已有够多的人详细论证这个问题了,英文网站上相关的论述更是一挖一麻袋。
以及,最重要的,我一直认为,作为一份科研用途的代码,可维护性的提升比运行速度的提升,重要得太多太多太多太多了。平庸的运行速度,至多消耗了更多的机时——这在至今仍然日新月异地发展着的计算机那儿常常不是个事儿;低劣的可维护性,吞噬的则是科研中两样最重要的东西:一曰可重现性,二曰(有经验的)科研人员的生命。我承认,用 C/C++/Julia 仍然有一定概率写出难以维护的代码,但至少,客观上看,Fortran 带来的沉重历史负担,基本摧毁了”存在可维护性“的可能性;同时,Fortran 的低门槛,客观上使得太多人养成了大炼钢铁式的编程习惯,让很多原本可圈可点的代码落到最后臭不可闻。
我心目中的”优秀代码“,也算是有个参照标准的——比如,Linux 内核。它固然有过并有着许许多多的 bug,可是它是一个庞大却可以维护的系统。如果 Linux Kernel 是荷马史诗,那么,那些(以 Fortran 为首的)科研所用代码,至多只能算是《尤利西斯》了。
然后,是的,我知道孤证不立,我知道有人喜欢喷“这是逻辑错误”,也有人会讲另一面的故事,但我还是要讲故事。
我刚到某屯搬砖的时候,与一个师兄合作,用上一份叫做 Pinocchio 的代码(
)。这份代码是用来模拟星系团尺度上的暗物质演化的,使用微扰论替代直接求解 N-S 方程,使其可以在小计算机上跑出效果还不错的大尺度结构模拟。首先,花了一个周末,这货才能被成功编译,原因是 gfortran 不接受某些 ifort 的写法——这还不是整形长度之类的细节,而就是关于某些日常用法的语法不一致——以及 fftw 更改 API 的坑。编译成功,随即跑了个 的,looking good。接着,就遇上大坑了。我们需要 的分辨率,可一把边长设置为 1500,程序马上就崩溃了。整形溢出。那好说嘛,把数组指标改为长整型不就得了——当然不,这样更改后的代码,同样跑 ,得出的结果与数组下标为普通整型时不!一!致!(固定随机数种子之后,这个代码每次得到的结果是完全一样的)调了整整一周,毫无进展。决定暂时不管那么多,先跑 ,跑完后,看到输出数据里头一堆 *****(输出超宽),心都碎了……
以及,我要喷我最想喷的货色:Enzo(
)。正是这个奇葩,让我失去了对天体物理 simulation 的几乎所有兴趣和信心。虽然这个 code 的 C++ 部分也很烂,但 Fortran 部分更是坑得人生活不能自理,最后便合成了一篇集 C++ 和 Fortran 烂法之大成的东西。
就在前不久,我这个 rotation 项目都快结束了,还被它给坑过。我那位 rotation 老板在二十年前为此写过一个模块,用 F77 写的。时至今日,他一直以为该模块在此程序中发挥着巨大的作用,直到我发现,调用这个模块的部分,被人注释掉了。他顿时花容失色,令我重启该调用,却发现巨量无法修正的编译错误——是的,gfortran 的“兼容模式”下支持的 F77 已经不是他当年那个 F77 了。老板长叹一声道,幸好我后来用了 C++……
然后,再来看点儿来自 enzo 的酸掉你大牙的东西,F77 和 F90 都有。比如,这个宏伟的参数列表:
以及前一个函数在 C++ 中的调用,是不是感受到了一种灵压:
这个赋值方式真是不错:
此朵数组的眼神,忧郁得让人蛋疼:
Fortran,Let's GOTO places(广告语原句:Toyota, let's go places):
当然还有很多 crap,比如:
现在的计算机,缓存都已经够大了;函数调用带来的开销,早都是零头了;还在以这种手段拼命避免函数调用,简直就是防止他人维护,良心大大的坏了。若是用 cpp 写个 class,也能省得一不小心写错一个参数的位置啊——面向对象再是大坑,至少能让人们有一个靠谱的 namespace 可用(F90/95 的命名空间?呵呵)。
就那种可维护性,算得再快,又能怎样?
最后,私货。
高德纳(曾经记成 RMS 了,感谢纠正):“过早的优化是万恶之源”。我得加一句,“自以为是的优化是万恶之源”,包括在今日仍然坚持使用 Fortran 开发新项目。
人在做,天在看,implicit 留隐患;
GOTO 一出亲友散,非结构化天下乱。
诚心诚念 CPlus 好,抓紧重构灭退保。
众生皆为数值来,Fortran 险恶杀时间。
CPlus 弟子说真相,教你脱险莫拒绝。
天灭 Fortran,快看 Julia!Numpy/Scipy/Cython 坠吼!Haskell 赛高!
全世界被 Fortran 坑过的人,联合起来!Fortran-sufferers of the world, unite!
