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 缓存行和预取策略 的相关文章

  • 编译时运算符

    有人可以列出 C 中可用的所有编译时运算符吗 C 中有两个运算符 无论操作数如何 它们的结果始终可以在编译时确定 它们是sizeof 1 and 2 当然 其他运算符的许多特殊用途可以在编译时解决 例如标准中列出的那些整数常量表达式 1 与
  • 为什么 C# Array.BinarySearch 这么快?

    我已经实施了一个很简单用于在整数数组中查找整数的 C 中的 binarySearch 实现 二分查找 static int binarySearch int arr int i int low 0 high arr Length 1 mid
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • 为什么当实例化新的游戏对象时,它没有向它们添加标签? [复制]

    这个问题在这里已经有答案了 using System Collections using System Collections Generic using UnityEngine public class Test MonoBehaviou
  • 关于 C++ 转换:参数 1 从“[some_class]”到“[some_class]&”没有已知的转换

    我正在研究 C 并且遇到了一个错误 我不知道确切的原因 我已经找到了解决方案 但仍然想知道原因 class Base public void something Base b int main Base b b something Base
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 垃圾收集器是否在单独的进程中运行?

    垃圾收集器是否在单独的进程中启动 例如 如果我们尝试测量某段代码所花费的进程时间 并且在此期间垃圾收集器开始收集 它会在新进程上启动还是在同一进程中启动 它的工作原理如下吗 Code Process 1 gt Garbage Collect
  • Windows 窗体:如果文本太长,请添加新行到标签

    我正在使用 C 有时 从网络服务返回的文本 我在标签中显示 太长 并且会在表单边缘被截断 如果标签不适合表单 是否有一种简单的方法可以在标签中添加换行符 Thanks 如果您将标签设置为autosize 它会随着您输入的任何文本自动增长 为
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 混合 ExecutionContext.SuppressFlow 和任务时 AsyncLocal.Value 出现意外值

    在应用程序中 由于 AsyncLocal 的错误 意外值 我遇到了奇怪的行为 尽管我抑制了执行上下文的流程 但 AsyncLocal Value 属性有时不会在新生成的任务的执行范围内重置 下面我创建了一个最小的可重现示例来演示该问题 pr
  • C# - OutOfMemoryException 在 JSON 文件上保存列表

    我正在尝试保存压力图的流数据 基本上我有一个压力矩阵定义为 double pressureMatrix new double e Data GetLength 0 e Data GetLength 1 基本上 我得到了其中之一pressur
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