造成这种性能下降的原因是什么?如何调查它?

2024-04-04

Context

我正在使用 NEON SIMD 指令为 ARM64 编写一些高性能代码,我正在尝试进一步优化。我只使用整数运算,不使用浮点数。此代码完全受 CPU 或内存限制:它不执行任何类型的系统调用或 I/O(文件系统、网络或其他任何内容)。该代码在设计上是单线程的——任何并行性都应该通过使用不同参数从不同的 CPU 调用代码来处理。数据工作集应该足够小,以适合我的CPU的L1 D-cache,如果它溢出一点,它肯定会适合L2,并且有大量的空闲空间。

我的开发环境是配备 M1 处理器的 Apple 笔记本电脑,运行 macOS;因此,性能调查工具的首选是 Apple 的 Instruments。我知道 VTune 有一些更高级的功能,例如自上而下的微架构分析,但显然这不适用于 ARM。

问题

我有一个想法,在高层次上,它的工作原理是这样的:某个函数f(x, y)可以分解为两个函数g() and h()。我可以计算x2 = g(x), y2 = g(y)进而h(x2, y2),获得与以下相同的结果f(x, y)。然而,事实证明我计算f()多次使用相同输入参数的不同组合。通过将所有这些输入应用到g()并缓存它们的输出,我可以直接调用h()使用这些缓存的值并节省一些重新计算的时间g()-部分f().

基准测试

我通过 Google Benchmark 进行微基准测试,确认了基本想法是合理的。如果f()需要 100 X(其中 X 是任意时间单位),然后每次调用g()需要 14 X,并调用h()需要 78 X。虽然打电话的时间更长g()然后两次h()而不是f(),假设我需要计算f(x, y) and f(x, z),通常需要 200 X。我可以计算x2 = g(x), y2 = g(y) and z2 = g(z),取 3*14 = 42 X,然后h(x2, y2) and h(x2, z2),取 2*78 = 156 X。总共,我花费了 156 + 42 = 198 X,这已经少于 200 X,对于更大的示例,节省的费用将增加,最多可达 22%,因为这就是少得多h()成本相比f()(假设我计算h()g())。这将显着加快我的应用程序的速度。

我继续在一个更现实的例子上测试这个想法:我有一些代码可以做很多事情,加上 3 次调用f()它们之间使用相同的两个参数的组合。所以,我将 3 个调用替换为f()通过 2 次调用g()以及 3 次致电h()。上面的基准测试表明这应该将执行时间减少 3*100 - 2*14 - 3*78 = 38 X。但是,对修改后的代码进行基准测试表明执行时间增加约 700 倍!

我尝试将每个呼叫替换为f()分别拨打 2 次g()其论点并呼吁h()。这应该会使执行时间增加 2*14 + 78 - 100 = 6 X,但执行时间却增加了 230 X(并非巧合,大约是 700 X 的 1/3)。

使用 Apple Instruments 的性能计数器结果

为了给讨论带来一些数据,我使用 CPU 计数器模板在 Apple Instruments 下运行了这两个代码,监视了一些我认为可能相关的性能计数器。

作为参考,原始代码执行时间为 7.6 秒(仅考虑迭代次数乘以每次迭代的执行时间,即忽略 Google Benchmark 开销),而新代码执行时间为 9.4 秒;即相差 1.8 秒。两个版本都使用完全相同的迭代次数并处理相同的输入,产生相同的输出。该代码在 M1 的性能核心上运行,我假设该核心以其最大 3.2 GHz 时钟速度运行。

