23-03-10
banq
我从你那里看到的关于清洁代码的大多数解释包括我在视频中提到的所有事情:
喜欢继承层次结构而不是if/switch语句,不暴露内部("德墨忒尔法则 Law of Demeter"),等等。
但听起来你听到我这么说很惊讶。
在我们开始之前,你能不能花点时间更全面地解释一下你对类型设计的想法,这样就能感觉到我们的分歧在哪里了?
BOB:脱节。嗯。我不确定是否有这种情况。
我看了你视频的前半部分。在那之后,我想我已经明白了你的意思。我在一个主题中回应说,我认为你的分析基本上是正确的。我还认为,你的修辞在代表 "干净的代码 "方面有点不准确。我不记得具体是什么不准确的地方了;而且这并不重要。
所以....是的,绝对的,你所介绍的结构并不是从系统中榨取每一纳秒性能的最佳方式。事实上,使用这些结构会让你损失很多纳秒。它们在纳秒级上并不高效。很久以前,这通常是很重要的。我们担心的是函数调用的开销和间接性的成本。如果可以的话,我们甚至会取消循环。这在嵌入式实时环境中尤其如此。
但是,如今那种重要的标准性环境已经很少了。绝大多数的软件系统需要不到现代处理器功率的1%。更重要的是,处理器是如此的便宜和可用,以至于在一个系统中增加更多的处理器是一件微不足道的事情。这些事实改变了从程序性能到开发团队性能的权衡,以及他们创建系统和保持这些系统运行的能力。而这些需求正是 "清洁代码 "理念发挥作用的地方。
对大多数组织来说,节约程序员的周期比节约计算机的周期更经济。因此,如果说我们之间有什么分歧的话,我想那只是在我们优先考虑的那种情况下。如果你试图从GPU电池中榨取每一纳秒,那么清洁代码可能不适合你;至少在你最深层的内部循环中是最费劲的。另一方面,如果你试图从一个软件开发团队中榨取每一个工时的生产力,那么清洁代码可以成为实现这一目标的有效策略。
============================================
凯西:为了更具体一点,让我明白你在这里所说的,我们能不能选一些具体的软件例子?例如,我认为我们都熟悉使用Visual Studio和CLANG/LLVM。这些都是你所说的绝大多数软件的合理例子,这些软件对现代处理器的要求不到1%?
BOB:不,在我看来,IDE是一个非常专业的软件系统。现存的只有少数几个,而且只有极少数已经变得流行。
集成开发环境是一个有趣的系统,因为它们跨越了一个巨大的领域的背景。有些部分的纳秒是非常重要的,而另一些部分的纳秒则非常不重要。现代集成开发环境必须能够在逐个击键的基础上解析大量的代码。确保解析代码能够保留纳秒数,会产生很大的影响。另一方面,设置配置对话框的代码甚至不需要这种效率的一小部分。
而且,作为一个旁观者,IDE的编译引擎所需要的那种效率更多的是算法上的,而不是周期性的。周期性代码可以提高一个数量级的效率;但是正确的算法选择可以提高许多数量级的效率。
不,我所指的那种需要<1%的现代处理器的软件是大多数程序员从事的常规运行系统。一个网站,一个日历应用程序,一个流程控制仪表板(针对一个简单的流程)。事实上,几乎所有的Rails应用程序,或任何Python或Ruby应用程序。甚至大多数Java应用。如果纳秒是你关心的问题,你根本不会选择这样的语言。
像Clojure(我目前的 "首选 "语言)这样的语言可能比同等的Java应用慢30倍,可能比同等的C应用慢60倍。但我并不关心这些。首先,如果有必要,我可以放弃使用Java(而且我已经在计算任务中使用了)。其次,对于许多应用程序来说,增加处理器是简单而便宜的。因此,我通常认为程序员的时间节省是有成本效益的。
不要误会我的意思。我是一个70年代和80年代的老汇编员和C黑客。在重要的时候,我勤奋地计算微秒(纳秒是远远超出我们所能想象的)。因此,我知道周期短的代码是多么重要。但是今天的处理器比我们当年使用的机器快一万倍(字面意思)。因此,对于现在的大多数软件系统来说,我们有能力用一些额外的每秒周期来交换程序员的效率。
============================================
凯西:如果我理解正确的话,你是说有两大类软件,所以我们也许要分别讨论每一类。听起来我实际使用的大多数软件都属于你所说的 "纳秒级重要 "的类别--换句话说,Visual Studio、LLVM、GCC、Microsoft Word、PowerPoint、Excel、Firefox、Chrome、fmmpeg、TensorFlow、Linux、Windows、MacOS、OpenSSL等等。我想根据你的回答,你会同意所有这些确实要关注性能的问题?
BOB:不完全是。相反,我的经验是,在模块层面上有一个广泛的软件适用范围。有些模块必须在纳秒级的期限内执行。其他的则需要微秒级的响应时间。还有一些模块只需要在毫秒级的限制下运行。还有一些模块,在响应时间接近一秒的情况下也能令人满意地运行。
大多数应用程序都是由涵盖上述大部分范围的模块构成的。例如,Chrome浏览器必须快速渲染。当你在填充一个复杂的网页时,微秒很重要。另一方面,Chrome浏览器中的首选项对话框可能甚至不需要在毫秒级的水平上进行响应。
如果我们把一个特定的应用程序中的模块按响应时间排列成柱状图,我想我们会看到某种非正态分布。一些应用程序可能有许多纳秒级的模块和少数毫秒级的模块。其他的应用则有大多数模块在毫秒范围内,而纳秒范围内的模块很少。
例如,我目前正在开发一个应用程序,其中绝大多数模块在毫秒级上工作良好;但有几个模块需要20倍的性能。我的策略是用Clojure编写毫秒级的模块,因为它虽然慢,但却是一种非常方便的语言。微秒级的模块我是用Java写的,速度快得多,但远没有那么方便。
有一些语言和结构,可以抽象出机器的硬金属性能,使程序员更容易专注于问题领域。对于程序员来说,当他们不必关心优化二级缓存的点击率时,编写毫秒级的代码要高效得多。相反,他们可以考虑业务需求,以及在未来十年内必须处理他们的代码的其他程序员。
还有一些语言和结构暴露了机器的硬金属特性,使程序员更容易从算法中挤出每一个纳秒。这些结构可能不是最容易编写、解释或维护的;但当纳秒计数时,忽视它们将是愚蠢的。
当然,在这个范围的中间也有一些语言和环境。了解这些环境,并知道哪种环境最适合手头的问题,是我们都需要熟练掌握的事情。
我十几年前写过一本名为《清洁代码》的书。它更多地关注问题的毫秒级方面,而不是纳秒级方面。在我看来,在当时,甚至在今天,程序员的生产力问题是一个重要问题。然而,这本书对于你和我在这里讨论的问题并不近视。例如,如果你阅读以 "倾向于多态性...... "开始的那一节,你会看到关于开关语句在某些情况下的好处的讨论。事实上,有整整一章都是关于这种背景的。这一章包括以下声明,我认为它体现了我在这次讨论中所表达的观点。"成熟的程序员都知道,所有东西都是一个对象的想法是一个神话。有时你确实需要简单的数据结构,并在其上操作程序。"
==============================================
CASEY:我想在这里说得具体一点,所以我试着重新表述我的问题。Visual Studio、LLVM、GCC、Microsoft Word、PowerPoint、Excel、Firefox、Chrome、fmmpeg、TensorFlow、Linux、Windows、MacOS和OpenSSL等程序,在你的术语中,至少有一些 "模块 "是 "毫秒级 "的吗?
BOB:毫秒?当然了。我想说的是,他们都有微秒级的模块;许多人有纳秒级的模块。
十几年前写过一本名为《清洁代码》的书。它更多地关注问题的毫秒级方面,而不是纳秒级方面。在我看来,在当时,甚至在今天,程序员的生产力问题是一个重要问题。然而,这本书对于你和我在这里讨论的问题并不近视。例如,如果你阅读以 "倾向于多态性...... "开始的那一节,你会看到关于开关语句在某些情况下的好处的讨论。事实上,有整整一章都是关于这种背景的。这一章包括以下声明,我认为它体现了我在这次讨论中所表达的观点。"成熟的程序员都知道,所有东西都是一个对象的想法是一个神话。有时你确实需要简单的数据结构,并在其上操作程序。"
CASEY:我想在这里说得具体一点,所以我试着重新表述我的问题。Visual Studio、LLVM、GCC、Microsoft Word、PowerPoint、Excel、Firefox、Chrome、fmmpeg、TensorFlow、Linux、Windows、MacOS和OpenSSL等程序,在你的术语中,至少有一些 "模块 "是 "毫秒级 "的吗?
BOB:毫秒?当然了。我想说的是,他们都有微秒级的模块;许多人有纳秒级的模块。
(第二天)顺便说一下,那天我回去看了你的整个视频。我想,既然我们在进行这种讨论,我应该研究一下你讲的整个故事。虽然我对你的一些言辞有些不满,但我必须补充你的分析非常贴切。
某些形状的面积都可以用同样的基本公式(KxLxW)来计算,这个可爱的见解是我认为只有程序员和数学家才能真正欣赏的时刻之一。
总的来说,我认为你的视频提供了一个很好的例子,说明程序员在资源受限的环境中解决受限问题时必须做的各种事情。显然(至少我认为应该是清楚的),在一个资源丰富的环境中,人们不会喜欢KxLxW的解决方案,除非人们非常确定企业不会将问题扩展到一般的形状。而且,事实上,即使问题仍然被限制在允许KxLxW解决方案的形状上,分离成更传统的公式可能更符合其他程序员的期望;并且不会导致他们困惑,并重新验证相对新颖的方法。虽然这可能会使他们失去洞察力的美好时刻,但这将使他们能够毫不拖延地继续完成他们的任务。
我不知道你是否读过唐-诺曼的作品。很久以前,他写了一本名为《日常事物的设计》的书。这本书很值得一读。在那几页中,他说了以下经验法则:"如果你认为某些东西很聪明,很复杂,那就要小心了--它可能是自我放纵"。在一个资源丰富的环境中,我担心KxLxW的解决方案可能会违反这一规则。
============================================
CASEY:好的,听起来我们对软件的分类有了相同的看法。我想谈谈我对你所描述的编码实践的描述,因为它适用于像LLVM这样的软件,因为它是我给出的列表中的一个软件,而且它刚好是开源的,所以我们知道它是如何构建的(不像Visual Studio)。
我认为你在上述段落中--以及在你的书和讲座中--所说的是,当对LLVM这样的大型软件进行编程时,程序员在编程时不需要关心性能。他们应该主要关心的是自己的生产力。如果用你之前的例子来说,是一个简单的日历应用程序,那么他们最好不要考虑性能问题。但在LLVM中,由于这属于有时 "纳米/微米/毫秒都很重要 "的范畴,那么他们就必须在某些时候考虑性能问题。这一点就是当他们发现程序运行得太慢时,无论何时都会发生。
在LLVM中,也许那是第一次有人试图用它来构建一个真正的大型程序,比如虚幻引擎或Chrome之类的。当这种情况发生时,那么假设性能问题将出现在代码的一些孤立的部分(我相信你在讨论中把它们称为 "模块"),所以现在只需要把这些部分改写成面向性能的。
这就是我对你目前所说的话的理解,也是我对你最近所说的话的理解,比如 "如果我的Clojure代码太慢了,我可以随时下降到Java",意思是如果这部分代码需要更多的性能,你可以用Java重写。
这样的描述公平吗?
BOB:我是敏捷宣言的签署者之一,我仍然相信有一些前期的架构和设计。(实际上,我很确定他们都是这样的。是后者的狂热者认为在没有任何预先考虑的情况下跳入代码更好)。
在你提到的案例中,我希望我能够很好地思考这个问题,认识到我可能会在哪里遇到性能问题,从而更加关注这些模块。例如,我可能已经创建了一个非常微弱的模块版本,然后在分析行为的同时对其进行折磨测试。当然,我担心的是投入大量的时间和精力的方法最终不能满足客户的需求。
当然,底线是,单因素分析总是次优的。没有一个真正的方法。(这是我在《清洁代码》中多次试图提出的观点)。
===================================================
CASEY:我已经有很多问题想问了,但你的最后一个回答与其中一个问题的衔接最好,所以我就用那个问题吧:)在这次谈话中,你已经谈到了软件架构中几个关键的性能问题。一个IDE解析器的 "纳秒级 "关注,将 "模块 "划分为纳/微/毫/秒级的响应时间要求,建议程序员(在这种情况下,你)在编写软件之前创建 "一个非常微弱的模块版本,然后对其进行酷刑测试,同时对其行为进行分析",以确保性能可以接受,甚至认为你可能不得不根据性能要求选择不同的语言(Clojure vs. C,在你的例子中)。在你的例子中,Clojure vs. Java vs. C)。
综上所述,你说:"了解这些环境,并知道哪种环境最适合手头的问题,是我们都需要精通的"。
考虑到这些,我想回到最初的问题:为什么你会惊讶于人们,比如我自己,把 "干净的代码 "与你在这里写的关于性能的东西有效地联系在一起?我刚才列举的这些东西在你的教义中都没有得到突出的位置。当然,我并不是说你不能在书中或博文中找到一个关于性能的句子。但是从数量上看,你在这里说的事情几乎没有得到很多人点头同意。
作为一个具体的例子,这里有一整个多小时的、由六部分组成的关于 "清洁代码 "的系列讲座,在9个小时的时间里,你在这里提到的东西没有一个被讨论。
https://www.youtube.com/playlist?list=PLmmYSbUCWJ4x1GO839azG_BBw8rkh-zOj
如果性能问题像你在这个主题中所说的那样重要,为什么九个小时中没有至少一个小时专门用来向听众解释诸如学习性能、提前计划代码的哪些部分可能对性能有影响、在这些情况下避免有害于性能的编程结构、事先创建性能测试(如你在前面答案中描述的那种)等事情的重要性?
另一种问法是,你是否认为性能意识对你来说也很重要,因为你在自己编程时已经习惯性地这样做了,因此你没有把它放在必要的突出位置,以确保你的听众--他们往往对性能知之甚少--在正确的时间和正确的方式上考虑它?特别是如果正如你所说,"我们都需要精通 "这些事情?
BOB:坦率地说,我认为这是一个公平的批评。碰巧的是,我昨天教了一门课,其中我花了很多时间谈论我所教的学科和原则的绩效成本以及生产力的好处。所以谢谢你的鼓励。
我想我没有使用惊讶这个词。如果我说了,那也不是指这个话题,而是指语气。关于这一点,已经说得够多了。
你问我,我是否一直认为性能的重要性是理所当然的。经过一些自我反省,我认为这很有可能。我不是性能方面的专家。我的专长是帮助软件开发团队有效地建立和维护大型复杂软件系统的策略、纪律、设计原则和架构模式。而每个专家都知道,也必须与之斗争,专家的锤子认为一切都像钉子。
你还问我 "为什么..."。如果我在上面没有回答这个问题,我就简单地把问题转过来,指出这可能是由于同样的原因,你的视频只专注于性能的放大,而对其他的关注点进行了严厉的诋毁。对于性能锤来说,一切看起来都是钉子。)
话虽如此,我发现这次谈话比我最初预想的更有益处。它促使我的观点发生了变化。你不应该期望这种变化是巨大的。你不应该指望我制作关于清洁代码有多可怕的视频;-)。但是如果你看了我接下来的9个小时的视频,你可能会看到更多关于性能问题的 "勉强点头"。我想你可以期待两到三次的点头;-)
因为,正如你所指出的,我确实认为性能问题是很重要的,足以预测和计划。
==================================================
CASEY:老实说,点头是我希望在这里达到的大部分目的。)为了强调我今天认为性能有多重要,我在试图编辑github上的这个文件时注意到,如果我输入一段太长的行数,它就会开始变得非常慢,而且很难打字!"。虽然只是几百个字符,但系统中堆积了这么多层的东西,本应瞬间完成的工作变得异常缓慢。因此,我如此强调性能的原因之一是,现在的软件似乎变得异常缓慢,即使是简单的任务。事实上,为了让你知道我不是在编故事,这里有一段视频,我记录了打这段话的速度是多么的慢。
https://www.youtube.com/watch?v=gPgm28zXNEE
而这是在Zen2芯片上,它的速度特别快!无论什么样的组织力量(在这种情况下甚至可能是跨公司的力量)使这种事情变得普遍,都会从听到诸如你在这次谈话中早些时候所说的话中受益匪浅。有大量的组织绝对没有考虑到 "纳米/微米/毫秒 "的细分,他们需要这样做。只要把这个想法放在他们的脑子里--他们需要有机构能力在太晚之前及早识别出性能问题所在,并且有机构参与者有能力解决这些问题--就会是大多数开发组织的一个重大改进。
因此,我们绝对可以在这里结束对话。如果你想继续下去,下一件要谈的事情就是你提到的 "强烈的诋毁"。这将使我们进入架构领域,而不仅仅是性能,但如果你愿意,我很乐意去那里。你的选择!
BOB:那段视频很有意思。我得问问你用的是什么浏览器。我用的是Vivaldi(Chrome的一个分支),它也出现了同样的滞后。(虽然没有你那么糟糕。)所以我做了一些实验。结果发现,这种滞后与文件的大小没有关系。相反,它与段落的大小有关。段落越长,滞后就越长。事实上,这个段落已经无法跟上25cps的重复率。而且我在这一段中输入的内容越多,滞后就越严重。
现在,为什么会这样呢?首先,我想象我们都在向同一个javascript代码输入。毕竟,没有人愿意使用写在浏览器中的工具了;-)我的意思是,JavaScript就是这么好。其次,我还想象,这段代码的作者从来没有预料到你和我会把整段话装进一行。(注意左边的行号。)即使如此,在25cps的速率下,在200-300个字符时,滞后变得非常明显。那么,会是什么情况呢?
会不会是程序员使用了一个写得不好的数据结构,该结构每次增长时都要分配一个新的内存块,然后将数据复制到新的内存块中?我记得老的Rouge Wave C++库就有一个这样的增长字符串。孩子,哦,孩子,那会变得很慢!你可以计算一下。你可以算算看。如果你问我,那是O(n^2)。
当然,这更像是一个算法问题而不是直接的效率性能问题。而且,事实上,当一个东西看起来太慢时,算法总是第一个要看的地方。但是,你的观点是正确的。这里的程序员根本就没有预料到我们会把他们的代码用于何种用途;而且他们也没有很好地处理未预料到的负载。他们并没有考虑到这一点。