AArch64架构调用链性能数据采集原理

简介: 本次分享的主题是AArch64架构调用链性能数据采集原理,由阿里云苏轩楠分享。主要分为五个部分:1. 术语解释2. Frame Pointer RegisterStack Unwind3. Dwarf-based Stack Unwind4. /BRBE/CSRE Stack Unwind5. Kernel-space Stack Unwind&eBPF Unwinders

AArch64架构调用链性能数据采集原理

内容分析:

1. 术语解释

2. Frame Pointer RegisterStack Unwind

3. Dwarf-based Stack Unwind

4. /BRBE/CSRE Stack Unwind

5. Kernel-space Stack Unwind&eBPF Unwinders

 

本次分享的主题可以帮助到一些场景,比如在原有的平台上迁移到 Arm64 适配或优化的过程中会涉及到性能数据收集的步骤。性能数据收集例如调度栈的收集可以容易获得到相关信息的渠道,但是我遇到在 x86 上做性能优化时,如何获得程序运行时的调度栈的信息恰恰会卡住整个团队的进度。本次分享使用 Arm 平台对调度链进行收集。

image.png

本次分享有五部分。第一进行术语的解释,第二部分基于栈寄存器的调用链收集,第三部分基于 Dwarf 调用链的收集,这两部分都是基于软件调用链的收集,第四部分是基于硬件。第五部分在 Linux 内核中获得当下比较流行的基于 eBPF 系统数据收集,收集该调用栈会遇到什么问题或选择。

 

01.术语解释

image.png

首先介绍调用栈收集前介绍一些概念。一个调用栈由一个方法、一个帧组成。在编译原理中成为 Active Record 活动记录。一个帧有起始地址,保持方法的局部变量。也有栈顶地址,通常有一个单独的寄存器进行保存,例如X29是帧的起始地址,X31/XP用来保存栈顶的地址。帧与帧之间如何进行交互呢?通过 Calling Convention 调用约定。从A方法调用到B方法存在一个存参的约定。例如在 Arm 上X0是第一个参数,X1是第二个参数,如果超过X8可能需要通过栈来传输。所以从一个帧到另一个帧存在主动调用和被动调用的关系,主动调用是 Caller,被调用者是 Callee。

image.png

当一个事件发生时或者在程序中下一个断点时,需要从事件发生的点回溯到起始的调用方,称为 Stack Unwinder,一层一层解,从 Main方法一层一层调用。该过程为 Stack Unwinder。如图中的例子,该帧有一些参数和局部变量,FP(X29)指向帧的起始,SP(X31)指向栈底。

image.png

一层一层解开后就可以进行符号化。从帧到对应的符 Symbolization 。

image.png

图中中间是一个个帧, Frame 右侧是内部结构。一帧一帧解开后是地址,从地址到右侧的 Foo()、bar()、baz()是一个符号化过程。

介绍完术语后,介绍如何解开一个帧。

 

02.Frame Pointer RegisterStack Unwind

image.png

如下有一个示例 unwind.c。

image.png

在 Main 方法中调用 Foo(),在Foo()中调用Bar(),接着进行编译。

image.png

此处使用了一个参数 -fno-omit-frame-pointer,可以看到编号为54的 STP 的指令会将X29和X30保存到栈上。右侧保存X29、X30,同时将58位置上保存X29,更新 SP,指向新的位置。

image.png

Main 调用Foo,foo中是如何调用呢?28与2C的位置相同,但4C处返回时会调用上一帧的位置。右侧是在调用 Foo 后的状态,由于没有局部变量,所以保存上一帧的状态。接着调用 Bar。

image.png

调用 Bar 时,没有 STP 和X29、X30的寄存器。进入后寄存器的状态会指向该位置,其中会往上指,所以会产生链式的结构,一层一层往上解开栈。但是由于在调用Bar时,并没有保存 Foo 的返回位置,只是 Foo 保存了 Main 的返回位置,所以中间的 Foo 看不到。做 Stack Unwinder 时看不到这个地方。

image.png

这是第一个问题,第二个问题是如果将栈底的寄存器省略掉后会完全不保存,无法构建一个链向上回溯。由于它可以作为一个通用的寄存器进行使用,所以程序可以使用通用寄存器,降低将变量存到栈上的机会,提高性能。 Java 测试在一些极限的条件下大概有8%的性能影响。

以上就是性能问题和中间不保存丢失的问题。