Parameter Original code New code
Total cycles 22,199,155,777 27,510,276,704
MAP_DISPATCH_BUBBLE 78,611,658 6,438,255,204
L1D_CACHE_MISS_LD 892,442 1,808,341
L1D_CACHE_MISS_ST 2,163,402 4,830,661
L1I_CACHE_MISS_DEMAND 2,620,793 7,698,674
INST_SIMD_ALU 79,448,291,331 78,253,076,740
INST_SIMD_LD 17,254,640,147 16,867,679,279
INST_SIMD_ST 14,169,912,790 14,029,275,120
INST_INT_ALU 4,512,600,211 4,379,585,445
INST_INT_LD 550,965,752 546,134,341
INST_INT_ST 455,541,070 455,298,056
INST_ALL 119,683,934,968 118,972,558,207
MAP_STALL_DISPATCH 6,307,551,337 5,470,291,508
SCHEDULE_UOP 116,252,941,232 113,882,670,763
MAP_REWIND 16,293,616 11,787,119
FLUSH_RESTART_OTHER_NONSPEC 58,616 90,955
FETCH_RESTART 27,417,457 28,119,690
BRANCH_MISPRED_NONSPEC 432,761 465,697
L1I_TLB_MISS_DEMAND 754,161 1,492,705
L2_TLB_MISS_INSTRUCTION 485,702 1,217,474
MMU_TABLE_WALK_INSTRUCTION 486,812 1,219,082
BRANCH_MISPRED_NONSPEC 377,750 440,382
INST_BRANCH 1,194,614,553 1,151,040,641

仪器不允许我将所有这些计数器添加到同一运行中,因此某些结果来自不同的运行。然而,由于代码是完全确定性的并且运行相同次数的迭代,因此运行之间的任何差异都应该只是随机噪声。

EDIT:在使用 Instruments 时,我发现一个性能计数器在原始代码和新代码之间具有截然不同的值,即MAP_DISPATCH_BUBBLE。仍在研究它的含义,它是否可以解释我所看到的问题,以及如何解决这个问题。

EDIT 2:我决定在我可以访问的其他 ARM 处理器(Cortex-X2 和 Cortex-A72)上测试此代码。在 Cortex-X2 上,两个版本的性能相同,而在 Cortex-A72 上,新代码的性能略有提高(约 1.5%)。所以我比以往任何时候都更倾向于相信我遇到了 M1 前端瓶颈。

假设和数据分析

之前遇到过此代码库的性能问题,一些想法浮现在脑海中:

  • 内存对齐:SIMD 代码有时对内存对齐很敏感,特别是对于内存绑定代码,我怀疑我的代码可能是这样。但是,添加或删除__attribute__((aligned(64)))没有什么区别,所以我不认为是这样。
  • D 高速缓存未命中:新代码分配一些新数组来缓存输出g(),因此可能会导致更多缓存未命中。事实上,L1 D 缓存未命中(加载 + 存储)比原始代码多了 360 万次。然而,正如我在开始时提到的,工作集很容易适合 L2。假设 10 个周期的 L2 缓存未命中成本,则仅为 3600 万个周期。在 3.2 GHz 下,该时间仅为 1.1 ms,即
  • 指令缓存未命中:类似的情况:有额外的 510 万次 L1 I-cache 未命中,但以 10 个周期的成本计算,我们看到的是 1.6 毫秒,同样小于观察到的性能差异的 0.1%。
  • 内联/展开:我在代码上采用了积极的内联和循环展开,以及 LTO 和 Unity 构建,因为性能是第一优先级,代码大小无关紧要(除非它通过 I-cache 未命中等方式影响性能)。我考虑了新代码可能会不太积极地内联/展开的可能性,因为编译器会采用某种启发式方法来获得最大代码大小。这可能会导致执行更多指令,例如循环的比较/分支,以及CALL/RET以及函数调用的函数序言/尾声。然而,该表显示新代码执行的每种指令要少一些(正如我所期望的),当然,总共(INST_ALL).

不知何故,原始代码只是实现了更高的IPC,我不知道为什么。另外,需要明确的是:两个代码使用相同的算法执行相同的操作。我所做的基本上是代码f()(一堆对其他子例程的函数调用)之间g() and h().

问题

这让我想到了我的问题:什么可能使新代码比旧代码运行得慢?我还可以在 Instruments 中查看哪些其他性能计数器来深入了解此问题?

除了这个具体问题的答案之外,我还在寻找有关如何在未来解决类似问题的一般建议。我找到了一些关于调试性能问题的书籍,但它们通常分为两个阵营。第一个仅描述了我熟悉的分析过程:找出哪些函数执行时间最长并对其进行优化。第二种是以书籍为代表,例如系统性能:企业和云 https://www.brendangregg.com/systems-performance-2nd-edition-book.html and 每台计算机性能书籍 https://rads.stackoverflow.com/amzn/click/com/1482657759,并且更接近我正在寻找的东西。然而,他们关注系统级问题,如 I/O、内核调用等;我编写的代码类型受 CPU 限制,也可能受内存限制,有很多机会转换为 SIMD,并且不与外界交互。基本上,我想知道如何使用分析器和 CPU 性能计数器(周期计数器、缓存未命中、按 ALU、内存等类型执行的指令)设计有意义的实验,以解决我的代码的此类性能问题当它们出现时。


