原文http://www.altdevblogaday.com/2011/11/22/parallel-implementations/
by John Carmack
我一直都在“无畏的编码”,勇敢地干掉所有我认为有更好解决方式的东西。甚至于我为此感到一点点骄傲:“我不怕承受作正确事情所带来的后果!”当然,诚实的讲,这样的后果常常会撂倒一个新手,因为他们需要对付那些因此而无法工作的愤怒的开发者。
当然,如果这些改动真的是灾难性的,你可以通过源代码控制工具回滚,但是如果你的改动真的带来了好的效果,尽管其中包含着痛苦,你还是会希望继续前进。更微妙的是,总是有各种情况阻止你对新方法和老方法进行真实的比较。由于回滚代码重新构建运行测试是一件痛苦的事情,就算你怀疑你的代码在一些你重写时没有考虑到的特殊情况下会出问题,你也不会希望经常这么作。
现在当我实现一个新的想法时,我尝试着不去修改已有的代码,而是将其与原有想法并存。这样使得对两个实现进行真实的比较变得容易,并且如果新的实现中有问题,回到原来稳定的实现也变得简单了。在控制台中改变一个变量来获的不同的实现,比运行老的exe并且回滚代码重新构建方便的多。
对于一些任务这是显而易见的。如果你有一个ray tracer,很容易就可以实现一个允许Trace()函数使用各种后端实现的接口,比如kD tree,BVH,BSP。同样的也可以使处理代码为各种后端实现构建他们使用的加速数据结构。如果丢失了一些像素,切换到其他实现再试试。
然而,这个方法在其他方面更具威力。这些年我已经做了很多从软件渲染引擎到硬件加速的转换工作。过去,我必须从头开始,首先实现环境渲染,接着是角色,然后是特效。总是会有一些小的特性被漏掉。比较两个实现必须在两个系统上同时进行游戏。在最后两次转换过程中,我先让软件渲染引擎在新的系统上跑起来,这样所有的东西都可以在低帧速率下得以测试。然后并行这实现硬件加速,使两种方案在任何时候都可以立即切换。对于一个在windows模拟器中开发的移动版OpenGL ES应用程序,我打开一个独立的硬件加速窗口,使我可以同时看到两种实现。这是开发中的一个重大胜利。
如果你正在处理的任务可以被表示为一个简单的将输入处理为一个输出的纯函数(pure function,函数时编程),将其切换成不同的实现非常简单。如果是一个需要维护内部状态或者具有多个入口点的系统,在切换时就要格外小心了。如果这个系统比较混乱,内部使用许多对其他系统的调用来维持平行状态改变,那就应该在做平行实现前先对系统做一下清理工作。
我一般会把并行实现分为两类:参考实现,小而简单,会持续维护。实验性实现,你希望获得成功,但在你对功能和改进有足够信心之前,先用其他的实现替代几个星期。
在构建实验性实现时,完全可能违反一些常规的好的编码规则--拷贝,粘贴,查找替换重命名可以作为一个好的开始方法。在保持原有的代码能正常工作的前提下,勇敢的拷贝吧。为了方便,经常需要给已有代码传递一些选项,而不是完整的做一个并行实现。这是一个灰色地带,我一直倾向于找到哪些由于使用选项方法而搞乱两个版本的额外路径复杂度,而且需要在两个实现中做一定成都的妥协。
每次我使用这个并行实现方法时,都会感觉非常的受益,并且现在我更倾向于使用适合这个方法的编码风格。
强力推荐。