cpu 缓存行和预取策略

2024-02-28

我读了这篇文章http://igoro.com/archive/gallery-of-processor-cache-effects/ http://igoro.com/archive/gallery-of-processor-cache-effects/。文章中说因为cacheline延迟,代码:

int[] arr = new int[64 * 1024 * 1024];

// Loop 1
for (int i = 0; i < arr.Length; i++) arr[i] *= 3;

// Loop 2
for (int i = 0; i < arr.Length; i += 16) arr[i] *= 3;

几乎有相同的执行时间,我编写了一些示例 c 代码来测试它。我在采用 Ubuntu 64 位、ARMv6 兼容处理器 rev 7 和 Debian 的 Xeon(R) E3-1230 V2 上运行代码,也在 Core 2 T6600 上运行。所有的结果都不是文章说的那样。

我的代码如下:

long int jobTime(struct timespec start, struct timespec stop) {
    long int seconds = stop.tv_sec - start.tv_sec;
    long int nsec = stop.tv_nsec - start.tv_nsec;
    return seconds * 1000 * 1000 * 1000 + nsec;
}

int main() {
    struct timespec start;
    struct timespec stop;
    int i = 0;
    struct sched_param param;
    int * arr = malloc(LENGTH * 4);

    printf("---------sieofint %d\n", sizeof(int));
    param.sched_priority = 0;
    sched_setscheduler(0, SCHED_FIFO, &param);
    //clock_gettime(CLOCK_MONOTONIC, &start);
    //for (i = 0; i < LENGTH; i++) arr[i] *= 5;
    //clock_gettime(CLOCK_MONOTONIC, &stop);

    //printf("step %d : time %ld\n", 1, jobTime(start, stop));

    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < LENGTH; i += 2) arr[i] *= 5;
    clock_gettime(CLOCK_MONOTONIC, &stop);

    printf("step %d : time %ld\n", 2, jobTime(start, stop));
}

每次我选择一个片段来编译和运行(注释一个并取消注释另一个)。 编译:

gcc -O0 -o cache cache.c -lrt

在至强上我得到这个:

step 1 : 258791478
step 2 : 97875746

我想知道这篇文章说的是否正确?或者,最新的 cpu 是否具有更高级的预取策略?


简短回答(TL;DR):如果您正在访问未初始化的数据,则第一个循环必须在定时循环内为整个数组分配新的物理页。


当我运行您的代码并依次注释每个部分时,我得到两个循环几乎相同的时间。但是,当我取消注释这两个部分并逐一运行它们时,我确实得到了与您报告的相同的结果。这让我怀疑你也这么做了,并且遭受了痛苦冷启动将第一个循环与第二个循环进行比较时的效果。检查起来很容易——只需更换循环的顺序,看看第一个循环是否仍然较慢。

为了避免,要么选择足够大的LENGTH(取决于您的系统),这样您就不会从第一个循环帮助第二个循环中获得任何缓存优势,或者只是添加一次不定时的整个数组的遍历。

请注意,第二个选项并不能完全证明博客想要说的内容 - 内存延迟掩盖了执行延迟,因此无论您使用缓存行的多少个元素,您仍然受到内存访问的瓶颈时间(或更准确地说是带宽)

另外 - 对代码进行基准测试-O0这是一个非常糟糕的做法


Edit:

这就是我得到的(删除了日程安排,因为它不相关)。
这段代码:

for (i = 0; i < LENGTH; i++) arr[i] = 1;   // warmup!

clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < LENGTH; i++) arr[i] *= 5;
clock_gettime(CLOCK_MONOTONIC, &stop);
printf("step %d : time %ld\n", 1, jobTime(start, stop));

clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < LENGTH; i+=16) arr[i] *= 5;
clock_gettime(CLOCK_MONOTONIC, &stop);

Gives :

---------sieofint 4
step 1 : time 58862552
step 16 : time 50215446

在评论时,预热线具有与您在第二个循环中报告的相同的优势:

---------sieofint 4
step 1 : time 279772411
step 16 : time 50615420

替换循环的顺序(热身仍然被注释)表明它确实与步长无关,而是与顺序相关:

---------sieofint 4
step 16 : time 250033980
step 1 : time 59168310

(gcc 版本 4.6.3,在 Opteron 6272 上)

现在请注意这里发生的情况 - 理论上,只有当数组小到足以位于某个缓存中时,您才会期望预热才有意义 - 在这种情况下LENGTH即使对于大多数机器上的 L3 来说,您使用的也太大了。然而,您忘记了页面地图 - 您不仅仅是跳过了数据本身的预热 - 您避免了初始化它首先。这永远不会在现实生活中给你带来有意义的结果,但由于这是一个基准,你没有注意到这一点,你只是将垃圾数据乘以它的延迟。

这意味着您在第一个循环中访问的每个新页面不仅会进入内存,它可能会出现页面错误,并且必须调用操作系统为其映射新的物理页面。这是一个漫长的过程,乘以您使用的 4K 页面数量 - 累积到很长一段时间。在这个阵列大小下,您甚至无法从 TLB 中受益(您有 16k 个不同的物理 4k 页,远远超过大多数 TLB 即使有 2 个级别也能支持的数量),因此这只是故障流的问题。这可能可以通过任何分析工具来测量。

