第一章 介绍
首先接受那些事实,之后你就能去扭曲他们。
Mark Twain
让我们讨论一下输入输出(I/O)吧!不,不,算了吧,我们不想谈那么无聊的东西!输入输出(I/O)虽然不是一个有趣的东西, 但确实是一个非常重要的东西。很多程序员觉得处理I/O跟处理下水道一样:毫无疑问它非常重要,生活中都不能缺少它,但是人们却极不情愿直接去面对它,尤其是当它不能正常工作的时候,你将到处闻到它们散发出的恶臭。当然这本书不是一本谈论下水道的书,在下面的章节里,你将会学到怎样让你的数据流更加流畅。
面向对象设计所作的都是封装。封装的确是一个好东西:它可以划分职责,隐藏具体实现,提供代码可重用性。这种职责划分和封装非常有利于程序员和程序。你可能是一个经验丰富的Java程序员,就算你一点都不知道Java平台中IO的基本概念,你仍然可以写出非常复杂的对象和非常不一般的程序。这一章中,我们将暂时违反你的封装原则,看一看底层的IO实现,希望它能够让你更好的协调你IO操作中不同部分的衔接。
1.1 IO vs CPU
绝大多数程序员把自己想象成软件艺术家,在这里弄一些聪明的代码来减少一些字节的消耗,在那里展开一个循环,在另外一些地方重构一下增强对象功能。尽管这些东西非常的重要,但是有趣的是这些优化很容易被低效的IO掩盖的一文不值。操作IO数据花费的时间跟操作内存数据相比完全不在一个数量级上。很多的码工们往往只注意到操作数据的对象,而忽略了那些包含获取、排序数据的环境因素。
表1-1列举了处理单位数据量的任务在读取和写入硬盘所消耗的时间(这个时间是假设的,但符合实际意义)。第一列表示CPU处理一单位数据所消耗的平均时间,第二列表示将那个数据单元进行读写的时间,第三列表示单位时间处理的数据吞吐量,最后一列表示吞吐量的改变的百分比,它是由前面两列计算得来。
表1-1 吞吐率,CPU时间vs IO时间 | |||
CPU时间(ms) | IO时间(ms) | 吞吐量(单元/s) | 增量(%) |
5 | 100 | 9.25 | 0(基准) |
2.5 | 100 | 9.76 | 2.44 |
1 | 100 | 9.9 | 3.96 |
5 | 90 | 10.53 | 10.53 |
5 | 75 | 12.5 | 31.25 |
5 | 50 | 18.18 | 90.91 |
5 | 20 | 40 | 320 |
5 | 10 | 66.67 | 600 |
前面三列可以看出CPU时间对吞吐量的影响。减小一倍的时间对吞吐量的提升仅仅只有2.2%。然而仅仅降低10%的IO延迟就可以提升9.7%的吞吐量。当然当你看到花费在IO上的处理时间是在CPU上的二十倍或者更多时,你就不会对降低一半的IO延迟就会得到将近一倍的吞吐量感到惊奇。
上面的数据是为了举例说明两者的相对时间数量级而估计的,真实世界绝不会如此简单。从上面的表中可以看出IO经常是影响应用程序吞吐量的限制性因素,而不是处理器时间。程序员喜欢去优化他们的代码,但是往往把IO调整放到最后,甚至是直接忽略他们。这是一件非常遗憾的事,因为即使是对IO做非常少量的优化也会对程序性能有本质的提升。