并行实现 by John Carmack
(译了一篇卡马克的博客,文章好,译的烂,有能力的同学推荐看原文。)
我曾经一直都是代码无惧的(Code Fearlessly), 每当我想到更好的方式去实现什么的时候我就会砍掉所有东西重写。 我甚至对此有点自豪——“我不会为了做正确的事而畏惧遭受不好的后果。” 当然,说实话,这些后果通常会落到一个更初级的程序员头上,他不得不去怒气冲冲的处理我为了“更好”而砍掉代码导致的被意外打断的工作。
如果你的改动是错的,你当然可以通过版本控制软件回滚到之前的版本, 但是如果你找到了某个解决问题的方向, 即使会有一点风险,你仍然希望继续向前推进。 问题是,你时常想知道新旧代码之间的确切比较,比如在你重写新代码的时候突然考虑到在某个特殊情况下可能新代码不一定工作的很好。但把代码回滚到旧版本然后去重编并且跑测试实在是太痛苦了,即使是你不得不这么做的时候。
我现在的做法是保留旧代码的情况下并行实现新想法,而不是直接在旧代码上修改。这样就可以很轻易的比较两者,而且当发现新代码有缺陷的时候可以立刻回到旧版本上。最重要的区别在于, 你要做的只是修改一下控制台参数去运行不同的版本,而不是回退代码重新编译。
对有些任务来说,好处显而易见。 比如你在实现一套光线追踪代码,开放一个接口允许你修改Trace()函数的后端是使用kD Tree,还是BVH 或 BSP 并不是什么难事, 这样同一种用例可以很容易的执行在不同加速结构的代码里。缺失了一些像素? 转入其他实现里看看怎么回事。
然而, 我现在已经开始更大胆的使用这种策略。多年以来,我已经实现过很多次从软件渲染引擎到硬件加速的转换, 以前, 我基本上都是从头开始,首先实现环境渲染, 然后是角色,然后特效。 写完之后发现总是有很多小特性忘了实现,于是就和原始版本一起跑一次去比较不同。
但最近两次, 我的做法是首先让软件渲染代码在新平台上跑起来, 这样所有东西都可以在低帧率下测试, 然后并行实现硬件加速版本, 你可以在随时切换两者的环境下逐渐实现整套系统。 有一次我在windows虚拟机上开发一个移动OpenGL ES 应用, 我开启了一个完全分离的窗口用来显示加速视图, 这样我就可以同时看到原来的软件版本和新的硬件加速版本。 这对于开发者绝对意义非凡。
如果你现在正工作在一个有参数传入和一个返回值结构的简单流程的函数,那可以很容易的切换成不同的实现。如果是一个包含了内部状态或者有很多入口的系统,那你必须更谨慎的进行切换。如果是一个有很多内部调出到其他包含并行状态的杂乱系统,那你需要在并行实现前先做一些清理。
我有两大类并行实现的方法,第一类是参考性实现,更小也更简洁,但是会长时间进行; 另一类是实验性实现,当你期望一个版本胜过当前版本, 并且在几周后当你对新版本功能完整并且实质性提升性能有信心的时候提交实现到版本控制里的时候, 你应该考虑实验性实现。
在建立一个实验性实现的时候违反一些传统意义上的编程准则是完全合理的,复制, 粘贴,寻找-替换 实际上是一个很合理的开始工作的途径。在原来代码健壮和稳定的情况不必害怕复制。 因为比起启动一个完整的并行实现, 略过一系列到原来代码的选项标志(option flag)经常是诱人的捷径。 这是一个灰色地带, 我曾试图于找到额外的方法去处理复杂的标志,但是却经常最后把两个版本都弄乱, 并且在某种程度上危害到两种实现。
在我任何一次使用并行实现方法的时候, 我都感到益处多多, 所以现在我非常倾向于让代码支持这种风格。强力推荐!
浙公网安备 33010602011771号