browser icon
You are using an insecure version of your web browser. Please update your browser!
Using an outdated browser makes your computer unsafe. For a safer, faster, more enjoyable user experience, please update your browser today or try a newer browser.

键歇(4)

Posted by on 2014 年 04 月 11 日

你可以任意转载本文,但请在转载后的文章中注明作者和原始链接。
媒体约稿请联系 titilima_AT_163.com(把“_AT_”换成“@”)。

我刚出道不久的时候,在公司的技术讨论会上一直都扮演着旁听兼打酱油的角色。记得有一次,大家聊到了关于引擎效率优化的问题,有一哥们说了下面的这番话:

“我亲自测试过文件操作的性能:我写了个循环来读文件,每次只读一个字节,结果发现 fread 居然要比 ReadFile 快很多。”

以我当时的认知,这整个的事情大概是这样的:ReadFile 是操作系统的 API,它肯定较之于 fread 更为底层化;换句话说,fread 应该是对 ReadFile 的一个封装,所以它怎么可能会比 ReadFile 更快呢?——但是,无论如何,这都是事实。

于是,这个问题在我心中沉淀了多年,我则时不时会再将其忆起,然后咀嚼一番。

也不知什么时候,我突然发现我已经在不经意之间参透了这个奥秘。所谓独乐乐不如众乐乐,在本篇的《键歇》之中,我就来分享这个奥秘。当然,肯定有人早已熟稔了此间的那些小把戏——随你吧少年,反正我这儿没退票那一说儿。

杯水车薪

我觉得本篇开始那哥们所说的那段测试代码与成语“杯水车薪”所描述的场景很相似,因此我决定就以“杯水救薪火”为喻,来解释一下隐藏在 fread 布幔之下的那些秘密。

那么,先来看看我的场景设定吧:

  1. 火场距离最近的水源为一公里,救火者没有任何的交通工具可用,只能徒步往返于火场和水源之间。
  2. 救火者的洒水救火行为必须使用一个指定大小的水杯,不能使用任何的现代设备,比如抽水机和水龙头。

这些设定太荒诞了是不是?无论如何,请先享受接受它们,然后来看看我为之提供的两种解决方案:

  1. 拿着水杯舀一杯水→来到火场,倒水灭火→回到水源舀水。
  2. 拿着水桶舀一桶水→来到火场,用水杯从桶里舀水灭火→桶里的水用完后,回到水源舀水。

至此,相信 ReadFile 和 fread 的那点事儿在你心里一定已经跟明镜儿似的了。那么我也无意再去挖 fread 的 CRT 源码给你看,还是让我们继续聊聊这个“杯水车薪”的场景中有关优化的那些事儿吧。

水源

我们知道,水源和火场有着一公里的距离。由于没有交通工具的协助,因此我们大致可以确定:这一公里的距离是“杯水车薪”场景中的关键瓶颈;换句话说,它客观存在且无法改变,只能通过策略来进行适度的规避。
将问题具体到 ReadFile 和 fread 上来,那么这里的瓶颈显然是效率低下的磁盘 I/O。也许你会说,这个瓶颈事实上是可以通过改换 SSD(固态硬盘)来克服的,就好比在“杯水车薪”中其实是可以使用交通工具代替步行来提高救火效率的。

诚然,我在设定中却是禁止了交通工具的使用的。这个禁止的原因我一会儿再讲。

水杯

现在来看看水杯。在设定中,我规定了最后救火的操作(洒水的行为)必须用水杯来完成。由是而平心论之,“杯水车薪”中最为荒诞的设定便大抵莫过于此了——当然,本篇《键歇》开头处那个每次只读一个字节的 ReadFile 操作似乎也确是游离于现实之外的。
无论你如何吐槽,请将它只看作是一个拥有特定限制、特定需求的数据表达方式,这种限制和需求则恰好会将系统中某个性能上的缺陷放大成为软件中不可忽视的性能瓶颈。

水桶

显然,水桶是“杯水车薪”中最为重要的优化技巧,我们可以称之为“以空间换时间”的策略。也就是说,既然这一千米的距离是客观存在、无法改变的,那么我们可以通过减少往返次数的策略来最大程度地规避这个瓶颈。
如你所见,这个策略就是水桶。我们先用它来预先获取相对更多的水量,然后每次用水杯舀水的时候就可以直接从水桶里来取水了。相比一千米之外的水源,水桶是在自己身边的,所以大大提升了洒每一杯水的效率。
用咱们的行话来讲,这里的水桶其实就是传说中的缓存(cache)——存而缓用之,是为“缓存”也。

代价

有道是“天下没有免费的午餐”,既然要规避瓶颈的开销,那么势必会付出其它的代价。在“以空间换时间”的策略中,这个代价通常就是那个额外付出的空间,比如每次往返于火场和水源时需要负担的水桶重量,或者 fread 在 FILE * 之中开辟的那块内存缓冲区。

显然,在同等的时间范围内,相对于磁盘 I/O 的性能提升而言,内存条容积能力的提升要更为明显。这就好比与其等待着大陆板块漂移来缩短那一千米的距离,不如练好肌肉来负担更大容积的水桶。于是,在对各种可能的代价进行了综合考量和评估之后,我们认为用空间换取效率会是更为现实、更为合理的解决方案。

最后来回答之前有关改换 SSD 的问题。当然,如果你这个程序是一个面向人傻钱多可速来的大头客户的私有项目的话,那你用 SSD 我是完全没有意见的。但是,如果你的程序是一个通用软件呢?以微软之能,尚要在淘汰 Windows XP 的问题上瞻前顾后达五年之久,你呢?你真的有魄力在软件启动后弹个下面这样的 MessageBox 吗少年?

①自 2009.4 微软宣布取消 Windows XP 主流技术支持起计算。

订阅本站

一条评论

  • At 2014.11.22 11:21, 刀刀 said:

    前辈, 不更新么吗?

    (Required)
    (Required, will not be published)