gcc -O0 在 2 的幂矩阵大小(矩阵转置)上优于 -O3

2023-12-29

(出于测试目的)我编写了一个简单的方法来计算 nxn 矩阵的转置

void transpose(const size_t _n, double* _A) {
    for(uint i=0; i < _n; ++i) {
        for(uint j=i+1; j < _n; ++j) {
            double tmp  = _A[i*_n+j];
            _A[i*_n+j] = _A[j*_n+i];
            _A[j*_n+i] = tmp;
        }
    }
}

当使用优化级别 O3 或 Ofast 时,我希望编译器展开一些循环,这将带来更高的性能,特别是当矩阵大小是 2 的倍数(即,每次迭代可以执行双循环体)或类似的情况时。相反,我测量的结果恰恰相反。 2 的幂实际上显示执行时间显着增加。

这些尖峰也以 64 为间隔,以 128 为间隔更明显,依此类推。每个尖峰延伸到相邻的矩阵大小,如下表所示

size n  time(us)
1020    2649
1021    2815
1022    3100
1023    5428
1024    15791
1025    6778
1026    3106
1027    2847
1028    2660
1029    3038
1030    2613

我使用 gcc 版本 4.8.2 进行编译,但 clang 3.5 也会发生同样的情况,所以这可能是一些通用的东西?

所以我的问题基本上是:为什么执行时间会周期性增加?它是任何优化选项附带的通用东西吗(就像 clang 和 gcc 一样)?如果是这样,哪个优化选项导致了这种情况?

这怎么会如此重要,以至于该程序的 O0 版本的性能也比 03 版本高出 512 的倍数呢?


EDIT:请注意此(对数)图中尖峰的大小。通过优化转置 1024x1024 矩阵实际上需要与转置 1300x1300 矩阵一样多的时间without优化。如果这是缓存错误/页面错误问题,那么有人需要向我解释为什么程序的优化版本的内存布局如此显着不同,以至于它在 2 的幂上失败,只是为了恢复高性能稍大的矩阵。缓存错误不应该创建更多类似步骤的模式吗?为什么执行时间又下降了? (为什么优化会产生以前不存在的缓存故障?)


EDIT:以下应该是 gcc 生成的汇编代码

没有优化(O0):

_Z9transposemRPd:
.LFB0:
    .cfi_startproc
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp
    .cfi_def_cfa_register 6
    mov QWORD PTR [rbp-24], rdi
    mov QWORD PTR [rbp-32], rsi
    mov DWORD PTR [rbp-4], 0
    jmp .L2
.L5:
    mov eax, DWORD PTR [rbp-4]
    add eax, 1
    mov DWORD PTR [rbp-8], eax
    jmp .L3
.L4:
    mov rax, QWORD PTR [rbp-32]
    mov rdx, QWORD PTR [rax]
    mov eax, DWORD PTR [rbp-4]
    imul    rax, QWORD PTR [rbp-24]
    mov rcx, rax
    mov eax, DWORD PTR [rbp-8]
    add rax, rcx
    sal rax, 3
    add rax, rdx
    mov rax, QWORD PTR [rax]
    mov QWORD PTR [rbp-16], rax
    mov rax, QWORD PTR [rbp-32]
    mov rdx, QWORD PTR [rax]
    mov eax, DWORD PTR [rbp-4]
    imul    rax, QWORD PTR [rbp-24]
    mov rcx, rax
    mov eax, DWORD PTR [rbp-8]
    add rax, rcx
    sal rax, 3
    add rdx, rax
    mov rax, QWORD PTR [rbp-32]
    mov rcx, QWORD PTR [rax]
    mov eax, DWORD PTR [rbp-8]
    imul    rax, QWORD PTR [rbp-24]
    mov rsi, rax
    mov eax, DWORD PTR [rbp-4]
    add rax, rsi
    sal rax, 3
    add rax, rcx
    mov rax, QWORD PTR [rax]
    mov QWORD PTR [rdx], rax
    mov rax, QWORD PTR [rbp-32]
    mov rdx, QWORD PTR [rax]
    mov eax, DWORD PTR [rbp-8]
    imul    rax, QWORD PTR [rbp-24]
    mov rcx, rax
    mov eax, DWORD PTR [rbp-4]
    add rax, rcx
    sal rax, 3
    add rdx, rax
    mov rax, QWORD PTR [rbp-16]
    mov QWORD PTR [rdx], rax
    add DWORD PTR [rbp-8], 1