None

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

造成这种性能下降的原因是什么?如何调查它? 的相关文章

  • 网站性能衡量

    我需要一个免费的工具来测量网站的性能 并且不需要对代码 jsp asp 页面 进行任何更改 感谢所有帮助 对于绩效衡量 我建议您YSlow http developer yahoo com yslow 它是一个 Firefox 插件 集成了
  • CUDA 矩阵加法时序,按行与按行比较按栏目

    我目前正在学习 CUDA 并正在做一些练习 其中之一是实现以 3 种不同方式添加矩阵的内核 每个元素 1 个线程 每行 1 个线程和每列 1 个线程 矩阵是方阵 并被实现为一维向量 我只需用以下命令对其进行索引 A N row col 直觉
  • Java中的马尔可夫模型决策过程

    我正在用 Java 编写辅助学习算法 我遇到了一个我可能可以解决的数学问题 但由于处理量很大 我需要一个最佳解决方案 话虽这么说 如果有人知道一个优化的库 那就太棒了 但语言是 Java 所以需要考虑到这一点 这个想法相当简单 对象将存储变
  • 在 Transact SQL 中何时使用 EXCEPT 而不是 NOT EXISTS?

    我最近刚刚通过阅读同事编写的代码了解到 SQL Server 中存在新的 EXCEPT 子句 有点晚了 我知道 真的让我很惊讶 但是我对它的使用有一些疑问 建议什么时候使用它 使用它与使用 AND NOT EXISTS 的相关查询在性能方面
  • Python 中快速、小型且重复的矩阵乘法

    我正在寻找一种使用 Python Cython Numpy 快速将许多 4x4 矩阵相乘的方法 任何人都可以给出任何建议吗 为了展示我当前的尝试 我有一个需要计算的算法 A 1 A 2 A 3 A N 哪里每个 A i A j Python
  • scipy-optimize-minimize 不执行优化 - CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL

    我试图最小化定义如下的函数 utility decision decision risk cost 其中变量采用以下形式 决策 二进制数组 风险 浮点数数组 成本 常数 我知道解决方案将采取以下形式 决定 1如果 风险 gt 阈值 决定 0
  • vm.dirty_ratio 和 vm.dirty_background_ratio 之间的区别?

    我目前正在试验中找到的内核参数 proc sys vm 尤其dirty ratio and dirty background ratio 内核文档对两者都有以下解释 脏背景比例 包含 以包含空闲页面的总可用内存的百分比表示 和可回收页 后台
  • 使用列模数的更简洁方法

    我目前有一个人员列表 我已将其分为两列 但在完成代码后 我一直想知道是否有更有效或更干净的方法来完成同样的事情 echo table class area list tr Loop users within areas divided up
  • time() 会返回相同的输出吗?

    当用户注册时 我正在为 PHP 中的用户生成令牌 我想知道两个用户是否可以获得相同的令牌 因为这会破坏系统 请让我知道这是否足够 token md5 rand time 编辑 我现在正在使用我在另一个问题上找到的generate uuid
  • 如何使用 VBA 将符号/图标格式化为单元格而不使用条件格式

    我使用 VBA 代码放置条件格式以覆盖大型表格中的值 每个单元格使用 2 个公式来确定使用 3 个符号中的哪一个 我需要根据列使用不同的单元格检查每个单元格的值 因此据我了解 我必须将条件格式规则单独放置在每个单元格上 以确保每个单元格中的
  • Maven 依赖项更新报告需要数小时才能完成

    我有任务运行 Jenkins 工作女巫会报告新版本的库 我认为这些可以满足我的需要 org codehaus mojo versions maven plugin 2 5 plugin updates report org codehaus
  • 在 R 中替换数据帧中最低列表值的最有效方法

    我有一个数据框 df 其中包含为每个受试者记录的数字列表 向量 用于测试项目的两次重复 subj item rep vec s1 1 1 2 1 4 5 8 4 7 s1 1 2 1 1 3 4 7 5 3 s1 2 1 6 5 4 1 2
  • 如何编写更高效的代码

    世纪问题 我基本上想知道如果我将此代码编写为几个不同的变量或使用小数组 哪个会更有效 int x 34 int y 28 int z 293 vs double coordinate 3 34 28 293 我有一个坐标结构 我将按以下方式
  • 如何分析Android应用程序的电池使用情况并对其进行优化?

    我想分析我的应用程序的电池使用情况 我的意思是应用程序的各个部分 例如 广播接收器 监听器 服务等 使用多少电池 我需要一个详细的列表 从列表中 我想优化电池的使用 方法与使用内存分析器类似 http android developers
  • 给定两个(大)点集,我如何有效地找到彼此最接近的点对?

    我需要解决一个计算问题 该问题归结为搜索两个集合之间最接近的点对 问题是这样的 给定欧几里德空间中的一组点 A 和一组点 B 找到所有对 a b 使得 b 是 B 中与 a 最近的点 a 是 A 中与 b 最近的点 集合 A 和 B 的大小
  • 为什么 Chrome 审核建议我最小化 Cookie 大小?

    如何最小化请求的 cookie 大小 Chrome 似乎 警告我 我的 cookie 大小为 41B 这根本不是很多 但是它警告我有什么原因吗 这是一个 PHPSESSID cookie 我真的不知道如何最小化它 有任何想法吗 我的请求响应
  • 在 JavaScript 中嵌套“switch”案例:有速度优势吗?

    这里有新手问题 我有一个包含大量字符串的 开关 像这样按字母顺序拆分是否有速度优势 switch myString substring 0 1 case a switch myString case a string beginning w
  • glBlitFramebuffer 渲染缓冲区和渲染全屏纹理哪个更快?

    哪个更快更高效 使用 OpenGL 纹理作为 CUDA 表面并在四边形上渲染 新样式 使用渲染缓冲区作为 CUDA 表面并使用 glBlitFramebuffer 进行渲染 None
  • PrintStream是有缓冲的,但是flush不会降低性能,而BufferedOutputStream会加速性能

    我预计由于 PrintStream 是缓冲的 通过在每次 print 之后添加刷新操作 速度性能应该会显着降低 但事实并非如此 如下面的代码片段所示 此外 将 PrintStream 包裹在 BufferedOutputStream 周围可
  • 如何减少 JSF 中的 javax.faces.ViewState

    减少 JSF 中视图状态隐藏字段大小的最佳方法是什么 我注意到我的视图状态约为 40k 这会在每次请求和响应时下降到客户端并返回到服务器 特别是到达服务器时 这对用户来说会显着减慢 我的环境 JSF 1 2 MyFaces Tomcat T

