【问题标题】:Cachegrind output interpretationCachegrind 输出解释
【发布时间】:2011-05-03 06:56:27
【问题描述】:

这是 cachegrind 输出的一部分。这部分代码已经执行了 1224 次。 elmg1 是一个大小为 16 x 20 的 unsigned long 数组。我的机器 L1 缓存大小为 32KB,缓存行大小为 64B,8 路组关联。

  1. for (i = 0; i
  2. {
  3. telm01 = elmg1[i]; 146,880 0 0 73,440 0 0 24,480 0 0
  4. telm31 = (telm01
  5. telm21 = (telm01 > 1); 146,880 1,224 1 48,960 0 0 24,480 0 0
  6. telm11 = (telm01 > 2); 146,880 0 0 48,960 0 0 24,480 0 0
  7. }

A.我把它放在这里的原因是,在 for 循环内的第 3 行,我看到了许多 I1 未命中(还有一个 L2 未命中)。这有点令人困惑,我猜不出原因?

B.我正在尝试优化(时间)一部分代码。以上只是一个小sn-p。我认为在我的程序内存访问中花费了我很多。就像上面的例子一样,elmg1 是一个 16 x 20 大小的无符号长数组。当我尝试在代码中使用它时,总会有一些失误,而在我的程序中,这些变量经常出现。有什么建议吗?

C.我需要分配和(有时初始化)这些无符号长整数。你能建议我更喜欢哪一个,calloc 或数组声明,然后显式初始化。 顺便问一下,缓存处理它们的方式会有什么不同吗?

谢谢。

【问题讨论】:

    标签: c caching optimization kcachegrind cachegrind


    【解决方案1】:

    您是否尝试过展开循环?

    1. 我现在不会担心 L1 未命中。 1224 次中的一次 L2 未命中也是可以的,cpu 必须在某个时候将值加载到缓存中。
    2. 与程序的其余部分相比,此代码的 L2 未命中百分比是多少?
    3. 使用 calloc(),如果数组大小始终相同并且您使用常量作为大小,那么编译器可以优化数组的归零。此外,唯一会影响缓存行使用的是对齐,而不是它的初始化方式。

    edit:很难以这种方式阅读并第一次阅读错误的数字。

    让我们确保我正在阅读第 5 行的正确数字:

    Ir    146,880
    I1mr  1,224
    ILmr  1
    Dr    48,960
    D1mr  0
    DLmr  0
    Dw    24,480
    D1mw  0
    DLmw  0
    

    L1 缓存分为两个 32KByte 缓存,一个用于代码 I1,一个用于数据 D1。 IL & DL 是 L2 或 L3 缓存,由数据和指令共享。

    大量I1mr是指令未命中而不是数据未命中,这意味着循环代码正在从I1指令缓存中弹出。

    第 1 行和第 5 行的 I1 未命中总数为 3672,是 1224 的 3 倍,因此每次运行循环时,您都会获得 3 个 I1 缓存未命中,其中 64Byte 缓存行,这意味着您循环代码大小在 128-192 字节之间以覆盖 3缓存行。所以第 5 行的那些 I1 未命中是因为那是循环代码穿过最后一个缓存行的地方。

    I would recommend using KCachegrind for viewing the results from cachegrind

    编辑:关于缓存行的更多信息。

    那个循环代码看起来不像它本身被调用了 1224 次,这意味着有更多的代码将这个代码从 I1 缓存中推出。

    您的 32Kbyte I1 缓存分为 512 条缓存线(每条 64 字节)。 “8-way set associative”部分意味着每个内存地址仅映射到这 512 个高速缓存行中的 8 个。如果您所配置的整个程序是一个连续的 32 KB 内存块,那么它将全部放入 I1 缓存中,并且不会弹出任何内容。很可能不是这种情况,对于相同的 8 个高速缓存行,将有超过 8 个 64 字节的代码块。假设您的整个程序有 1Mbyte 的代码(包括库),那么每组 8 条缓存线将有大约 32 条(1Mbyte/32Kbyte)的代码满足这 8 条缓存线。

    Read this lwn.net article for all the gory details about CPU caches

    编译器无法始终检测程序的哪些函数将成为热点(多次调用),哪些将成为代码点(即错误处理程序代码,几乎从不运行)。 GCC 具有函数属性hot/cold,它允许您将函数标记为热/冷,这将允许编译器将热函数组合在一块内存中以获得更好的缓存使用(即冷代码不会将热代码推出缓存)。

    无论如何,那些 I1 失误真的不值得花时间担心。

    【讨论】:

    • A.没关系,但是为什么在第 5 行有缓存未命中,而在第 3、4 行则更少。我是否需要自己指定对齐的东西,我读到 malloc 默认提供 8/16 字节对齐。
    • 是的,malloc 应该提供至少 8 字节对齐,但这与 64 字节缓存对齐不同。仅当您拥有一个每个 64 字节的对象数组时,缓存对齐才重要。如果数组未分配缓存对齐,则访问数组中的任何一项都可能导致两次缓存未命中而不是一次。但在这种情况下,缓存对齐不是问题。
    • 感谢您的回复。但是,我不明白这些与 3 个缓存行有什么关系?应该有更多的缓存行数。
    猜你喜欢
    • 1970-01-01
    • 2023-03-18
    • 2012-05-19
    • 2014-02-17
    • 2012-02-17
    • 2021-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多