image.png

还有此处,我们很依赖于保存上下文、栈帧的起始和结束位置。但是如果此时有一个远程的跳转 Black Miss,而代码不在指定的 Catch 中就会发生 Catch Miss。 Catch Miss 指的是代码的起始位置,但是代码的起始位置无法回溯到栈,这一层会被忽略掉,还没有开始构建栈的信息。或者在构建 Return 时就出去就会产生这样的问题。

 

03.Dwarf-based Stack Unwind

第二个方法基于 Dwarf 信息作为 Stack unwind 的工作。

  image.png

image.png

Dwarf 信息是一个格式标准,不仅使用在 Stack Unwind,在 GDB 中也会大量使用 Dwarf 信息进行 Stack Unwind 。将栈的边界信息或者调试信息存在单独的F文件或 Section 中。如果只做 Stack Unwind,不需要使用 gcc-g 生成 Dwarf 信息。因为 Stack Unwind 是栈的回溯信息,存在 .eh_frame 中。该信息用来做异常处理,例如写 C 代码调用 C++ 就会进行异常处理,所以需要存该信息进行回溯。 .eh_frame section 一定存在,所以可以进行一层层解开。

image.png

如果使用 Dwarfdump 命令,Dwarf 出结果后可以看到 Bar 方法下面有一些汇编和条目。其中 CFA=00(r31)位置的 CFA 代表 Canonical Frame Address,是帧的起始位置。R31是栈底位置,R31加上00表示栈底和栈顶在一起。第二条的400698位置,由于我们已经在栈帧上开辟了一个字节空间,CFA 会变成R31加 SP 变成栈的起始位置。最后一条4006b8位置执行Return 指令, Return 之前有 Add 指令,将SP加回16字节,栈顶位置与栈底位置在重复位置,所以偏移为00。虽然没有保存栈的上下文的 Frame Pointer 在栈帧上,但是通过该信息可以准确还原。如果 Exception 出现 Catch Miss 在起始位置也没有关系,可以进行正常回溯。

后面类似,可以通过 Bar方法进行回溯,一层一层往上。

image.png

看似该方案很好,但实际上还存在一个问题:速度慢。它是一个基于查表的方式,该表若是全部存下来会非常大。所以提出使用一个状态机或字节码快速描述该表,在执行时使用状态机或字节码去解开。

image.png

此处定义了一些字节码,但是存在一些缺陷。

image.png

非常慢,而且容易出错。由于是一个状态机,所以也存在安全问题。所以该机制并不完美。

 

04. /BRBE/CSRE Stack Unwind

image.png

之后是一个硬件机制 Arm64 BRBE/CSRE或Intel。

image.png

通过硬件机制去记录跳转过程,好处是开销很低并且非常可靠。下面是一个例子。

image.png

可以看到 Perf 命令中 any_call 会将跳转的位置罗列出:从哪里跳转到哪里。但是存在一个问题:硬件记录时存储成本太高。通常来讲,栈的深度足够长是不能回溯到起始位置的。

image.png

还有 Inline 函数展示不出。

以上介绍了几种能用的方式。

 

05.Kernel-space Stack Unwind&eBPF Unwinders

image.png

最后介绍内核态。

image.png

如果在内核上做 Stack Unwinder 有两种方式。第一基于 Frame Pointer Register,第二是 ORC Unwinder。

image.png

另外 eBPF 分为两部分。如果解的是内核态的栈,与上述一致。如果解用户态的栈,有很多人无法使用 ORC 技术,会提前基于 Dwarf 信息构建表,通过 eBPF 的 Map 机制将表的信息送入内核,在里面构建状态机进行解。好处是不用将用户态的栈从内核拷到用户态中,可以节省一定效率。所以该机制相对较好,未来还在探索更好的方式。

