diff --git a/ch7.md b/ch7.md index ed433cfc451e3675e9e8f2af4e6be8c13b480d38..76cb6d2eb4cb7ed55e2fd5f7d5c8b36209c674dd 100644 --- a/ch7.md +++ b/ch7.md @@ -20,11 +20,11 @@ + 指令寄存器(IR),它含有当前执行的指令的机器码。 + 栈指针(SP),它含有当前函数栈帧的指针,其中包含函数参数和局部变量。 + 程序当前使用的存放数据的通用寄存器。 -+ 状态寄存器,或者位寄存器,含有当前计算的信息。例如,位寄存器通常含有移位来存储上个操作是否是零的结果。 ++ 状态寄存器,或者位寄存器,含有当前计算的信息。例如,位寄存器通常含有一位来存储上个操作是否是零的结果。 在程序运行之中,CPU执行下列步骤,叫做“指令周期”: -+ 取指(Fetch):从内存中抓取下一条指令,储存在指令寄存器中。 ++ 取指(Fetch):从内存中获取下一条指令,储存在指令寄存器中。 + 译码(Decode):CPU的一部分叫做“控制单元”,将指令译码,并向CPU的其它部分发送信号。 + 执行(Execute):收到来自控制单元的信号后会执行合适的计算。 @@ -73,13 +73,13 @@ Th + m * Tp 另一方面,如果程序不可预测地跳来跳去,从内存中零散的位置读取数据,很少两次访问到相同的位置,缓存的性能就会很低。 -程序使用相同数据多于一次的倾向叫做“时间局部性”。使用相邻位置的数据的倾向叫做“空间局部性”。辛运的是,许多程序天生就带有这两种局部性: +程序使用相同数据多于一次的倾向叫做“时间局部性”。使用相邻位置的数据的倾向叫做“空间局部性”。幸运的是,许多程序天生就带有这两种局部性: -+ 许多程序含有非跳转或分支的代码块。由于这些代码块,指令顺序指令,访问模式具有空间局部性。 ++ 许多程序含有非跳转或分支的代码块。在这些代码块中指令顺序执行,访问模式具有空间局部性。 + 在循环中,程序执行多次相同指令,所以访问模式具有时间局部性。 + 一条指令的结果通常用于下一指令的操作数,所以数据访问模式具有时间局部性。 + 当程序执行某个函数时,它的参数和局部变量在栈上储存在一起。这些值的访问具有空间局部性。 -+ 最普遍的处理模型之一就是顺序读写数据元素。这一模式也具有空间局部性。 ++ 最普遍的处理模型之一就是顺序读写数组元素。这一模式也具有空间局部性。 下一节中我们会探索程序的访问模式和缓存性能的关系。 @@ -159,12 +159,12 @@ Size: 4096 Stride: 64 read+write: 0.7058 ns 花一分钟来考虑这张图片,并且看看你是否能推断出缓存信息。下面是一些需要思考的事情: + 程序多次遍历并读取数组,所以有大量的时间局部性。如果整个数组能放进缓存,平均缺失惩罚应几乎为0。 -+ 当步长是4的时候,我们读取了数组的每个元素,所以程序有大量的空间局部性。如果块大小足以包含64个元素,例如,即使数组不能完全放在缓存中,命中率应为63/64。 ++ 当步长是4的时候,我们读取了数组的每个元素,所以程序有大量的空间局部性。例如,如果块大小足以包含64个元素,即使数组不能完全放在缓存中,命中率应为63/64。 + 如果步长等于块的大小(或更大),空间局部性应为0,因为每次我们读取一个块的时候,我们只访问一个元素。这种情况下,我们会看到最大的缺失惩罚。 总之,如果数组比缓存大小更小,或步长小于块的大小,我们认为会有良好的缓存性能。如果数组大于缓存大小,并且步长较大时,性能只会下降。 -在图7.1中,缓存性能对于所有步长很好,只要数组小于`2 ** 22`字节。我们可以推测缓存大小近似4MiB。实际上,根据规范应该是3MiB。 +在图7.1中,只要数组小于`2 ** 22`字节,缓存性能对于所有步长都很好。我们可以推测缓存大小近似4MiB。实际上,根据规范应该是3MiB。 当步长为8、16或32B时,缓存性能良好。在64B时开始下降,对于更大的步长,平均缺失惩罚约为9ns。我们可以推断出块大小为128B。 @@ -188,11 +188,11 @@ Size: 4096 Stride: 64 read+write: 0.7058 ns 在这一章的几个位置上,你可能会有一个问题:“如果缓存比主存快得多,那为什么不使用一大块缓存,然后把主存扔掉呢?” -在没有深入计算机体系结构之前,可以给出两个原因:电子和经济学上的。缓存很快是由于它们很快,并且离CPU很近,这可以减少由于电容造成的延迟和信号传播。如果你把缓存做得很大,它就变得很慢。 +在没有深入计算机体系结构之前,可以给出两个原因:电子和经济学上的。缓存很快是由于它们很小,并且离CPU很近,这可以减少由于电容造成的延迟和信号传播。如果你把缓存做得很大,它就变得很慢。 -另外,缓存占据处理器芯片的空间,更大的处理器会更贵。主存通常使用动态随机访问内存(DRAM),每位上只有一个晶体管和一个电容,所以它可以将更多内存打包在同一空间上。但是这种实现内存的方要比缓存实现的方式更慢。 +另外,缓存占据处理器芯片的空间,更大的处理器会更贵。主存通常使用动态随机访问内存(DRAM),每位上只有一个晶体管和一个电容,所以它可以将更多内存打包在同一空间上。但是这种实现内存的方法要比缓存实现的方式更慢。 -同时主存通常包装在双列直插式内存模块(DIMM)中,它包含至少16个芯片。几个小型芯片比一个大型芯片更便宜。 +同时主存通常包装在双列直插式内存模块(DIMM)中,它至少包含16个芯片。几个小型芯片比一个大型芯片更便宜。 速度、大小和成本之间的权衡是缓存的根本原因。如果有既快又大还便宜的内存技术,我们就不需要其它东西了。 @@ -229,7 +229,7 @@ Size: 4096 Stride: 64 read+write: 0.7058 ns 这些问题的答案构成了“缓存策略”。在靠近顶端的位置,缓存策略倾向于更简单,因为它们非常快,并由硬件实现。在靠近底端的位置,会有更多做决定的次数,并且设计良好的策略会有很大不同。 -多数缓存策略基于历史重演的原则,如果我们有最近时期的信息,我们可以用它来预测不久的将来。例如,如果一块数据在最近使用了,我们认为它不久之后会再次使用。这个原则展示了一种叫做“最近最少使用”,即LRU。它从缓存中移除最久未使用的数据块。更多话题请见[缓存算法的维基百科](http://en.wikipedia.org/wiki/Cache_algorithms)。 +多数缓存策略基于历史重演的原则,如果我们有最近时期的信息,我们可以用它来预测不久的将来。例如,如果一块数据在最近使用了,我们认为它不久之后会再次使用。这个原则展示了一种叫做“最近最少使用”的策略,即LRU。它从缓存中移除最久未使用的数据块。更多话题请见[缓存算法的维基百科](http://en.wikipedia.org/wiki/Cache_algorithms)。 ## 7.8 页面调度 @@ -250,7 +250,7 @@ Size: 4096 Stride: 64 read+write: 0.7058 ns + 大多数进程不会用完所分配的内存。`text`段的许多部分都永远不会执行,或者执行一次就再也不用了。这些页面可以被换出而不会引发任何问题。 + 如果程序泄露了内存,它可能会丢掉所分配的空间,并且永远不会使用它了。通过将这些页面换出,操作系统可以有效填补泄露。 -+ 在多数系统中,有些进程像守护进程那样,多数时间下都是限制的,只在特定场合被“唤醒”来响应时间。当它们闲置时,这些进程可以被换出。 ++ 在多数系统中,有些进程像守护进程那样,多数时间下都是闲置的,只在特定场合被“唤醒”来响应事件。当它们闲置时,这些进程可以被换出。 + 另外,可能有许多进程运行同一个程序。这些进程可以共享相同的`text`段,避免在物理内存中保留多个副本。 如果你增加分配给所有进程的总内存,它可以超出物理内存的大小,并且系统仍旧运行良好。