为什么 _mm_stream_ps 会产生 L1/LL 缓存未命中?

2024-01-23

我正在尝试优化计算密集型算法,但遇到了一些缓存问题。我有一个巨大的缓冲区,它偶尔会随机写入,并且在应用程序结束时只读取一次。显然,写入缓冲区会产生大量缓存未命中,并且还会污染随后再次需要进行计算的缓存。我尝试使用非时间移动内在函数,但缓存未命中(由 valgrind 报告并由运行时测量支持)仍然发生。然而,为了进一步研究非时间移动,我编写了一个小测试程序,您可以在下面看到。顺序访问,大缓冲区,仅写入。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <smmintrin.h>

void tim(const char *name, void (*func)()) {
    struct timespec t1, t2;
    clock_gettime(CLOCK_REALTIME, &t1);
    func();
    clock_gettime(CLOCK_REALTIME, &t2);
    printf("%s : %f s.\n", name, (t2.tv_sec - t1.tv_sec) + (float) (t2.tv_nsec - t1.tv_nsec) / 1000000000);
}

const int CACHE_LINE = 64;
const int FACTOR = 1024;
float *arr;
int length;

void func1() {
    for(int i = 0; i < length; i++) {
        arr[i] = 5.0f;
    }
}

void func2() {
    for(int i = 0; i < length; i += 4) {
        arr[i] = 5.0f;
        arr[i+1] = 5.0f;
        arr[i+2] = 5.0f;
        arr[i+3] = 5.0f;
    }
}

void func3() {
    __m128 buf = _mm_setr_ps(5.0f, 5.0f, 5.0f, 5.0f);
    for(int i = 0; i < length; i += 4) {
        _mm_stream_ps(&arr[i], buf);
    }
}

void func4() {
    __m128 buf = _mm_setr_ps(5.0f, 5.0f, 5.0f, 5.0f);
    for(int i = 0; i < length; i += 16) {
        _mm_stream_ps(&arr[i], buf);
        _mm_stream_ps(&arr[4], buf);
        _mm_stream_ps(&arr[8], buf);
        _mm_stream_ps(&arr[12], buf);
    }
}

int main() {
    length = CACHE_LINE * FACTOR * FACTOR;

    arr = malloc(length * sizeof(float));
    tim("func1", func1);
    free(arr);

    arr = malloc(length * sizeof(float));
    tim("func2", func2);
    free(arr);

    arr = malloc(length * sizeof(float));
    tim("func3", func3);
    free(arr);

    arr = malloc(length * sizeof(float));
    tim("func4", func4);
    free(arr);

    return 0;
}

函数 1 是简单的方法,函数 2 使用循环展开。函数 3 使用 movntps,实际上至少在我检查 -O0 时它已插入到程序集中。在函数 4 中,我尝试同时发出多个 movntps 指令来帮助 CPU 进行写组合。我编译了代码gcc -g -lrt -std=gnu99 -OX -msse4.1 test.c where X是 [0..3] 之一。结果是..充其量是有趣的:

-O0
func1 : 0.407794 s.
func2 : 0.320891 s.
func3 : 0.161100 s.
func4 : 0.401755 s.
-O1
func1 : 0.194339 s.
func2 : 0.182536 s.
func3 : 0.101712 s.
func4 : 0.383367 s.
-O2
func1 : 0.108488 s.
func2 : 0.088826 s.
func3 : 0.101377 s.
func4 : 0.384106 s.
-O3
func1 : 0.078406 s.
func2 : 0.084927 s.
func3 : 0.102301 s.
func4 : 0.383366 s.

正如您所看到的,当程序未经过 gcc 优化时,_mm_stream_ps 比其他程序要快一些,但当 gcc 优化打开时,_mm_stream_ps 明显无法达到其目的。 Valgrind 仍然报告大量缓存写入未命中。

因此,问题是:为什么即使我使用 NTA 流指令,那些 (L1+LL) 缓存未命中仍然会发生?为什么特别是 func4 这么慢?!有人可以解释/推测这里发生了什么吗?


  1. 您的基准测试可能主要衡量内存分配性能,而不仅仅是写入性能。您的操作系统可能分配的内存页不在malloc,但在第一次触摸时,在你的内部func*功能。在分配大量内存后,操作系统也可能会进行一些内存洗牌,因此在内存分配后立即执行的任何基准测试可能不可靠。
  2. 你的代码有aliasing http://en.wikipedia.org/wiki/Aliasing_%28computing%29问题:编译器不能保证你的数组的指针在填充这个数组的过程中不会改变,所以它必须总是加载arr来自内存的值而不是使用寄存器。这可能会降低一些性能。避免别名的最简单方法是复制arr and length到局部变量并仅使用局部变量来填充数组。有许多众所周知的建议可以避免使用全局变量。别名是原因之一。
  3. _mm_stream_ps如果数组按 64 字节对齐,效果会更好。在您的代码中,无法保证对齐(实际上,malloc按 16 字节对齐)。这种优化仅对于短数组才显着。
  4. 打电话是个好主意_mm_mfence当你完成之后_mm_stream_ps。这是为了正确性所需要的,而不是为了性能。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 _mm_stream_ps 会产生 L1/LL 缓存未命中? 的相关文章

