科学计算的程序编写和通常所说的码农的编程有多大的区别?

来源:ob欧宝官网

阅读 25
发布时间 2021-10-23 12:47:15

  科学计算程序的编写通常需要利用数学函数或运算库,除此之外它和通常意义上的码农的工作有何差异?平常仅限于编写科学计算程序,如何培养和加强编程能力,做一些…

  90%的时间用C/C++、Fortran。偶尔用mathematica, matlab等高级工具干些零碎的活;

  经常访问数学维基或百科网站(wikipedia、mathworld、planetmath等);

  科学计算领域的圣经是Abramowitz和 Stegun的数学手册,谭浩强是Numerical Recipe;

  不断有新入组的学弟学妹抗议代码不符合软件工程,后来他们要么闭上嘴去灌水,要么拿了master走人了;

  常常有重构代码的欲望,试过几次都放弃了,终于有天下午把代码重构了一遍,结果发现跑出来的数据怎么都不对,从此彻底放弃;

  总以为做科学计算的人写的代码都很丑,后来看过一个大牛写的代码,彻底惊呆了 —— 但学CS的室友照样说丑;

  写着写着,一不小心代码就成了某个领域某个小方向 the state of the art;

  程序员总是认为HPC的编程很弱智,真让他/她们去写HPC代码 ——开玩笑——他/她们连流体力学(量子化学、广义相对论 …… )都不懂,怎么可能会做科学计算;

  是程序员里面最懂流体力学的,是懂流体力学的人里面最能发文章的,是能发文章的人里面最会写程序的;

  2,代码review,做科学计算时有几人会找人审核代码?现在工作pull request后必须有两人review后才可以提交

  3,文档,用的比较多的open source的科学计算软件的文档还可以,但学生自己写的软件,呵呵

  4,测试,以前自己写波浪模拟软件时,修改后跑几个benchmark,感觉结果还一样就算可以了,现在要写unit test,要跑regression test,在公司里也是做数值模拟,script自动和已存的结果比较,有些数值要精确到小数点后15位,产生的任何数值模拟结果变化都要可以解释

  6,UX,user experience,比如按钮的布局,颜色的搭配,流程的合理与否,现在有专人负责研究

  7,License管理,之前不大关心什么MIT BSD GPL协议的具体细节,现在有些协议要找律师来帮忙解读协商

  8,团队合作,学校里大部分时间一个人写,现在是几个团队,要agile,要有冲刺,要有planning meeting,refinement meeting等等

  9, 软件开发的技术应用,博士时Fortran Matlab解决一切,程序里 for do while if else 解决大部分问题,真正的软件开发里就会用到什么DI container,模版,设计模块的时候也会想到设计模式

  最后推荐一篇文章 Best practice for scientific computing

  iology/article/asset?id=10.1371/journal.pbio.1001745.PDF

  一切以物理研究维准绳, 你能提出一个新公式, 甚好; 能在计算中发现新现象, 不错; 把程序用面向对象重写一遍--你这里有什么新物理么? 我招你进来又不是让你干这个的!

  如果你能提出一些新设想或者新观点, 老板会两眼放光; 如果想把程序的某一部分写的简单易懂一点, 老板表面上还是欢迎态度, 心想这货又在偷懒, 反正我又不会放到master branch里面。

  虽然正确性绝对是第一位,速度和优化还是非常要紧的, 某超算中心给我们分了每年五千万小时*核心的计算资源, 大概50人一起烧, 有个家伙第一个月就用掉两百万小时*核心, 然后老板开会跟我们说你们特么省点用, 想好了再算!而性能上的每一点优化, 省下来的都是真金白银, 你说我们有没有动力去做优化。

  然而优化数据结构, 展开循环什么的其实是相对较low的优化方法。 物理界的数学大牛一般都是直接优化算法, 辛流形实分析复分析满天飞, 有的时候可以带来几十倍的性能提升, 几年内发几十篇文章, 然后飘飘然去碾压其他方向。

  同学到天河1A去做整机运行, 就是把天河所有的计算能力全用上。 24小时烧掉10万+人民币, 后来发现参数调错了, 计算结果无效。自行脑补老板的表情。

  我们实验室的主要应用程序,从90年代一直开发至今, 原来是GPL授权, 后来变成private的了~~~~

  PS:这个是有典故的, 负责做软件的其他组同学过来参观, 看我们的版本都是用日期命名, 好奇问我们用什么VCS, 我说没有, 老板说我们有版本控制,

  经过多方努力, 我们的程序终于初具面向对象功能了!就是把原来的全局数组+子程序用30几个参数的函数包起来, 每一个参数都是几十万长度的Array(幸好Fortran是传指针)

  , 有的同学不会用循环, 直接写成 x(1) =1 x(2) = 3 x(3)=5, 省的编译器展开循环了。。。。

  程序里有一个核心部分从1995年以后就没人动过, 原理是一个循环, 中间会有各种条件跳出, 然而并没有用结构化循环, 全尼玛是goto实现的, 后来说要优化这一部分, 果断找浪潮的工程师, 还是为他们捏一把汗。

  上面说到的switch还算是良心之作, 一个师姐写的概念验证程序(大概也有5000+行), 里面的开关选项是这么写的:

  当中学老师的觉得做销售员只要脸皮厚跪舔客户就行了,不需要什么专业背景,随便拉个人来培训培训就能干了,摸爬滚打就成了人精;当销售的觉得中学老师教的东西自己上学时候都懂,自己来干搓搓有余,指不定当年高考还看不上师范类学校呢。

  无论是做科学计算还是做软件开发,编程都只是工具而不是目的。磨刀不误砍柴工,没有必要彼此贬低对方的工作价值和难度。大家不过都是在用工具来解决需求而已,要不然,造轮子的人还看不上你们这些开车的呢。

  因此做出来的程序不是从用户的角度考虑的,没有测试,容错性太差。我们实验室的程序,必须严格按照流程,哪一步输入什么数据,一不小心输错了,你就从头再输吧。运气不好,可能要操作十多遍,终于能算了,OK 程序很完美。

  码龙的程序,喵的,测试又报来一个bug。当用户在登录的时候,碰到断网,这时按下回车键,同时用鼠标拖动程序窗口,在这个过程中来网了,仍然显示登录失败,再次登录一切正常。这他喵的也算bug?

  自己的程序想怎么写就怎么写,svn是什么?文件夹下面 projectv1 projectv2 projectv3不是很好看。等等,你说要那个版本?我挨个试一下看看。

  注释是什么?文档是什么?程序的结构是什么?哇,师兄好厉害,一个函数几百行,一个.c文件写了几千行。咦,这段func1函数是干什么用的?

  3. 基础知识太差。抱着一本中文C++的书,看了前三章,发现就可以写计算程序了,原来编程这么简单的,好了,后面的不用看了。

  最奇葩的,要将数组A进行计算得到结果,再对这个结果再次进行同样的计算,一直这样重复下去。有的是将A计算后的结果保存到B,再用for循环交换整个A数组和B数组的值,然后进行下一步计算。这还算好的,有的将A计算后的结果,保存到文件,然后再从文件读取进行计算,再保存到文件,喵了个咪了,硬盘一直狂读写,都不想想问题在哪。

  -----------------------------------------------------------------------------------------------------------------

  最主要的就是国内实验室的氛围。一个良好的编程习惯,是需要整个实验室共同营造的。如果老板在评上教授之后,还以保持当年写程序的习惯和状态,并指导和要求学生。实验室共同开阔眼界,改进旧的代码,学习新的技术,经过几年的积累,想必是能够做出一些非常优秀的程序的。但是实际上呢?现在的老板不要求它写程序,只求他能看看文献,我觉得就算是一个不错的老板了。另外做科学计算的课题组,也觉得写程序是一件非常Low的事,做模型发文章才是大伙应该做的事。想想各个学校还用着vc6在做学程序设计,计算机系尚且如此,能指望我们这些非专业人士的编程水平有多高呢?

  做数值计算的上辈子都是折翼的天使。发文章被做实验的鄙视,毕业了没专业对口的企业要,想转行混个码龙,还发现处处不如别人。

  ---------------------------------------------------------------------------------------------------------------

  我很赞同@天象的看法。特别是研究计算机体系结构还有编译器的人,他们的调优的方法值得我们所谓的做科学计算的人了解一下。比如,如何使用性能分析工具,找出程序的hotspots。现代编译器会对我们的代码做哪些自动的优化,比如自动的循环展开,向量化计算等。有人说用汇编代码是最高效的,但是放到现在,主流观点都认为编译器生成的机器码往往比我们手工的汇编更高效。所以避免我们浪费时间做一些无用功,也能更好的提高效率。但是这些提高效率的研究真的是值得搞科学计算的人研究的吗?

  1. 针对特定的问题,对模型做了一些修改和优化。这才是真正的所谓搞科学计算的人应该做的事。

  2. 对于微分方程的展开形式进行研究。用什么样的形式进行离散之后,进行求解,可以获得较高的精度,进而提高计算效率。搞CFD的人喜欢研究这个吧,我这种FDM狗只能膜拜。

  3. 针对离散出来的结果进行求解的算法的研究。实际上就是研究矩阵的计算了,什么预处理哇,选哪种迭代法哇,自适应网格之类的。这个我感觉应该是搞数学或是计算机相关的人研究的。

  4. 代码级别的的优化。比如减少冗余计算量啊,人工循环展开啊,提高cache命中率啊,把什么关键代码汇编化哇,比如卡马克的平方根倒数算法,简真是走火入魔,令人发指。但这个,真的是熟悉计算机体系,编译器优化的人做才比较合适。

  不得不说这上面的任意一条确实都是需要大量专业的知识和经验的积累,绝不是上个什么语言培训班出来的码畜就能搞定的。

  但是我觉得科学计算的最终目的是用来解决实际的问题,至少我做的相关的工作应该是这样的。最终的产出不应该只是几篇论文。而是能投入实际使用。

  2. 你的程序能够保证你的程序处理任何相关的问题,都能正常而高效运行吗?不会改下网格,改下参数就发散了?出错了?

  3. 你的程序是否可以通过一些简单的问题,或者补充,然后就能解决领域内其它类似的问题。比如做成一个开源的lib提供给别人使用。

  上面这些问题对于论文的发表没有任何意义,所以被大伙嗤之以鼻。一边口上喊着产学研一体化,一边写出一堆除了自己没有人看得懂,没有人能使用的代码。这是广大课题组的现状吧。

  ------------------------------------------------------------------------------------------------

  被大神喷得好惨,感觉和大神们不在一个次元上。至少根据我的了解,国内许多做计算的停留在我描述的水平上,当然也有可能是我井底之蛙了。要知道好多人都是抱着一本数值分析,一本C++的书,就踏上了数值计算的道路,没有导师的指导,没有超算环境的支持,有的只是一堆乱七八糟据说正确的代码和几篇莫名其妙的论文。

  因为后者代码量只是前者的一个简单模块,而且通常计算量不大,难度主要集中在于理论上,而非代码量

  首先要说,我们好多模型都是码农写的,一个写好的模型只要简单包装一下就是一个软件。包装无非就是增加些UI界面,提供一些前处理及后处理接口或者自己写可视化程序。例如有限元有牛逼哄哄的Ansys、Abqus,流体模拟则是有限体积的Fluent、MikeZero等。

  要开发这种大型软件,我觉得没有点专业编程水平真做不出来,我从本科大二开始学Matlab编程,后面学Fortran、C,再后来又接触并行计算工具,如mpich2、openmpi、CUDA等,到现在已经有足足四年经验,可是要现在写程序还是不满意。我总结有以下几点还没做好:

  前三者是编程理论知识,后一个是编程经验。数据结构、算法不了解的话,写的程序效率不会太高,而且代码还可能会很臃肿;计算机体系结构知识可以帮助你更好的优化代码,而且要想掌握大规模并行编程(CUDA,MPI)知识,计算机体系结构也是必学的。

  可能大部分人(包括我导师)认为科学计算程序没必要程序写的多么漂亮,只要速度快,结果正确即可,可读性或者代码规范根本不在考虑之内。假如一段程序,过了一个月连你自己都看不懂,你写它根本没用,写完后这段代码就是死的。

  模型开发是一个持续性的工作,我觉得持续性其实就是可读性。以写文章来举例,大部分人写文章都无法一蹴而就,那么你当然也没办法保证写程序一遍就OK,修修改改都是免不了的。那么问题来了,假如你过去文章是像医生一样拿草书写的,过几天后你还读的懂么?(请自己脑补医生狂草)

  我觉得了解数据结构、算法可以大幅缩短你的代码量。因此就算你写的再难懂,逻辑再混乱,假如只有短短十几行,也是可以接受的,那么写这些程序的工作就不会变成无用功。

  代码规范性更像是写字时候一笔一划,写的工工整整。医生由于给别人看病需要所以会写草书,可是编程并非如此。听别人说过,规范的程序是完全不需要注释的,因为整个代码就像是注释一样。变量命名有意义,函数命名规范,都能帮助别人(包括自己)更好理解代码。

  最后提一点,就算你代码写的再差,只要手册写的好也能将功补过,从模型整体规划到每个函数具体实现全部写完也是一件要命的工作。(反正我是从没写过)

  不写伪代码或者流程图直接写程序就是耍流氓!这是大一我们上第一节C++课时老师给我们说的,当然原话不是这么讲的,但是大致就是这个意思。

  有的模型开发过程可能要历经几年,延续好几个博士生。那么问题来了,首先要能保证你在毕业之前按照原计划把模型开发完。(记得有个师姐在提交论文的前一晚还在改模型……)

  还有一点就是要考虑模型在开发完后是否是最新的,不过大部分科学计算模型都不用考虑这些问题。像我们实验室现在用的模型大部分都是90年代开发的,一般模型用十几年还是没问题的。(前提是你写的出来,而且写的比别人好)

  最后说说在学校这种大环境下怎么提高编程水平。我觉得只有多读别人程序了,而且要读写的比较规范的,然后就是照猫画虎,照葫芦画瓢而已。

  看的时候要多想,这个模型结构是怎么样的,为什么这样设置结构,有什么好处,代码读起来是不是清晰易懂,还有什么问题没有。像这样能读懂一两个模型基本就可以简单入门了,至于今后如何提高还是看将来到底要不要继续从事编程这种繁重的脑力工作吧。

  科学计算的成本主要是超级计算机的机时,人力成本(博士)很便宜,所以重视程序效率,不重视开发速度。所以瓶颈问题是经费不足,而不是人的技术。

  1. 需要标记好引用的文献,标记好每步骤的数学公式。否则代码很难读懂。(其实我觉得标记好了也不容易读懂。。)

  2. 保证正确性,对计算接口加一定程度的单元测试。第一步先是通过简单的方式正确的实现出来。将复杂算法分为多步,分别单元测试。组合成复杂算法后,继续测试。

  3. 性能监测。要有性能测试的代码。测试要全面,针对不同规模,进行足够多迭代。要注意设备和算法初始化问题,刨除初次迭代。

  4. 初步判断性能瓶颈。通过 注释、添加计时宏、profiler ,找到性能瓶颈。考虑增大并行性。找论文读,读懂并实现出来。

  5. 常犯错误是:越界、内存管理问题。排查的方式,主要靠注释和log,偶尔用断点debug能找出简单的问题(希望有人能指点一下有更好的工具?)

  8. 尽量使用nvidia,intel提供的计算库。注意调用的时候,注意参数会影响性能。比如数据对齐方面。

  10. 了解gpu和cpu的常见技术和陷阱,流水线、分支预测、多指令并发。合理使用寄存器和缓存加速。如果是具体某个硬件确定下来,了解缓存大小和加载方式也是必要的。

  见过太多读数学和硬件的人写的C代码,惨不忍睹。不过人家就是算东西,用到的都是语言中最简单的功能。

  非常不赞同所谓两边互相看不惯,在编程领域,写科学计算的都把正统软件工程师当大神看。但作为软件工程师,我也从没看不惯把编程语言当计算器使的,里面大量共识和算法我也不懂,因为他们从事另一个行业。

  但是我很讨厌会打几行代码就说自己是程序员的。曾经遇到一个学金融技术的,学了点C会做一些运算,到处说自己是程序员,然后自身能力不足找不到工作,怪计算机是垃圾学科”没能让他找到工作”,职责程序员全是废的。

  科学计算程序的编写通常需要利用数学函数或运算库,除此之外它和通常意义上的码农的工作有何差异?

  写出来的那些人。够简单吧。另外一个差异前面的人也提到了,就是科学计算重要的是你怎么描述计算的结果(你的结果说明了什么问题,和你想要的结果有什么差异,和别人的结果有什么差异),码农是关心写的程序好不好用,壮不壮,满不满足客户的需要。

  很简单啊,如果觉得自己学科的现有的程序有很大问题,自己编得好又心怀整个学科,那就自己写自己领域内可能公用的程序/库,然后共享给大家,节约大家的时间,提升整个领域的科研水平,功德无量啊。(一些大学教授会这么做,很酷;有的科研机构也会牵头给自己的领域写这种共用的库或者软件)

  当然,如果想转专业,最好直接转,别浪费时间(这么说是因为一般做科学计算的都会认为自己领域的科学计算解决的问题最有趣,或者自己的计算能得到同行的认可/引用/点赞最有趣)。

  没有计算机的时候,铁匠靠手工技能加工金属,后来靠操纵电动工具加工金属;文官靠书信、纸质报表进行治理;会计靠筹算、珠算、计算器来算账;科学家通过手工做实验,通过书信发表文章,通过笔算/心算/筹算/计算器来进行数据分析。

  现在有了计算机,编程让数控机床加工金属;字处理工具、宏(广义编程)、报表系统等来进行行政管理和财务管理;科学家可以用计算机仿真等手段进行一部分实验工作,通过Latex等排版工具编写文章,通过Fortran、R、Matlab、Python、ROOT等等语言写程序来分析数据。

  计算机是很多行业中,面向人类开放的机器接口,而码农(程序员)则是相应的行业中,面向机器开放的人类接口:直接从事生产的劳动者嘛!只不过,狭义的码农(很多行业都有人自嘲为码农,但其实不是真正的码农)其实就是除了编程,啥也不会的一个工种。这和其他专业领域首先具备专业技能,其次利用计算机加快生产的工种不同。

  所以,其实,拿今天(利用计算机开展工作的)科学家和今天各行各业的码农进行对比,就好像古典时期的科学家和铁匠/文官/会计对比一样,恐怕共同点仅仅是都有手艺/会写字/会计算。但要求是不同的。

  比如,为了科学计算而编写程序时,软件生命周期、配置项的成熟度等等软件工程方面考虑的东西,科学家往往不关心。但是对于参数的选取、对必要的近似的说明等等,则又是特别小心、必须明确的。因为要拿出去跟其他的结果对比。

  模拟近地小天体的轨道、尺寸、反照率的分布。如果只是为了画图的话,大概其差不多就行了。但是因为后面要对望远镜进行仿真,所以必须定好小天体的数密度随尺寸的broken power-law的尺寸指数,并且用已经观测到的近1000颗公里级小天体的样本数量来归一,等等。重要的参数,一定要注释清楚,是从哪篇文章里看来的。量纲一定要一致。图中太阳、水星、金星、地球、火星和木星的尺寸我却又不关心了,因为与估算望远镜本身的性能无关,仅仅起到“示意”作用而已,尺寸随便取取就好了。另外,几颗大行星的轨道位置都对吗?不对。为啥不对?方便画箭头啊!

  但至于说到我用来画图的程序本身,注释是否清楚,是否可以从python 2移植到python 3,我不关心。

  上面是一个“糙快猛”的例子。但也不是一直这样的。比如,前段时间,我需要解决一个球面上不具有旋转对称性的Kernel的卷积以及解卷积问题。目前现成的方法要么需要假设Kernel轴对称,要么需要对球面做平面近似。但因为我要分析的是巡天数据,球面已经不可能近似成平面了,所以就想了别的办法,找到一组正交的轴对称基,把Kernel分解,然后就可以快速计算了,还顺便找到一个快速的矩阵-矩阵乘法算法。

  所以,回到问题,如果说科学计算和通常所说的码农的编程有多大区别,那么区别还挺大的。因为科学计算一般不受软件工程的约束,一个研究课题的周期也比较短,少量科学计算软件工具有可能变成某个领域、某个行业的通用工具,但大部分软件都是随开题生,随结题亡。几年前,我试图想把我过去写的程序再整理整理,挖掘一些可共用的库出来,弄个工具包,但这个工作到现在也没完成,更可怕的是,需要整理的代码增加快,而整理的速度很慢——考虑框架等较为顶层的设计,根本就不经济。

  我忏悔,知乎玻璃心码农真是惹不起。哪敢歧视码农?我们一个组全员加起来都没蓝翔毕业的前端拿的多,自嘲已经是我们唯一的乐趣了。

  当然后者是理想状态,不过问题是如果你不遵守,那你修改就会越来越慢,最后结果就是什么都做不出来。所以与其说是为了别人,倒不如说长远还是为了自己……

  for(i=0;in;i++) ...... 咦??结果不对?试试这个: for(i=0;in+1;i++) ...... 靠!怎么还不对?改成这样试试: for(i=1;in+1;i++) ...... 唉。。。又错了,要不试试这样吧: for(i=1;in;i++) ...... 终于对了!!!

  double a[100000][10000], b[100]; for(.....){ for(.....){ for(....){ a[i][j] += a[i][j]*b[k]; } ......... 半分钟后:啊呀,这个程序怎么还没跑完啊,我还是输出一下进度吧: double a[100000][10000], b[100]; for(.....){ for(.....){ for(....){ a[i][j] += a[i][j]*b[k]; cout i , j , k endl; } ......... 原来计算机跑起东西来这么慢啊,看程序输出ijk的速度就知道了,看来我得等上一个周了,先挂着吧。

  double f(double x){ some huge calculation here } double a[10000],sum=0; for(int k=0;k1251;k++){ // do a for loop here double s = 0; // set s to zero for(int i=0;i10000;i++) // do a for loop here s += f(a[i]); // s equals s plus f(a[i]) sum += s*k; // sum equals sum plus s times k } 程序终于写完啦,看我习惯多好,关键地方随时写注释。啊呀,我的程序为什么这么慢啊!

  当然啦,吐槽归吐槽,认真你就输了。很多人吐槽说科学计算的程序不会用面向对象的编程,实际情况很多时候都是并不需要。科学计算面对的基本上没有啥复杂的类型,不像码农编程的又是二叉树又是网络连接句柄又是客户对象又是数据库句柄。比如说,如果你要模拟一堆原子在牛顿力学作用下运动,难道你还要这样不成:

  class velocity { private: double value; public: velocity(double v):value(v){} velocity():value(0){} virtual ~velocity(){} virtual double operator=(double v){ return value = v; } virtual double operator(double)(){ return value; } }; class coordinate { private: double value; public: coordinate(double v):value(v){} coordinate():value(0){} virtual ~coordinate(){} virtual double operator=(double v){ return value = v; } virtual double operator(double)(){ return value; } }; class atom { public: velocity vx; velocity vy; velocity vz; coordinate x; coordinate y; coordinate z; }; atom all_atoms[100000];

  另外,还有很多人吐槽说科学计算很多函数动辄几千行,这样做确实不是太利于阅读,但是如果这几千行代码没有需要复用的,写到一个函数里面有有什么问题,如果你的整个模拟过程需要几百个局部变量存储一些中间结果,这些中间结果随时都会被用到,难道你还是要硬生生把你的大过程分割成一堆有几百个参数的小函数,然后通过参数传递的方式把这一堆中间结果传递给小函数不成?即使可以定义一个结构体,把几百个参数放到一个结构体里面,难道你真的愿意就为了把本来就完整的函数拆开而额外定义一个结构体吗?

上一篇:冷水江项目运营偿债能力数据分析价格【鼎莱大数据】 下一篇:再下一城! 中国移动咪咕云直播带你见证侯志慧夺冠时刻