.L3:
    mov eax, DWORD PTR [rbp-8]
    cmp rax, QWORD PTR [rbp-24]
    jb  .L4
    add DWORD PTR [rbp-4], 1
.L2:
    mov eax, DWORD PTR [rbp-4]
    cmp rax, QWORD PTR [rbp-24]
    jb  .L5
    pop rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   _Z9transposemRPd, .-_Z9transposemRPd
    .ident  "GCC: (Debian 4.8.2-15) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

优化 (O3)

_Z9transposemRPd:
.LFB0:
    .cfi_startproc
    push    rbx
    .cfi_def_cfa_offset 16
    .cfi_offset 3, -16
    xor r11d, r11d
    xor ebx, ebx
.L2:
    cmp r11, rdi
    mov r9, r11
    jae .L10
    .p2align 4,,10
    .p2align 3
.L7:
    add ebx, 1
    mov r11d, ebx
    cmp rdi, r11
    mov rax, r11
    jbe .L2
    mov r10, r9
    mov r8, QWORD PTR [rsi]
    mov edx, ebx
    imul    r10, rdi
    .p2align 4,,10
    .p2align 3
.L6:
    lea rcx, [rax+r10]
    add edx, 1
    imul    rax, rdi
    lea rcx, [r8+rcx*8]
    movsd   xmm0, QWORD PTR [rcx]
    add rax, r9
    lea rax, [r8+rax*8]
    movsd   xmm1, QWORD PTR [rax]
    movsd   QWORD PTR [rcx], xmm1
    movsd   QWORD PTR [rax], xmm0
    mov eax, edx
    cmp rdi, rax
    ja  .L6
    cmp r11, rdi
    mov r9, r11
    jb  .L7
.L10:
    pop rbx
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE0:
    .size   _Z9transposemRPd, .-_Z9transposemRPd
    .ident  "GCC: (Debian 4.8.2-15) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

执行时间的周期性增加一定是由于缓存只是N路关联而不是全关联造成的。您正在目睹与缓存行选择算法相关的哈希冲突。

最快的 L1 缓存的缓存行数比下一级 L2 少。在每个级别中,每个缓存行只能从一组有限的源中填充。

高速缓存行选择算法的典型硬件实现将仅使用内存地址中的少数位来确定数据应写入哪个高速缓存槽中——在硬件中,移位是自由的。

这会导致内存范围之间的竞争,例如地址 0x300010 和 0x341010 之间。 在完全顺序算法中,这并不重要——N足够大,实际上可以all形式的算法:

 for (i=0;i<1000;i++) a[i] += b[i] * c[i] + d[i];

但是,当输入(或输出)数量变大时(算法优化时会在内部发生这种情况),缓存中的一个输入会强制将另一个输入移出缓存。

 // one possible method of optimization with 2 outputs and 6 inputs
 // with two unrelated execution paths -- should be faster, but maybe it isn't
 for (i=0;i<500;i++) { 
       a[i]     += b[i]     * c[i]     + d[i];
       a[i+500] += b[i+500] * c[i+500] + d[i+500];
 }

中的一个图表示例 5:缓存关联性 http://igoro.com/archive/gallery-of-processor-cache-effects/说明了矩阵行之间的 512 字节偏移是特定系统的全局最坏情况尺寸。当知道这一点时,有效的缓解措施是将矩阵水平过度分配到某个其他维度char matrix[512][512 + 64].

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

