为什么__builtin_prefetch在这里没有任何效果
Why doesn’t __builtin_prefetch have any effect here?
我正在编写一个解析文件的程序。它由一个主循环组成,该循环逐字符解析并处理它们。这是主循环:
char c;
char * ptr;
for( size_t i = 0; i < size ; ++i )
{
ptr = ( static_cast<char*>(sentenceMap) + i );
c = *ptr;
__builtin_prefetch( ptr + i + 1 );
// some treatment on ptr and c
}
正如您所看到的,我添加了一条builtin_prefetch
指令,希望在循环的下一次迭代中放入缓存。我尝试了不同的值:ptr+i+1
、ptr+i+2
、ptr+i+10
,但似乎什么都没有改变。
为了衡量性能,我使用valgrind的工具cachegrind,它可以指示缓存未命中的数量。在线路c = *ptr
上,当未设置__builtin_prefetch
时,cachegrind记录632378 DLmr(L3高速缓存未命中)。然而,奇怪的是,不管我将参数设置为__builtin_prefetch
,这个值都不会改变。
对此有什么解释吗?
这是因为硬件比您领先几年。:)
有一些硬件预取器设计用于识别简单模式并为您进行预取。在这种情况下,您有一个简单的顺序访问模式,这对于硬件预取器来说是微不足道的。
只有当硬件无法预测访问模式时,手动预取才会派上用场。
这里有一个这样的例子:预取示例?
首先,缓存处理的最小单元被称为cache line
,缓存行可以是64字节长,但永远不会像1字节那么小。因此,当你要求预取时,你需要在你当前感兴趣的位置之前问很多。您需要知道缓存行的大小,所以不应该询问位于同一缓存行中的地址。您还需要不要多次调用预取,因为这也可能会收回即将使用的缓存线,并在执行指令时产生性能命中。
现代架构也有硬件预取器的概念,它可以根据您的访问模式提前为您预取数据。这应该是大部分时间创建的数据访问时间与您的简单预取一样好。如今,软件预取只能帮助你,如果你能找到一个如此明显的地方来预取数据,而不是在代码中随机扩展。例如,在开始处理一段数据之前,但如果您只是调用预取并立即访问数据,这对您没有帮助。您需要尽早完成此操作,并在访问数据之前进行其他设置工作。
我建议任何对这个话题感兴趣的人阅读《软件优化食谱》。我通常处理ARM体系结构,但我发现这本书非常宝贵。网上也有一些与这个问题有关的摘录;参见#1和#2。
正确的答案是:预取不能改变缓存未命中的数量,它只是迫使它们更早发生:)
- 努力将整数转换为链表。不知道我在这里做错了什么
- 我可以在这里替换什么,因为我不能在 C# 中使用隐式变量的 lambda 函数?
- 当我从下面的代码中删除关键字 virtual 时,它可以正常工作,否则会出现错误。在这里"virtual"字的意义是什么?
- File.cpp.o:OpenPose 标志 CMakeFiles/.. 的多重定义/main.cpp.o:首先在这里定
- 为什么thread_local变量在这里从未初始化?
- 为什么我必须在这里使用dynamic_cast
- 在这里,当我们比较 if(vc[i]==vc1[i]) 时,它是向量数组. 实际上比较的值是多少,
- 我正在尝试使用 while 循环从字符串中删除字母,直到没有字母。我在这里做错了什么?
- 为什么 C++20 中的 [[可能]] 属性在这里引发警告?
- 我在这里正确传递参数了吗?
- 为什么gmp会在这里与"invalid next size"重新定位一起崩溃?
- 移动语义在这里如何工作?
- 如何在这里循环运行?
- 为什么枚举变量在这里是右值?
- 我的C++合并排序代码不起作用。我在这里错过了什么?
- 试图美化这个Arduino代码[初学者在这里]
- 复制交换习惯用法-我们可以在这里使用动态强制转换操作吗
- 在这里使用删除运算符是否正确,我很困惑
- 类介绍 (c++) 项目希望我们创建两个构造函数,但它们都不需要任何参数 - 我应该在这里做什么?
- 为什么__builtin_prefetch在这里没有任何效果