The Kollected Kode Vicious

Kode Vicious - @kode_vicious

  下载本文的PDF版本 PDF

不想要的惊喜

当那个糟糕的API落到你头上

亲爱的KV,

我读你在Queue上的咆哮已经有一段时间了,我不禁想问,有什么代码是你喜欢的吗?你似乎总是那么消极;我真的很想知道你是否真的认为编程世界是如此丑陋的地方,还是在某个地方,存在着一个你去的快乐之地,但你从未告诉你的读者。
一位快乐的程序员

亲爱的先生或女士 快乐

虽然我会尽量不反驳你称我的作品为“咆哮”,但我不得不说,我对你的问题感到惊讶。KV是一个快乐、开朗、外向的人,他不仅拥有一个“快乐之地”,而且还随身携带它,与周围的每个人分享快乐和欢笑,并给小孩糖果(咳嗽)。

在我洗脑之后,我可以更诚实地回答一下。是的,事实上,存在着优秀的系统,而且在我工作期间,我见过好的代码,有时甚至是伟大的代码。我现在想描述其中一段好的代码。不幸的是,要解释这段代码是什么,需要一些背景知识,但请耐心听我说。也许你可以先去你的“快乐之地”放松一下。

我最近的项目之一是在我工作的操作系统FreeBSD上扩展对hwpmc(硬件性能监视计数器)的支持。顾名思义,hwpmc是在硬件中实现的,在这种情况下,硬件指的是CPU。我不知道你是否读过CPU或芯片的文档,但在我们这个行业中,很少有比这更难写或更令人不愉快的阅读材料了。主题本身就枯燥乏味,这已经够糟糕的了,但更糟糕的是,编写此类文档的人要么不了解这项技术,要么是糟糕的作家,而且通常两者兼而有之。从这个基础上出发,典型的软件工程师编写的代码在某种程度上反映了规范,而且正如在有毒土壤中生长的东西本身也会变成有毒的,代码通常也像规范一样令人困惑。

什么是hwpmc?它是一组位于CPU上的计数器,可以记录工程师感兴趣的各种类型的事件。如果你想知道你的代码是否在疯狂地使用L2缓存,或者编译器是否生成了次优代码,从而搞乱了流水线,那么这就是你想要使用的系统。虽然这些东西可能看起来很深奥,但如果你正在从事高性能计算,它们就至关重要。你可以想象,这样的计数器是特定于CPU的,但不仅仅是公司层面,英特尔与AMD不同:即使是CPU型号也会影响存在的计数器,以及如何访问它们。
英特尔当前手册《Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide, Part 2》中关于hwpmc的章节共包含249页:81页描述各种芯片上的各种系统,168页涵盖你可以在芯片上使用的所有计数器。那是一本相当大的小说,当然,没有有趣的故事主线。向英特尔的技术作家致敬,因为这不是我读过的最糟糕的芯片手册,但我仍然宁愿读其他的东西。一旦我读完所有这些背景材料,我就有点担心当我打开文件时会看到什么。

但我并没有太担心,因为我个人认识编写这段代码的程序员。他是一位非常勤奋的工程师,他不仅是一位优秀的程序员,而且还能解释他所做的事情以及原因。当我告诉他我将尝试向他编写的系统添加更多芯片型号时,他给我发了一封1300字的电子邮件,详细说明了如何向系统添加对新芯片和计数器的支持。

这段软件有什么了不起的?好吧,让我们看一下代码的一些片段。始终在阅读代码之前阅读头文件很重要,因为头文件是定义结构的地方。如果结构没有在头文件中定义,那么你从一开始就注定要失败。查看我打开的第一个头文件的顶部,我们看到以下代码

 #define	PMC_VERSION_MAJOR	0x03 #define	PMC_VERSION_MINOR	0x00 #define	PMC_VERSION_PATCH	0x0000 

这些行为什么对我来说表明了高质量的代码?是大小写?间距?制表符的使用?不,当然不是!而是存在版本号的事实。这位工程师清楚地知道他的软件不仅会被他自己修改,而且还会被其他人修改,并且他通过拥有主版本号、次版本号和补丁版本号专门考虑到了这一点。简单吗?是的。经常发现吗?不。

接下来几行?记住这只是我打开的第一个文件?也很有启发意义

 /* * Kinds of CPUs known */ #define __PMC_CPUS()						\ 	__PMC_CPU(AMD_K7, "AMD K7")			\ 	__PMC_CPU(AMD_K8, "AMD K8")			\ 	__PMC_CPU(INTEL_P5, "Intel Pentium")		\ 	__PMC_CPU(INTEL_P6, "Intel Pentium Pro")	\ 	__PMC_CPU(INTEL_CL, "Intel Celeron")  	\ 	__PMC_CPU(INTEL_PII, "Intel Pentium II") 	\ 	__PMC_CPU(INTEL_PIII, "Intel Pentium III") 	\ 	__PMC_CPU(INTEL_PM, "Intel Pentium M") 	\ 	__PMC_CPU(INTEL_PIV, "Intel Pentium IV") 

KV的常客可能会认为让我高兴的是注释,但他们会错的。而是将常量翻译成可理解的文本名称。在处理一段软件时,没有什么比不得不记住另一个愚蠢的,通常是十六进制的常量更令人沮丧的了。那些能记住他们从0x100开始编号,而0x105恰好很重要的程序员并没有给我留下深刻印象。谁在乎?我不在乎。我想要的是使用描述性名称的代码。还要注意代码中的常量不是很长,但刚刚好足够长,可以很容易地在代码中知道我们正在讨论哪个芯片。

