第一章 明白高性能步伐 (9) 保持缓存的热度

源码 2024-10-2 20:10:17 39 0 来自 中国
保持缓存的热度


       起首,别浪费缓存,由于主存很慢。这意味着无序地从内存中读取数据(被称为指针追逐pointer-chasing)并非明智。在当代处置处罚器上,步伐以猜测的方式读取一连的内存块,可以受益于硬件级的预读取(prefetching)机制。一言以蔽之,即数据局部化(data locality)。
       举个反面例子,唉,是我们古老而可靠的链表,遍历链表是一次实着实在的指针追逐盛宴,由于全部的节点都是动态分配的,可以位于内存中的任何地方。然而我们可以用前述的优化本领举行调停:先举行预分配再使用定制化的内存分配器。云云一来,链表的全部节点将位于预分配的缓冲区附近,这将使链表重新变为缓存友爱型。固然,我们可以一开始就使用数组,但这个例子分析的是怎样在雷同的环境下实现数据的局部化。
        第二个常见的错误是将两个常用的整数x, y在数据布局中分隔得很远,那么当我们要同时使用这两个整数时,就不得不加载两个缓存行而不是一个。须要在一起使用的数据应当在数据布局上确保相近;不要让缓存行的界限冲破你的数据布局。
        更深入的广为人知的优化是包罗数组的布局体代替元素是布局体的数组。这有助于SIMD指令加载数据或应用其他并行化读数据的技能。
        另一个常常被提及的优化本领是通过调解数据访问模式来进步矩阵乘法的性能。我们可以用k, j, i三层循环替代简朴的三元组i, j, k:
for ( size_t k = 0; k < P; ++k)
       for (size_t j = 0; j < M; ++j)
              for (size_t i = 0; i < N; ++i)
                       res[j] += m1[k] * m2[k][j];
         这一调解将遍历的顺序变得对缓存友爱,即对两个矩阵的元素,以一连的方式读取能极大地提升运算地性能(在一些测试中,有高达94%的性能提升)!
        从上面的例子我们可以看到数据的构造方式和布局,乃至于数据的访问模式,在当代CPU环境里,都对步伐的性能有巨大的影响!
        第二种缓存即指令缓存,也须要心存痛惜。与在数据区一样,在代码段中前后跳跃,是同样忌讳的。因此,代码的局部化是接下来的一个重要议题。一个大概的做法是将正常环境的处置处罚代码放在错误处置处罚的代码之前,比如:
       if (ok) {
               do_work();
       } else {
              printf(“ERROR”);
              return;
       }   
        如许做避免了在正常环境下的指令跳转,从而进步了代码的局部性。我们将在第3章深入C++及其性能讨论步伐优化中的编译器的作用时,讨论更多的细节。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 16:51, Processed in 0.118332 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表