同一数组上的第二次迭代不会产生这种效果,并且速度会快得多 - 尽管仍然必须在每个新页面上执行完整的页面遍历(纯粹在硬件中完成),然后从内存中获取数据。

顺便说一句,这也是当您对某些行为进行基准测试时,您多次重复同一件事的原因(在这种情况下,如果您以相同的步幅多次运行数组,并忽略第一个,那么它就会解决您的问题)几轮)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cpu 缓存行和预取策略 的相关文章

  • 如何使用带有进度条的 HttpClient 下载文件?

    我创建了一个名为SiteDownload并添加了一些下载图像的链接 using System Collections Generic using System Linq using System Net using System Threa
  • 井字游戏代码有助于改进

    这是我必须检查玩家在井字棋游戏中获胜的代码 这是一个很长的 if 语句 可以改进 该板由 9 个图片框组成 我是一名 C 初学者 pBox Image Player players Player playerTurn getImage ch
  • C++,多语言/本地化支持

    向 C 程序添加多语言支持的最佳方法是什么 如果可能 应该从包含键值对 WelcomeMessage Hello s 之类的纯文本文件中读取语言 我想到了添加一个 localizedString key 函数来返回加载的语言文件的字符串 有
  • OpenGL,如何独立旋转对象?

    到目前为止我的代码 void display void glClear GL COLOR BUFFER BIT GL DEPTH BUFFER BIT Clear Screen And Depth Buffer glLoadIdentity
  • lambda 表达式到函数指针的转换

    这是这个问题的后续问题 Lambda 如何作为参数传递 https stackoverflow com questions 3321283 c0x lambda how can i pass as a parameter 据推测 MSDN
  • stl 集的 C# 等效项是什么?

    我想使用 C 将一些值存储在平衡二叉搜索树中 我查看了泛型命名空间中的集合 但没有找到与 stl 集合等效的集合 我可以使用什么通用集合 我不想存储键 值对 只是值 你可以使用HashSet http msdn microsoft com
  • 如何通过C#在SQLite数据库中写入变量DateTime值?

    我很新C and SQLite数据库并有一些变量存储在 SQLite 数据库中TimeStamp 这是我的代码 DateTime now DateTime Now m dbConnection new SQLiteConnection Da
  • 如何使用 C# 调用 REST API?

    这是我到目前为止的代码 public class Class1 private const string URL https sub domain com objects json api key 123 private const str
  • const_iterators 更快吗?

    我们的编码指南更喜欢const iterator 因为它们比正常的要快一点iterator 当您使用时 编译器似乎会优化代码const iterator 这真的正确吗 如果是的话 内部到底发生了什么使得const iterator快点 编辑
  • dlopen 或 dlclose 未调用信号处理程序

    我在随机时间内收到分段错误 我注册了信号 但发生分段错误时未调用信号处理程序 include
  • 二元运算符重载、隐式类型转换

    class my bool private bool value public my bool bool value value value explicit operator bool return value friend my boo
  • C++ fill() 与 uninitialized_fill()

    您好 我是初学者 我想知道容器的 fill 和 uninitialized fill 之间的区别 我在谷歌上进行了快速搜索 但没有得到很好的答案 有人可以帮助我吗 fill 将值 使用赋值运算符 分配给已构造的对象 uninitialize
  • 尝试缓冲区溢出

    我正在尝试使用缓冲区溢出来更改函数的结果 以使用以下代码更改堆栈上的结果 include
  • 如何修改 edmx 的默认代码生成策略?

    我想修改默认的代码生成策略 该怎么做 我只是想修改类名 lt code Escape container gt to Entities并将默认连接字符串更改为name Default 我不想为该项目创建模板文件 我想编辑它以便它可以在全球范
  • C++头文件问题

    我在处理类时尝试了一些 C 代码 这个问题出现在我身上 并且让我有点烦恼 我创建了一个包含类定义的头文件和一个包含实现的 cpp 文件 如果我在不同的 cpp 文件中使用此类 为什么要包含头文件而不是包含类实现的 cpp 文件 如果我包含类
  • 使用可变参数模板函数计算多个值的平均值

    我正在尝试编写一个函数来确定任意数量参数的平均值 所有参数都具有相同的类型 出于学习目的 我尝试使用可变参数模板函数来做到这一点 这是我到目前为止所拥有的 template
  • 如何将这个基于代码的 WPF 工具提示转换为 Silverlight?

    以下工具提示代码适用于WPF 我正在努力让它发挥作用银光 但它给了我这些errors TextBlock does not contain a definition for ToolTip Cursors does not contain
  • 当前线程中的单例

    我的单身人士如下 public class CurrentSingleton private static CurrentSingleton uniqueInstance null private static object syncRoo
  • 使用 Powershell 或 C# 获取 Azure“文件和文件夹”作业状态

    我一直在尝试找到一种方法来获取在 AzureRM 中运行的几个客户上运行的 文件和文件夹 备份作业的状态 可以在 AzureRm 门户中手动找到状态 恢复服务保管库 gt 作业 gt 备份作业 使用powershell不显示任何作业信息 G
  • InvalidOperationException:没有为方案“CookieSettings”注册身份验证处理程序

    我正在使用 ASP Net MVC core 2 1 开发一个应用程序 其中不断出现以下异常 InvalidOperationException 没有为方案 CookieSettings 注册身份验证处理程序 注册的方案有 Identity

随机推荐