gcc -O0 在 2 的幂矩阵大小(矩阵转置)上优于 -O3 的相关文章

  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 访问私人成员[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 通过将类的私有成员转换为 void 指针 然后转换为结构来访问类的私有成员是否合适 我认为我无权修改包含我需要访问的数据成员的类 如果不道德 我
  • pthread_cond_timedwait() 和 pthread_cond_broadcast() 解释

    因此 我在堆栈溢出和其他资源上进行了大量搜索 但我无法理解有关上述函数的一些内容 具体来说 1 当pthread cond timedwait 因为定时器值用完而返回时 它如何自动重新获取互斥锁 互斥锁可能被锁定在其他地方 例如 在生产者
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • 在 Visual Studio 2008 上设置预调试事件

    我想在 Visual Studio 中开始调试程序之前运行一个任务 我每次调试程序时都需要运行此任务 因此构建后事件还不够好 我查看了设置的 调试 选项卡 但没有这样的选项 有什么办法可以做到这一点吗 你唯一可以尝试的 IMO 就是尝试Co
  • C - 找到极限之间的所有友好数字

    首先是定义 一对友好的数字由两个不同的整数组成 其中 第一个整数的除数之和等于第二个整数 并且 第二个整数的除数之和等于第一个整数 完美数是等于其自身约数之和的数 我想做的是制作一个程序 询问用户一个下限和一个上限 然后向他 她提供这两个限
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • 在 ASP.NET Core 3.1 中使用包含“System.Web.HttpContext”的旧项目

    我们有一些用 Net Framework编写的遗留项目 应该由由ASP NET Core3 1编写的API项目使用 问题是这些遗留项目正在使用 System Web HttpContext 您知道它不再存在于 net core 中 现在我们
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • 如何衡量两个字符串之间的相似度? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给定两个字符串text1 and text2 public SOMEUSABLERETURNTYPE Compare string t
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • 如何在 VBA 中声明接受 XlfOper (LPXLOPER) 类型参数的函数?

    我在之前的回答里发现了问题 https stackoverflow com q 19325258 159684一种无需注册即可调用 C xll 中定义的函数的方法 我之前使用 XLW 提供的注册基础结构 并且使用 XlfOper 类型在 V
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • 需要哪个版本的 Visual C++ 运行时库?

    microsoft 的最新 vcredist 2010 版 是否包含以前的版本 2008 SP1 和 2005 SP1 还是我需要安装全部 3 个版本 谢谢 你需要所有这些
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • 如何使用 std::string 将所有出现的一个字符替换为两个字符?

    有没有一种简单的方法来替换所有出现的 in a std string with 转义 a 中的所有斜杠std string 完成此操作的最简单方法可能是boost字符串算法库 http www boost org doc libs 1 46
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐

  • Express GET 路由不适用于参数

    我是 Express 和 Mongoose 的新手 我目前正在开发我的第一个项目 这不是教程 我遇到了问题 我有多个路由 它们在 index js 中定义如下 app use api client require routes client
  • 如何从字符串中读取 NSDate?

    我有带有日期的字符串 并且想将它们解析为 NSDate 对象 有没有办法做到这一点 我看过 NSDate 和 NSScanner 但没有看到任何可以从字符串中读取它的东西 在cocoa sdk中 通常是 如果您想要一个日期并且有一个字符串
  • MVC 场景中的 Javascript 事件与回调

    我正在尝试找出一种很好的方法来拥有视图和控制器并最大限度地减少它们之间的联系 除了一个事件有多个订阅者之外 像这样的 js 代码之间还有什么主要区别吗 var customers get function callback get cust
  • 使用循环时如何使 makefile 错误退出?

    如果我有以下 bash 命令 for i in x do ls i done echo OK 执行 ls 然后执行 ls x 失败 缺少 x 并且不打印 OK If for i in x do ls i done echo OK 那么即使
  • Google Cloud Platform:从命令行登录 GCP

    我确信这会很简单 但找不到任何文档或解决方案 我正在尝试使用 gcloud 编写一个脚本来在我的 GCP 实例中执行一些操作 是否可以仅通过命令行使用 gcloud 登录 身份验证 Thanks 这里有几个选择 取决于您到底想做什么 第一个
  • 原则 2 - 从实体外部禁用 PrePersist

    我正在尝试从 Doctrine 2 中的实体外部禁用实体事件 每次我们将新记录插入表中时 都需要运行很少的文件操作 这些操作已在带有 prePersist 注释的方法中实现 但是 我还需要运行一些数据装置并跳过文件操作部分作为测试的一部分
  • 强制轨迹栏值为十倍

    我用 C 在 Winform 项目上添加了一个轨迹栏 mySlider Minimum 0 mySlider Maximum 200 mySlider Value 30 mySlider SmallChange 10 mySlider La
  • Java applet 清单 - 允许所有 Caller-Allowable-Codebase

    从 Java 7u45 开始 如果网页尝试通过 javascript 与小程序交互 并且该页面未在清单的 Caller Allowable Codebase 属性中列出 则小程序将显示警告消息 即使使用受信任的证书进行签名 有关此更改的发行
  • 是否抛出 ConcurrentModificationException 取决于系统

    我正在使用 Iterator 编写一段代码 当我在 Windows 上从 IDE 运行该程序时 在 a 行收到 ConcurrentModificationException LinkedList ll new LinkedList Ite
  • 是否应该在编写代码之前先编写单元测试?

    我知道测试驱动开发的定义原则之一是首先编写单元测试 然后编写代码来通过这些单元测试 但是有必要这样做吗 我发现 在编写之前 我常常不知道自己在测试什么 主要是因为我过去从事的几个项目更多地是从概念验证演变而来 而不是设计出来的 我之前曾尝试
  • UNIQUE 约束失败

    我正在使用 Django 进行 Tango 但无法解决这个练习 我明白了django db utils IntegrityError UNIQUE constraint failed rango category name错误 这是我尝试实
  • IE 浏览器控件 res:// 用法

    我在我的应用程序中使用 IWebBrowser2 控件 并且有各种 html 文件作为资源存储在 exe 中 为了加载这些 我使用 res 协议 问题是 对于某些版本的 IE 页面不再加载 而只是显示 操作已取消 Internet Expl
  • 如何根据另一个矩阵、data.frame 或向量的行重新排序

    test1 lt as matrix c 1 2 3 4 5 row names test1 lt c a d c b e test2 lt as matrix c 6 7 8 9 10 row names test2 lt c e d c
  • 为我的 numpy 数组提供更紧凑的 __repr__ 吗?

    当我显示数组时 默认 repr 方法用于ndarray对象对于我想做的事情来说太大了 a np eye 32 b hello 42 array a b 产生 array array 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1
  • MongoDB 选择 _id 数组中的哪个位置?

    在 mongo db 中可以像 SQL 一样选择集合的文档 SELECT FROM collection WHERE id IN 1 2 3 4 或者如果我有一个 id array我必须一一选择 然后重新组合array object结果 E
  • 从命令行启动进程时如何捕获进程的 PID?

    有没有办法纯粹在 bat 文件中执行此操作 目的是推出iexplore exe 然后在完成时杀死该实例 这是我使用的 echo off rem there is a tab in the file at the end of the lin
  • 注释声明中 String[] 的默认“”是什么?

    什么是default 下面代码中的部分是什么意思 public interface MyAnnotation String names default 是否等于 String names default new String 0 publi
  • 将响应发送给除发件人之外的所有客户端

    要将某些内容发送给所有客户 您可以使用 io sockets emit response data 要接收来自客户的信息 您可以使用 socket on cursor function data 如何将两者结合起来 以便在服务器上从客户端接
  • enctype“application/json”形式可用吗?

    我正在读这个w3c 文档 https www w3 org TR html json forms 关于用 html 表单发布 JSON 数据 并尝试测试它 我的测试表格如下
  • gcc -O0 在 2 的幂矩阵大小(矩阵转置)上优于 -O3

    出于测试目的 我编写了一个简单的方法来计算 nxn 矩阵的转置 void transpose const size t n double A for uint i 0 i lt n i for uint j i 1 j lt n j dou