随机推荐

  • 使用Karate REST API工具上传文件

    我正在尝试使用空手道在特定的松弛通道上上传图像 但没有运气 我尝试了多次不同的步骤 但仍然有 200 响应 并且图像未显示在通道中 尝试发布文字内容并成功在频道上找到文字 以下是我根据空手道文档尝试过的两个 post Feature Pos
  • 如何创建如下所示的交叉表报表?

    是否可以使用 Crystal Report 交叉表创建以下报表 这是报告 Growth Rate Last Year First Year Total each Area 这是我的结果集 Year Area PeopleCount 2005
  • 调整图像大小以适合 div + 保持比例 + 水平和垂直居中 + 圆角

    问题 调整图像大小以适合 div 已解决 保持比例已解决 水平和垂直居中 已解决 圆角 a 矩形图像 已解决 b 横幅图像 不可能的 所以问题是 如何去掉图像的矩形角 请看这里看看问题所在 gt gt gt http jsfiddle ne
  • 安装WDK VC++后损坏

    从以下位置安装 WDK https learn microsoft com en us windows hardware drivers download the wdk https learn microsoft com en us wi
  • 按给定位置将字符串分成两部分

    string Some string pos 5 begging Some s end tring 按给定位置将字符串分成两部分的最佳方法是什么 您可以使用substr http php net substr获取两个子字符串 str1 su
  • 如何发现 Heroku 上的内存泄漏?

    我有一个在 Heroku Cedar 上使用 Ruby 1 9 3 运行的 Rails 3 2 8 应用程序 该应用程序启动时运行良好 但连续使用一天左右后 我开始在日志中看到 R14 错误 一旦内存错误开始 即使应用程序闲置几个小时 它们
  • scikit-learn 中的 TfidfVectorizer:ValueError:np.nan 是无效文档

    我正在使用 scikit learn 中的 TfidfVectorizer 从文本数据中提取一些特征 我有一个 CSV 文件 其中包含分数 可以是 1 或 1 和评论 文本 我将这些数据提取到 DataFrame 中 以便可以运行 Vect
  • 尝试填充浮点数组时出现 ArrayStoreException

    为什么以下代码会引发异常 这意味着什么 float foo array new float WIDTH HEIGHT Assume WDITH and Height are defined java util Arrays fill foo
  • VB.Net 控制台应用程序返回 SHA512 哈希值

    我正在尝试在 VB Net 2008 中编写一个控制台应用程序来接受命令行参数输入 密码 并输出哈希值 我修改了这样它就会接受一个参数 并且我添加了自己的盐 密钥 但是与相同密码和盐的 PHP 输出相比 输出似乎不正确 我需要这样的东西 这
  • 将时间戳添加到 db Rails 5+ 中的现有表

    尝试将时间戳添加到现有表中 根据API文档添加时间戳 https apidock com rails ActiveRecord ConnectionAdapters SchemaStatements add timestamps 这是我的迁
  • 以程序方式生成二维“斑点”图形的好方法

    我希望以计算快速的方式创建一个 blob 这里的斑点被定义为可以是任何形状但全部相连的像素的集合 例子 ooo oooo oo oooooo o o ooooooooooooooooooo oooo oo ooooooo o oo oooo
  • 以编程方式检测图像是否有边框(返回布尔值)

    首先 我读过这篇文章 如何以编程方式检测图像边框 https stackoverflow com questions 1954742 how to detect an image border programmatically不过 他似乎在
  • Maximo 7.6 / SQL Server 2014 中文或泰文字符

    我工作的公司正在从带有 Oracle 10g 的 Maximo 7 1 升级到带有 SQL Server 2014 的 Maximo 7 6 该程序允许 7 1 和 Oracle 10g 中的中文和泰语字符通过前端 没有任何问题 但带有 S
  • Flutter 如何使AppBar条子标题垂直居中?

    我试图将 Sliver AppBar 的标题居中 并在其下方添加第二个文本 我做不到 下面是现在的图像以及它应该的样子 谁能帮我 这是我的代码 import package flutter material dart void main g
  • 如何在android中设置文本方向

    我有一个文本视图 我在其中设置了波斯语 阿拉伯语文本并将重力设置为右侧 但它在平板电脑中显示左侧的文本 如果设置左侧的重力则显示右侧 我该怎么做 直到它正常工作
  • 显示网络流量

    是否可以查看类似于以下内容的网络流量 http 请求 原始 http 等 chrome dev tools network tab 由应用程序生成 nodejs如果有什么区别的话 通过vscode 此功能已经存在 但它不显示正在运行的应用程
  • 在 CentOS 上安装 M2Crypto

    我正在尝试通过从源代码编译来在 CentOS 上安装 M2Crypto 我正在做一个 python setup py 构建 但我收到以下错误 usr local lib python2 6 distutils dist py 266 Use
  • 在 Swift 中将对象写入 CSV 时如何处理逗号?

    关于堆栈溢出似乎还有其他答案 但没有任何特定于 swift 的答案 我正在从包含 3 个属性的站点对象生成 CSV Struct SiteDetails var siteName String var siteType String var
  • 用于谷歌存储的静态网站的 Terraform 谷歌存储桶 - 403

    尝试为静态网站创建一个谷歌存储桶 provider google project myprojectname 123 credentials file storage admin json region us central1 zone u
  • 造成这种性能下降的原因是什么?如何调查它?

    Context 我正在使用 NEON SIMD 指令为 ARM64 编写一些高性能代码 我正在尝试进一步优化 我只使用整数运算 不使用浮点数 此代码完全受 CPU 或内存限制 它不执行任何类型的系统调用或 I O 文件系统 网络或其他任何内