随机推荐

  • 使用 ffmpeg 循环更改 bash 变量

    我编写了一个脚本 用于根据我在时间戳上录制的视频快速创建简短的预览剪辑 我发现这些视频值得稍后查看以进行剪辑 我的带有时间戳的文件是这样写的 FILE NAME1 MM SS MM SS FILE NAME2 MM SS MM SS MM
  • 如何为 AWS Elastic Beanstalk 部署运行 npm 脚本?

    My package json has scripts start node modules bin coffee server coffee test NODE ENV test node test runner js coverage
  • Android 7.1 写入文本文件

    来自果冻豆的牛轧糖新手尝试将文本文件写入 SD 卡我知道我现在必须请求权限 但找不到任何有效的代码 尝试了以下方法 StringBuilder bodyStr new StringBuilder bodyStr append data1St
  • 用 Java 下载的 PDF 已损坏?

    我读过有关的精彩讨论如何使用 Java 从 Internet 下载并保存文件 https stackoverflow com questions 921262 how to download and save a file from int
  • 有条件地启用 C++ 类中的构造函数 [重复]

    这个问题在这里已经有答案了 我正在学习如何使用std enable if到目前为止 我在课堂上有条件地启用和禁用方法方面取得了一定程度的成功 我根据布尔值对方法进行模板化 此类方法的返回类型是std enable if这样的布尔值 这里的最
  • 如何在 Python 中创建迭代器管道?

    是否有库或推荐的方法在 Python 中创建迭代器管道 例如 gt gt gt all items get created by location surrounding cities 我还希望能够访问迭代器中对象的属性 在上面的例子中 a
  • 每个类元素的简单 jquery .hover() 方法

    没做过太多jquery 遇到了问题 我想为所有具有 social tile 类的 div 绑定悬停事件 我这样做 function var social default social tile css margin right social
  • 在 VS 2012 中调试 javascript - 本地主机缓存有旧代码

    我开始构建一个 PhoneGap 应用程序 并决定使用 VS2012 作为编辑器 调试器 因为 Eclipse 和 XCode 不进行 javascript 调试 或者它们做 也许我错过了一些东西 并且 VS2012 有 js 的智能感知
  • 对 Lisp 引用感到困惑

    我有一个关于 lisp 中列表评估的问题 Why is a and a 1 未评价 defun test a a 1 就像 print 4 这里不评价 if lt 1 2 print 3 print 4 but print 2 3 在这里评
  • C# 类似于 VBA 中的 List

    我想创建一个List
  • Codenameone 中使用 split 方法时出错

    我创建了一个新的 Codenameone 项目 它包含以下代码 String values one two tree String v values split Codename One 支持 Java 5 的一个子集String spli
  • 使用 jQuery 调用 Sinatra 删除路由

    我对 Sinatra 还很陌生 正在制作一个利用基本 CRUD 功能的简单待办事项应用程序 在后端 我有工作路线并测试了所有内容 我想合并一些前端功能 并决定使用 jQuery 来帮助实现这一点 我在 jQuery 中有一段当前代码 当单击
  • 具有自定义对象的可过滤适配器

    我想将自动完成文本框添加到 xamarin android 中的列表视图 自定义对象 中 我有一个列表视图 它是从字符串数组填充的 我想使用自定义对象填充我的列表视图 下面的代码适用于字符串数组 任何帮助实现我的自定义对象适配器都会有所帮助
  • 如何更改两层的 ggplot 图例标签和名称?

    我正在使用 ggmap 和 ggplot 包绘制圣保罗地图中两个不同数据帧的经度和纬度坐标 并希望手动标记每个图例图层 更新 我编辑了下面的代码以使其完全可重现 我使用的是地理编码函数而不是 get map 更新 我想在不合并数据帧的情况下
  • addTooltip 间歇性地使用observeEvent 中的两个输入

    我正在构建一个闪亮的应用程序来绘制网络 用户可以选择一个节点 单击切换按钮以显示该节点的自我网络 然后单击相同的按钮返回主网络 我试图获得一个工具提示 将鼠标悬停在按钮上 其中的文本会根据按钮本身的状态以及是否选择节点而变化 问题是工具提示
  • Google Apps 脚本:一天内调用服务次数过多:电子邮件

    我正在尝试循环浏览电子表格 并为每一行发送一封电子邮件 电子邮件发送后 我想删除该行 然而那是行不通的 由于某种原因 它开始疯狂地发送电子邮件 并在某个时候达到限制并退出 它实际上只删除一行 请参阅下面的代码 function sendEm
  • Fortran 与 C++ 相比,如今 Fortran 在数值分析方面是否仍然具有优势? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 随着C 编译器 尤其是intel编译器 的快速发展 以及在C C 代码中直接应用SIMD函数的能力 Fortran在数值计算领域是否仍然具有真正
  • 如何在 App.config 中设置 SQLCommandTimeout

    我已经使用 SQL 数据库开发了一个 Window 服务 目前我的数据库中充满了记录 因此查询执行需要很长时间 而默认命令超时为 30S 但我想将其增加到 120S 一个选项是 com CommandTimeout 120 但我的应用程序中
  • Java 方法中的动态返回类型

    我在这里多次看到类似的问题 但有一个很大的区别 在其他问题中 返回类型由参数确定 我想要 需要做的是通过解析的值确定返回类型byte 根据我收集的信息 以下方法可能有效 public Comparable getParam String p
  • 为什么 _mm_stream_ps 会产生 L1/LL 缓存未命中?

    我正在尝试优化计算密集型算法 但遇到了一些缓存问题 我有一个巨大的缓冲区 它偶尔会随机写入 并且在应用程序结束时只读取一次 显然 写入缓冲区会产生大量缓存未命中 并且还会污染随后再次需要进行计算的缓存 我尝试使用非时间移动内在函数 但缓存未