相关文章
|
2月前
|
存储 SQL 关系型数据库
MySQL进阶突击系列(03) MySQL架构原理solo九魂17环连问 | 给大厂面试官的一封信
本文介绍了MySQL架构原理、存储引擎和索引的相关知识点,涵盖查询和更新SQL的执行过程、MySQL各组件的作用、存储引擎的类型及特性、索引的建立和使用原则,以及二叉树、平衡二叉树和B树的区别。通过这些内容,帮助读者深入了解MySQL的工作机制,提高数据库管理和优化能力。
|
2月前
|
人工智能 前端开发 编译器
【AI系统】LLVM 架构设计和原理
本文介绍了LLVM的诞生背景及其与GCC的区别,重点阐述了LLVM的架构特点,包括其组件独立性、中间表示(IR)的优势及整体架构。通过Clang+LLVM的实际编译案例,展示了从C代码到可执行文件的全过程,突显了LLVM在编译器领域的创新与优势。
103 3
|
3月前
|
运维 持续交付 云计算
深入解析云计算中的微服务架构:原理、优势与实践
深入解析云计算中的微服务架构:原理、优势与实践
113 3
|
20天前
|
Java Linux C语言
《docker基础篇:2.Docker安装》包括前提说明、Docker的基本组成、Docker平台架构图解(架构版)、安装步骤、阿里云镜像加速、永远的HelloWorld、底层原理
《docker基础篇:2.Docker安装》包括前提说明、Docker的基本组成、Docker平台架构图解(架构版)、安装步骤、阿里云镜像加速、永远的HelloWorld、底层原理
268 89
|
8天前
|
机器学习/深度学习 人工智能 NoSQL
记忆层增强的 Transformer 架构:通过可训练键值存储提升 LLM 性能的创新方法
Meta研究团队开发的记忆层技术通过替换Transformer中的前馈网络(FFN),显著提升了大语言模型的性能。记忆层使用可训练的固定键值对,规模达百万级别,仅计算最相似的前k个键值,优化了计算效率。实验显示,记忆层使模型在事实准确性上提升超100%,且在代码生成和通用知识领域表现优异,媲美4倍计算资源训练的传统模型。这一创新对下一代AI架构的发展具有重要意义。
30 11
记忆层增强的 Transformer 架构:通过可训练键值存储提升 LLM 性能的创新方法
|
12天前
|
存储 缓存 监控
ClickHouse 架构原理及核心特性详解
ClickHouse 是由 Yandex 开发的开源列式数据库,专为 OLAP 场景设计,支持高效的大数据分析。其核心特性包括列式存储、字段压缩、丰富的数据类型、向量化执行和分布式查询。ClickHouse 通过多种表引擎(如 MergeTree、ReplacingMergeTree、SummingMergeTree)优化了数据写入和查询性能,适用于电商数据分析、日志分析等场景。然而,它在事务处理、单条数据更新删除及内存占用方面存在不足。
132 21
|
12天前
|
存储 消息中间件 druid
Druid 架构原理及核心特性详解
Druid 是一个分布式、支持实时多维OLAP分析的列式存储数据处理系统,适用于高速实时数据读取和灵活的多维数据分析。它通过Segment、Datasource等元数据概念管理数据,并依赖Zookeeper、Hadoop和Kafka等组件实现高可用性和扩展性。Druid采用列式存储、并行计算和预计算等技术优化查询性能,支持离线和实时数据分析。尽管其存储成本较高且查询语言功能有限,但在大数据实时分析领域表现出色。
54 19
|
12天前
|
存储 SQL NoSQL
Doris 架构原理及核心特性详解
Doris 是百度内部孵化的OLAP项目,现已开源并广泛应用。它采用MPP架构、向量化执行引擎和列存储技术,提供高性能、易用性和实时数据处理能力。系统由FE(管理节点)和BE(计算与存储节点)组成,支持水平扩展和高可用性。Doris 适用于海量数据分析,尤其在电商、游戏等行业表现出色,但资源消耗较大,复杂查询优化有局限性,生态集成度有待提高。
46 15
|
9天前
|
Java 网络安全 开发工具
Git进阶笔记系列(01)Git核心架构原理 | 常用命令实战集合
通过本文,读者可以深入了解Git的核心概念和实际操作技巧,提升版本管理能力。
|
28天前
|
机器学习/深度学习 算法 PyTorch
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
软演员-评论家算法(Soft Actor-Critic, SAC)是深度强化学习领域的重要进展,基于最大熵框架优化策略,在探索与利用之间实现动态平衡。SAC通过双Q网络设计和自适应温度参数,提升了训练稳定性和样本效率。本文详细解析了SAC的数学原理、网络架构及PyTorch实现,涵盖演员网络的动作采样与对数概率计算、评论家网络的Q值估计及其损失函数,并介绍了完整的SAC智能体实现流程。SAC在连续动作空间中表现出色,具有高样本效率和稳定的训练过程,适合实际应用场景。
133 7
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现

热门文章

最新文章