本节讨论了一些提高应用程序性能的常用技术:选择UO大小、缓存、缓冲区、轮询、并发和并行、非阻塞 JO 和处理器绑定。参考应用程序文档看看这些技术哪些在应用,看看有没有应用程序其他的独有特性。
执行 IO的开销包括初始化缓冲区、系统调用、上下文切换、分配内核元数据、检查进程权限和限制、映射地址到设备、执行内核和驱动代码来执行IO,以及,在最后释放元数据和缓冲区s
“初始化开销”对于小型和大型的IO都是差不多的。从效率上来说,每次IO传输的数据越多,效率越高。
操作系统用缓存提高文件系统的读性能和内存的分配性能,应用程序使用缓存也出于类似的原因。将经常执行的操作的结果保存在本地缓存中以备后用,而非总是执行开销较高的操作、数据库缓冲区高速缓存就是一例,该缓存会保存经常执行的数据库查询结果。
部署应用程序时,一个常见的操作就是决定用什么样的缓存,或能启用什么样的缓存,然后配置适合系统的缓存尺寸。
缓存一个重要的方面就是如何保证完整性,确保查询不会返回过期的数据。这称为缓存致性(cache coherency),而且执行的代价不低理想情况下,不要高于缓存所带来的益处
缓存提高了读操作性能,存储通常用缓冲区来提高写操作的性能
为了提高写操作性能,数据在送人下一层级之前会合并放在缓冲区中。这增加了IO大小提升了操作的效率。取决于写操作的类型,这样做可能会增加写延时,因为第一次写人缓冲区后,在发送之前,还要等待后续的写人
环形缓冲区(或循环缓冲区)是一类用于组件之间连续数据传输的大小固定的缓冲区,缓冲区的操作是异步的。该类型缓冲可以用头指针和尾指针来实现,指针随着数据的增加或移出而改变位置
轮询是系统等待某一事件发生的技术,该技术在循环中检查事件状态,两次检查之间有停顿。轮询有一些潜在的性能问题:
这是性能问题,应用程序应能改变自身行为来监听事件发生,当事件发生时立即通知应用程序并执行相应的例程
poll系统调用
有系统调用po11()来检查文件描述符的状态,提供与轮询相似的功能,不过它是基于事件的,因此没有轮询那样的性能负担
poll()接口支持多个文件描述符作为一个数组,当事件发生要找到相应的文件描述符时需要应用程序扫描这个数组。这个扫描是O(n)(参见5.1.4节中的介绍),扩展时可能会变成个性能间题:在Linux里是epoll(),epoll()避免了这种扫描,复杂度是O(1)。基于Solaris的系统有一个相似的特性叫作事件端口 (eventponts),用port_get(3C)代替了Po11()。