[DRAFT] LLVM ThinLTO原理分析
我们在《论文阅读:ThinLTO: Scalable and Incremental LTO》中介绍了ThinLTO论文的主要思想,这里我们介绍下LLVM ThinLTO是如何实现的。本文主要分为如下几个部分:
- LLVM ThinLTO Object 含有哪些内容?
- LLVM ThinLTO 是如何做优化的?
- LLVM ThinLTO 能够enable哪些优化?
LLVM ThinLTO Objects都包含了哪些?
继续使用 Example of link time optimization 中的例子进行分析,在《LLVM full LTO 学习笔记》中我们通过 magic number 作为切入点,简单分析了 full lto 的过程。下面按照这个路子继续该分析
$ clang -flto=thin -c a.c -o a_lto.o
$ clang -flto=thin -c main.c -o main_lto.o
$ hexdump a_lto.o | head
0000000 4342 dec0 1435 0000 0005 0000 0c62 2430
0000010 594d 66be fb8d 4fb4 c81b 4424 3201 0005
0000020 0c21 0000 0266 0000 020b 0021 0002 0000
0000030 0016 0000 8107 9123 c841 4904 1006 3932
0000040 0192 0c84 0525 1908 041e 628b 1080 0245
0000050 9242 420b 1084 1432 0838 4b18 320a 8842
0000060 7048 21c4 4423 8712 108c 9241 6402 08c8
0000070 14b1 4320 8846 c920 3201 8442 2a18 2a28
0000080 3190 b07c 915c c420 00c8 0000 2089 0000
0000090 000e 0000 2232 0908 6220 0046 2b21 9824
我们可以看到 magic number 为 4342 dec0,说明对于 thin LTO 的 objects,其文件格式还是 bitcode file 。通过阅读 ThinLTO 的文档,发现其实文档中早已经说的很详细了。
In ThinLTO mode, as with regular LTO, clang emits LLVM bitcode after the compile phase. The ThinLTO bitcode is augmented with a compact summary of the module. During the link step, only the summaries are read and merged into a combined summary index, which includes an index of function locations for later cross-module function importing. Fast and efficient whole-program analysis is then performed on the combined summary index.
使用 llvm-dis a_lto.o 得到其可读的 IR。我们将其与 full lto 得到的 IR 进行对比后发现,两者差异极小,主要在于最后面的 summary 部分。以 a_lto.o 进行 thinLTO 和 full LTO 的对比如下。
// ---------------- Thin LTO ----------------//
!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}
!3 = !{i32 1, !"EnableSplitLTOUnit", i32 0}
!4 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 58e7bf78a3ef724b70304912fb3bb66af8c4a10c)"}^0 = module: (path: "a_lto.o", hash: (3489747275, 1762444854, 1461358598, 2667786215, 1835806708))
^1 = gv: (name: "foo2", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), refs: (writeonly ^2)))) ; guid = 2494702099028631698
^2 = gv: (name: "i", summaries: (variable: (module: ^0, flags: (linkage: internal, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1, constant: 0)))) ; guid = 2708120569957007488
^3 = gv: (name: "foo1", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 13, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^5)), refs: (readonly ^2)))) ; guid = 7682762345278052905
^4 = gv: (name: "foo4") ; guid = 11564431941544006930
^5 = gv: (name: "foo3", summaries: (function: (module: ^0, flags: (linkage: internal, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^4))))) ; guid = 17367728344439303071
^6 = blockcount: 5// ---------------- Full LTO ----------------//
!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}
!3 = !{i32 1, !"ThinLTO", i32 0}
!4 = !{i32 1, !"EnableSplitLTOUnit", i32 1}
!5 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 58e7bf78a3ef724b70304912fb3bb66af8c4a10c)"}^0 = module: (path: "a_lto.o", hash: (0, 0, 0, 0, 0))
^1 = gv: (name: "foo2", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 1, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), refs: (^2)))) ; guid = 2494702099028631698
^2 = gv: (name: "i", summaries: (variable: (module: ^0, flags: (linkage: internal, visibility: default, notEligibleToImport: 1, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1, constant: 0)))) ; guid = 2708120569957007488
^3 = gv: (name: "foo1", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 1, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 13, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^5)), refs: (^2)))) ; guid = 7682762345278052905
^4 = gv: (name: "foo4") ; guid = 11564431941544006930
^5 = gv: (name: "foo3", summaries: (function: (module: ^0, flags: (linkage: internal, visibility: default, notEligibleToImport: 1, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^4))))) ; guid = 17367728344439303071
^6 = flags: 8
^7 = blockcount: 5
我们将重点的差别进行 highlight,
| Difference | Thin LTO | Full LTO |
|---|---|---|
| Module Flags | !3 = !{i32 1, !"ThinLTO", i32 0} | |
Global Value Summary module ^0 | ^0 = module: (path: "a_lto.o", hash: (3489747275, 1762444854, 1461358598, 2667786215, 1835806708)) | ^0 = module: (path: "a_lto.o", hash: (0, 0, 0, 0, 0)) |
Global Value Summary foo2 ^1 | - notEligibleToImport: 0 - refs: (writeonly ^2) | - notEligibleToImport: 1 - refs: (^2) |
Global Value Summary i ^2 | - notEligibleToImport: 0 | notEligibleToImport: 1 |
Global Value Summary foo1 ^3 | - notEligibleToImport: 0 - refs: (readonly ^2) | - notEligibleToImport: 1 - refs: (^2) |
Global Value Summary foo3 ^5 | notEligibleToImport: 0 | notEligibleToImport: 1 |
通过 Metadata 知道,! 后面表示的是 metadata,^表示的是 global value summary。
All metadata are identified in syntax by an exclamation point (‘!’).
Compiling with ThinLTO causes the building of a compact summary of the module that is emitted into the bitcode. The summary is emitted into the LLVM assembly and identified in syntax by a caret (‘^’).
通过 Module Flags Metadata 来对 !3 = !{i32 1, !"ThinLTO", i32 0} 进行解释。module flags metadata 是一组三元组 triplets,
- The first element is a behavior flag, which specifies the behavior when two (or more) modules are merged together.
- The second element is a metadata string that is a unique ID for the metadata.
- The third element is the value of the flag.
!3 = !{i32 1, !"ThinLTO", i32 0}

