知识的吸收

你知道的是否足够完整

每个学习C语言的同学都会被告知 浮点数 类型的精度是有限制的,大家也习以为常。
但以下这个代码的输出结果会让你感到意外吗? 

#include <stdio.h>
int main()
{
 if (0.1+0.2+0.3 != 0.3+0.2+0.1) {
      printf("!=\n");
 } else {
      printf("=\n");
 }
}

结果是 在C语言(以及绝大部分语言中)中,浮点数是不遵循加法交换律的。

“浮点数精度有限制”对我们大多数人来说是非常自然的知识点,“不满足加法交换律”这个却着实让人感觉到一些意外。
但看到结果后,再去思考发现也是很正常的,意外的其实是对“加法交换律”的理解不够。以前一直忽略了“交换律”是作用在某一个集合内的。这个集合换成了精度有限的浮点数后就不再成立了。

不应想当然的解释某个现象

“RAM的 读取操作 与 写操作 谁更快”,大部分人可能都会稍微犹豫一下,然后选择“读取操作”会更快。
“机械磁盘的 读取操作 与 写操作 谁更快”, 绝大部分人都会毫不犹豫的选择“读取操作”更快。

/bin/cp在拷贝一个大文件的时候(假设机械磁盘),大家认为read(2)和write(2)的耗时分别占比多少?
“机械磁盘的读取操作确实比写操作快很多”,这个信息大家理解的没有错。但实际情况是复杂的,依旧有很多你潜意识里的信息是错误的。 以前我会认为write(2)至少占50%以上的时间。但实际测试后会发现,write(2)连10%耗时都不到。整个cp的过程,耗时20%在read(2)上, 50%在open(2)上。

为什么会这样?因为write(2)并没有发生在磁盘上,而是直接写到RAM了。在磁盘负载低的时候才会被kernel调度执行实际的写磁盘操作。

我们基于正确的信息,却得到了截然相反的结果。因为我们忽略了无处不在的cache. 
基于这个现象我们在优化启动速度的时候可以集中精力减少read(2)操作的消耗,write(2)操作只要避免fsync(2)之类的即可。

  syscall      calls  total    min    avg    max   stddev
                (msec)  (msec)  (msec)  (msec)    (%)
  --------------- -------- --------- --------- --------- ---------   ------
  write       21027 3360.727   0.025   0.160  20.060   5.34%
  open       14029 1906.604   0.002   0.136  931.308   51.87%
  read        28045  459.400   0.001   0.016  94.165   20.51%
  lstat        7012  25.513   0.002   0.004   0.107   0.59%
  fstat       14029  23.304   0.001   0.002   0.131   0.89%
  close       14032  21.787   0.001   0.002   0.046   0.40%

不应小瞧你已经知道的知识

在dde-file-manager某个版本之前,拷贝大量文件的时候速度会比nautilus慢非常多。

通过strace跟踪后发现dde-file-manager在执行拷贝的时候会执行fsync(2), 造成的结果就是每一个小文件的拷贝kernel都必须等待磁盘操作完毕。因此当文件数量较多时,拷贝速度的差距就非常大了(7000个300Kb左右的文件,速度相差20多倍)。 即使不调用fsync(2),只要不是拷贝完毕后立刻断电或立刻强行拔出U盘,都不会导致数据错误的。

考虑之后去掉fsync(2)的调用,拷贝速度和nautilus在同一水平之上了。但手贱又去对比了一下/bin/cp的速度,发现不论怎么保证环境一致,依旧是稳定的慢/bin/cp大概3倍之多。
然后就是误入歧途的跟踪两者的调用路径,测试对比fallocate(2), copy_file_range(2), splice(2)等等各种方式。最后通过删减/bin/cp的代码逻辑,定位到了根本原因。

/bin/cp之所以比dde-file-manager以及nautilus快3倍的原因其实很简单,基于的原理也是大家都习以为常的了------“机械磁盘的顺序读取比乱序读取要快”。
这次我们手上有正确的知识,却完全忽略它了。简单的把文件按照磁盘存放顺序进行排序后再进行拷贝操作速度就立刻提升了3倍。

迭代的成长

知识积累是迭代的,我们总能重新认识已经知道的。事实与认知发生冲突时就是知识的一次迭代。
软件世界特别是开源社区的很多组件都在快速更新,而我们的观念还停留在第一次认知它的时候。

2 条思考于 “知识的吸收

发表评论

电子邮件地址不会被公开。 必填项已用*标注