图1显示了来自头文件的另一个很好的例子。我使用了这个片段,这样我就可以避免包含整个文件。在这里,机器相关的结构与机器无关的结构是分开的。将特定于某种类型的CPU或设备的数据位与独立数据分开似乎是显而易见的,但看似显而易见的事情在实践中很少做到。工程师考虑哪些位应该放在哪里这一事实表明代码具有很高的质量。阅读每个元素的描述性注释,以及在pmd_cputype来自enum pmc_cputtype的情况下,指示在哪里可以找到正确的类型。

关于这个文件的最后一个评论。请注意,程序员正在用C语言编写对象。操作系统内核和其他底层代码仍然是用C语言编写的,尽管现在有很多例子表明人们正在以不同的方式思考这个问题(例如,Apple的Mac OS X驱动程序是用C++编写的),但底层代码将继续用C语言编写。这并不意味着程序员应该停止使用他们从数据封装中学到的教训,而是意味着在可能的情况下做正确的事情很重要。这里列出的结构是一个对象。它具有数据和作用于数据的方法。BSD内核在这方面已经使用了20多年了,这是一个其他人应该学习和记住的教训。

这些只是这段代码中的几个例子,但在一个又一个文件中,我都发现了相同水平的质量,相同的漂亮代码。如果你真的有兴趣看看好的代码是什么样的,那么我建议你自己阅读代码。如果说有什么地方是“快乐之地”,那就是像这样的代码。

被胁迫者


附言:包括FreeBSD在内的许多操作系统内核的完整代码交叉引用可以在 http://fxr.watson.org/ 找到,而你正在寻找的代码可以在 http://fxr.watson.org/fxr/source/dev/hwpmc/ 找到。类似的交叉引用集可以在codespelunking站点找到:http://www.codespelunking.org/freebsd-current/htags/

亲爱的KV,

弗雷德里克·P·布鲁克斯在他的书《人月神话》中用祖父般的耐心告诫我们,要计划构建一个原型?然后把它扔掉。反正你也会这么做的。

在某个时候,这导致了一场名为原型设计的年度时尚(以前被称为试错法的编程方法),这表明太少和太多同样糟糕。

你如何看待创建原型,特别是,一个原型需要多么忠实才能解决真正棘手的细节,而不是仅仅让营销部门获得屏幕截图,以便他们可以炫耀?

署名,
一位(非)典型的工程师

亲爱的非典型,

你说的“以前被称为试错法”是什么意思!?!你是要告诉我这种时尚已经死了吗?就我所知,它仍然活得很好,尽管也许它的许多实践者实际上并不知道他们的知识血统。实际上,我怀疑它的大多数实践者都拼不出知识血统

唉,好的建议往往会被过度采纳,并在一段时间内成为口头禅。任何重复足够多的东西似乎都会变成真理。正如我确信你知道的那样,布鲁克斯先生的建议旨在克服在计算机科学中过于普遍的“它必须是完美的”口头禅。在设计阶段就能了解一切的想法是一种谬论,我认为这种谬论始于数学家,他们是世界上最早的程序员。如果你整天都在看纸上的符号,然后只是偶尔才需要将这些符号构建成工作系统,那么你很少会体会到当你的系统的美与硬件的丑陋现实相遇时会发生什么。

从这个起点来看,很容易理解为什么1950年代和1960年代的程序员想要首先写下一切。问题是,一张纸是计算机的非常糟糕的替代品。纸张没有铜中电子速度、电线长度或磁鼓(现在是磁盘,即将成为闪存)速度引入的奇怪延迟。因此,当时告诫人们只需构建该死的玩意,无论它是什么,然后从原型中吸取教训并将其整合到真实系统中,这是完全有道理的。

自从首次给出该建议以来,计算机速度的提高使得人们能够在过去构建单个系统的时间内构建更大、更快,当然也更多的原型。原型病患者实际上只是胆小鬼。不在沙地上划线是工程师或团队怯懦的表现。“这只是一个原型”太经常被用作避免审视系统设计中难题的借口。在某种程度上,这种原型设计已经成为布鲁克斯先生试图做的事情的完全相反。原型的重点是找出难题在哪里,一旦确定了难题,就使完成整个系统成为可能。而不是给营销部门一些漂亮的东西来展示给潜在客户?那是纸巾和大量威士忌的用途。

我对原型的立场是什么?与我对分层或将系统分解为越来越小的对象的立场相同。你应该只构建尽可能多的原型,以找到并解决你试图构建的任何东西所产生的难题。其他任何事情都只是顾影自怜。现在,别误会我的意思,我和其他人一样喜欢顾影自怜?也许更喜欢?但当我沉浸在我的迷幻收藏中时所做的事情,我向你保证,与编写软件无关。

KV

KODE VICIOUS,对于凡人来说,他就是George V. Neville-Neil,他为了乐趣和利润而从事网络和操作系统代码工作。他还教授各种与编程相关的课程。他的兴趣领域是代码探险、操作系统和重写你的糟糕代码(好吧,也许不是最后一个)。他获得了马萨诸塞州波士顿东北大学的计算机科学学士学位,并且是、Usenix协会和IEEE的成员。他是一位狂热的自行车爱好者和旅行家,目前居住在纽约市。

版权 © 2008 由所有者/作者持有。出版权已授权给。

acmqueue

最初发表于 Queue vol. 6, no. 5
数字图书馆 中评论这篇文章








© 保留所有权利。

© . All rights reserved.