ThinLTO 的值为 0, 表示非 ThinLTO,另外一个表明是否为 ThinLTO 或者 FullLTO,GLOBALVAL_SUMMARY_BLOCK 默认是 thin lto。
$ llvm-bcanalyzer -dump a_full_lto.oBlock ID #24 (FULL_LTO_GLOBALVAL_SUMMARY_BLOCK):Num Instances: 1Total Size: 789b/98.62B/24WPercent of file: 3.4924%Num SubBlocks: 0Num Abbrevs: 6Num Records: 7Percent Abbrevs: 57.1429%Record Histogram:Count # Bits b/Rec % Abv Record Kind3 218 72.7 100.00 PERMODULE1 22 BLOCK_COUNT1 22 FLAGS1 22 VERSION1 38 100.00 PERMODULE_GLOBALVAR_INIT_REFS
$ llvm-bcanalyzer -dump a_thin_lto.oBlock ID #20 (GLOBALVAL_SUMMARY_BLOCK):Num Instances: 1Total Size: 789b/98.62B/24WPercent of file: 3.4727%Num SubBlocks: 0Num Abbrevs: 6Num Records: 7Percent Abbrevs: 57.1429%Record Histogram:Count # Bits b/Rec % Abv Record Kind3 218 72.7 100.00 PERMODULE1 22 BLOCK_COUNT1 22 FLAGS1 22 VERSION1 38 100.00 PERMODULE_GLOBALVAR_INIT_REFS
在有 global value summary 的情况下,默认是 thin lto,除非 ThinLTO module metadata flag 为 0 。
/// Emit the per-module summary section alongside the rest of
/// the module's bitcode.
void ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() {// By default we compile with ThinLTO if the module has a summary, but the// client can request full LTO with a module flag.bool IsThinLTO = true;if (auto *MD =mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("ThinLTO")))IsThinLTO = MD->getZExtValue();Stream.EnterSubblock(IsThinLTO ? bitc::GLOBALVAL_SUMMARY_BLOCK_ID: bitc::FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID,4);// ...
}
RFC
https://lists.llvm.org/pipermail/llvm-dev/2015-May/085526.html
https://sites.google.com/site/llvmthinlto/
Patches
https://reviews.llvm.org/D13107?id=35761
Function Importer
https://reviews.llvm.org/D14914
https://reviews.llvm.org/D18343
llvm-opt2/llvm-opt相关
关于 SyntheticCount的讨论
- https://lists.llvm.org/pipermail/llvm-dev/2017-December/119701.html
- https://reviews.llvm.org/D43521?id=135117#inline-388028
/// Compute synthetic function entry counts.
void computeSyntheticCounts(ModuleSummaryIndex &Index);
相关术语
- BFI, block frequency inforamtion
- BPI,probability information
- CGSCC,call graph scc analysis,https://lists.llvm.org/pipermail/llvm-dev/2016-June/100792.html
相关文章:
[DRAFT] LLVM ThinLTO原理分析
我们在《论文阅读:ThinLTO: Scalable and Incremental LTO》中介绍了ThinLTO论文的主要思想,这里我们介绍下LLVM ThinLTO是如何实现的。本文主要分为如下几个部分: LLVM ThinLTO Object 含有哪些内容?LLVM ThinLTO 是如何做优化的…...
使用Gitlab构建简单流水线CI/CD
什么是Gitlab Gitlab实质上是一套DevOps工具 目前看起来,Gitlab属于是内嵌了一套CI/CD的框架,并且可以提供软件开发中的版本管理、项目管理等等其他功能。 这里需要辨别一下Gitlab和Github Gitee的区别。 GIthub大家都很熟悉了,一般大家都会…...
【AIGC核心技术剖析】用于高效 3D 内容创建生成(从单视图图像生成高质量的纹理网格)
3D 内容创建的最新进展主要利用通过分数蒸馏抽样 (SDS) 生成的基于优化的 3D 生成。尽管已经显示出有希望的结果,但这些方法通常存在每个样本优化缓慢的问题,限制了它们的实际应用。在本文中,我们提出了DreamGaussian&…...
nginx平滑升级添加echo模块、localtion配置、rewrite配置
nginx平滑升级添加echo模块、location配置、rewrite配置 文章目录 nginx平滑升级添加echo模块、location配置、rewrite配置1.环境说明:2.nginx平滑升级原理:3.平滑升级nginx,并添加echo模块3.1.查看当前nginx版本以及老版本编译参数信息3.2.下…...
系统架构师备考倒计时19天(每日知识点)
软件架构评估(ATAM) 在SAAM的基础上发展起来的,主要针对性能、实用性、安全性和可修改性,在系统开发之前,对这些质量属性进行评价和折中。ATAM方法的主要活动领域包括: 第一阶段 场景和需求收集 收集场景…...
谈谈 Redis 如何来实现分布式锁
谈谈 Redis 如何来实现分布式锁 基于 setnx 可以实现,但是不是可重入的。 基于 Hash 数据类型 Lua脚本 可以实现可重入的分布式锁。 获取锁的 Lua 脚本: 释放锁的 Lua 脚本: 但是还是存在分布式问题,比如说,一个客…...
.NET 6.0 Web API Hangfire
Hangfire 文档 Hangfire 中文文档 Hangfire GitHub使用示例源码 在线Cron表达式生成器 ● Hangfire允许您以非常简单但可靠的方式在请求管道之外启动方法调用。 这种 后台线程 中执行方法的行为称为 后台任务。 ● 它是由:客户端、作业存储、服务端 组成的。 ● Hangfire可以在…...
基于java的校园论坛系统,ssm+jsp,Mysql数据库,前台用户+后台管理,完美运行,有一万多字论文
目录 演示视频 基本介绍 论文目录 功能架构 系统截图 演示视频 基本介绍 基于java的校园论坛系统,Mysql数据库,系统整体采用ssmjsp设计,前台用户后台管理,完美运行,有一万多字论文。 用户功能: 1.系统…...
Django小白开发指南
文章目录 HTTP协议socket实现一个web服务器WSGI实现一个web服务器WSGI实现支持多URL的web服务器WSGI实现图片显示的web服务器MVC && MTV1.MVC2.MTV3.总结 一、创建Django项目1.创建项目2.创建app3.第一次django 请求 二、模板1.配置settings.py2.模板语法3.继承模板 三…...
保序回归与金融时序数据
保序回归在回归问题中的作用是通过拟合一个单调递增或递减的函数,来保持数据点的相对顺序特性。 一、保序回归的作用 主要用于以下情况: 1. 有序数据:当输入数据具有特定的顺序关系时,保序回归可以帮助保持这种顺序关系。例如&…...
基于单片机设计的家用自来水水质监测装置
一、前言 本文介绍基于单片机设计的家用自来水水质监测装置。利用STM32F103ZET6作为主控芯片,结合水质传感器和ADC模块,实现对自来水水质的检测和监测功能。通过0.96寸OLED显示屏,将采集到的水质数据以直观的方式展示给用户。 随着人们对健…...
ubuntu20.04运用startup application开机自启动python程序
运用startup application开机自启动python程序。在终端中输入gnome-session-properties,如果显示没有则先进行安装,sudo apt-get update 和sudo apt install StartupApplications(根据显示提示安装)。在显示程序中搜索startup,打开应用程序。 在程序目录…...
SpringBoot整合Caffeine实现缓存
Caffeine Caffeine是一种基于Java的高性能缓存库,它提供了可配置、快速、灵活的缓存实现。Caffeine具有以下特点: 高性能:Caffeine使用了一些优化技术,如基于链表的并发哈希表和无锁算法,以提供卓越的读写性能。容量…...
DVWA-弱会话IDS
弱会话IDS Session简介: 用户登录后,在服务器就会创建一个会话(session),叫做会话控制,接着访问页面的时候就不用登录,只需要携带Session去访问即可。 sessionID作为特定用户访问站点所需要的唯一内容。如果能够计算…...
【C++中cin、cin.get()、cin.getline()、getline() 的区别】
文章目录 引入cin基本用法输入多个变量换行符存放在缓冲区中 cin.get()基本用法重载函数换行符残留在缓冲区中 cin.getline()基本使用重载函数换行符不会残留在缓冲区中 string 流中的 getline()总结用法总结几个输入实例输入格式输入格式输入格式输入格式 输出格式 写在最后 引…...
SSH连接华为交换机慢
ssh连接交换机慢是因为交换计算密钥算法阶段默认使用安全性更高的秘钥,由于性能问题导致连接比较慢,如一台华为S5735S-L24T4S-QA2的交换机默认使用如下秘钥,安全行由高到低。 ssh server key-exchange dh_group16_sha512 dh_group15_sha512 …...
Web攻防03_MySQL注入_数据请求
文章目录 PHP-MYSQL-数据请求类型1、数字型(无符号干扰)2、字符型(有符号干扰)3、搜索型(有多符号干扰)4、框架型(有各种符号干扰) PHP-MYSQL-数据请求方法数据请求方法GET:POST:Coo…...
JS加密/解密那些必须知道的事儿
一直以来,字符串的编码问题对于新手程序员来说,或者平常不太涉猎这方面的程序员来说,是犹如灵异学一样的存在。经常会遇到莫名其妙的编码问题,导致的各种的无法理解的错误。 今天,本问就来介绍一下作者所知晓的一切…...
搭建伪分布式Hadoop
文章目录 一、Hadoop部署模式(一)独立模式(二)伪分布式模式(三)完全分布式模式 二、搭建伪分布式Hadoop(一)登录虚拟机(二)上传安装包(三…...
【C++】特殊类的设计(只在堆、栈创建对象,单例对象)
🌏博客主页: 主页 🔖系列专栏: C ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 文章目录 一、请设计一个类,只能在堆上创建对象二、 请设计一个类,只能…...
Spring Data事务管理与多租户架构:企业级数据隔离终极指南
Spring Data事务管理与多租户架构:企业级数据隔离终极指南 【免费下载链接】spring-data-examples Spring Data Example Projects 项目地址: https://gitcode.com/gh_mirrors/sp/spring-data-examples Spring Data事务管理与多租户架构是企业级应用开发中的两…...
awesome-rust数据备份恢复:灾难恢复与业务连续性
awesome-rust数据备份恢复:灾难恢复与业务连续性 数据丢失可能导致项目停滞、业务中断甚至经济损失。在开源项目管理中,建立完善的数据备份与灾难恢复机制是保障业务连续性的关键。本文将介绍如何利用awesome-rust中精选的Rust工具构建可靠的备份恢复系…...
S-UI配置文件加密终极指南:保护敏感信息的最佳实践 [特殊字符]
S-UI配置文件加密终极指南:保护敏感信息的最佳实践 🔒 S-UI是一款基于SagerNet/Sing-Box构建的高级Web面板,提供多协议支持和流量管理功能。在使用过程中,配置文件包含大量敏感信息,如API密钥、用户数据和服务器配置&…...
Qwen3.5-4B-Claude-Opus中小企业落地:低成本代码助手私有化部署
Qwen3.5-4B-Claude-Opus中小企业落地:低成本代码助手私有化部署 1. 模型概述 Qwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF是一个专为中小企业设计的轻量级AI推理模型。这个基于Qwen3.5-4B的推理蒸馏版本,特别强化了结构化分析、分步骤回答以…...
蓄电池与超级电容混合储能并网matlab/simulink仿真模型 (1)混合储能采用低通滤波...
蓄电池与超级电容混合储能并网matlab/simulink仿真模型 (1)混合储能采用低通滤波器进行功率分配,可有效抑制功率波动,并对超级电容的soc进行能量管理,soc较高时多放电,较低时少放电,soc较低时状…...
【UE6.5 C++27 适配权威指南】:20年引擎老兵亲授7步零错误迁移法(含编译器链兼容性验证清单)
第一章:UE6.5 C27 适配的战略认知与前置准备Unreal Engine 6.5 对 C27 标准的初步支持标志着引擎底层工具链的重大演进。这一适配并非简单的编译器升级,而是涉及构建系统、反射机制、蓝图互操作性及内存模型兼容性的系统性重构。开发者需摒弃“仅更新编译…...
HoRain云--Bootstrap5图像形状全攻略
🎬 HoRain 云小助手:个人主页 ⛺️生活的理想,就是为了理想的生活! ⛳️ 推荐 前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。 目录 ⛳️ 推荐 …...
GLM-4.1V-9B-Base效果展示:低质量压缩图(微信发送后)识别鲁棒性
GLM-4.1V-9B-Base效果展示:低质量压缩图(微信发送后)识别鲁棒性 1. 模型介绍 GLM-4.1V-9B-Base是智谱开源的视觉多模态理解模型,专门针对图像内容识别、场景描述、目标问答和中文视觉理解任务进行了优化。这个9B参数的模型在保持…...
WinForms界面美化:用SunnyUI的UILight控件做个状态指示灯(附完整代码)
WinForms界面美化实战:用SunnyUI的UILight控件打造专业状态指示灯 在桌面应用开发中,状态指示是用户界面不可或缺的元素。传统的WinForms控件往往显得单调乏味,而SunnyUI的UILight控件为我们提供了一种简单高效的解决方案。这个圆形指示灯控…...
javaee-网络原理2
⽹络原理-TCP/IP ①应用层:规则 → 格式 → 实际用途讲解↓ (1)定义应用之间怎么通信比如:浏览器怎么请求网页、APP 怎么跟服务器发数据。 谁先说话什么时候发请求什么时候回响应出现错误怎么办一次会话怎么开始、怎么结束 比如 HTTP 协议就明确规定&…...
