Jos*_*vin 1 architecture performance multithreading caching prefetch
假设我有一个大数组,并且有多个线程从数组中读取.每个线程通过跳过一个恒定量迭代数组,但从不同的偏移量开始.因此,线程1可以从元素0开始,然后读取元素32,64,96等.但是线程2从元素1开始,并且读取元素33,65,97等(记住'元素'可以构成不止一个字节或单词)我知道通常空间局部性对于获得最佳缓存性能是可取的,但我还读到现代CPU具有寻找访问模式的硬件预取器,并且对我的一个步幅似乎是一个明显的模式.
缓存性能非常复杂,真正可靠的答案将来自硬件设计人员或专门负责调度调度的操作系统开发人员.我曾经在大型IBM系统上使用性能分析工具,因此我可以给出一个部分的,略微过时的答案:
首先,高速缓冲存储器按地址关联.如果寻址一块内存,则该地址的"缓存行"将加载到缓存中.根据处理器设计的不同,长度可以是4,8,16或32个字节.(也许更多.)这很可能是基于硬件地址的"alilgnment"; 换句话说,一个32字节的行将位于与32位可整除地址对齐的边界上.您的内存引用可能位于该缓存行的开头,中间或末尾.
一旦它进入缓存,该地址将用作"查找"以查找缓存的数据.
如果高速缓存行足够大以致引用的"相邻"项恰好已作为高速缓存行的一部分进行高速缓存,则引用的位置将帮助您.跳过你的阵列会打败这个.
缓存设计根据供应商,产品线,处理器价格以及更多内容而有很大差异.完美的缓存优化将是非常难以捉摸的,除非(1)您对将要运行的机器有很多了解,并且(2)您真的对在任何其他机器上运行不感兴趣.
另一个要考虑的因素是32位地址的大小是64位地址的一半,这对可缓存的数据量有很大影响.为地址提供更多位意味着更少的数据位,或多或少.
预取更多的是巫术而不是科学.从数据到缓存提取内存是昂贵的,即使它与处理器执行异步(尽管它不能与执行分开太远).引用的位置是一个很好的规则,尽管它将基于硬件架构,其方式不一定与微观规模上的代码执行相匹配.LRU(最近最少使用)是决定从缓存中启动什么的常用方法,但是从缓存中删除某些内容以便为最终未被使用的内容腾出空间并不是一个很好的优化.因此,至少可以说,预取将是明智的.
编辑:虚拟内存问题,任务切换等
虚拟内存肯定会使事情变得更加有趣,特别是在支持多个地址空间的操作系统中.缓存最有可能基于实际地址,而不是虚拟地址,因此页面交换等内容可能会对缓存产生有趣的副作用.通常,将要交换或释放的页面将首先失效,并移动到"刷新列表"(可以将其写入交换文件)或"空闲列表".根据实现,这些页面仍然可以由应用程序回收,但它们不再可寻址 - 这意味着在回收它们的过程中会发生页面错误.因此,一旦页面移出应用程序的工作集,很可能与其关联的任何缓存行都将失效.如果页面没有被大量使用,那么它也不太可能有很多缓存,但是在交换频繁的情况下,缓存性能会随着交换性能而受到影响.
此外,一些缓存设计具有"共享"缓存,并且大多数或全部具有处理器和核心特定缓存.如果将缓存指定给特定处理器或核心,并且该核心更改任务,则可能会刷新整个缓存以避免新进程损坏.这不包括线程切换,因为线程在相同的进程和相同的地址空间中运行.这里真正的问题是系统上其他应用程序的高活动可能会影响您的缓存性能.共享缓存在某种程度上缓解了这个问题,但必须更加谨慎地管理以避免损坏.