[LLVM] 分支概率 Branch Probability

Branch Probability

我们知道程序中的分支一般是有冷热之分的, 如果能够准确地预估、计算出分支的概率,那么对程序的优化是十分有益的。典型的优化如 据此调整程序的layout来降低分支预测的出错率,提高cache命中率等。

if ... else ...
for ... end for ...
switch ... case ... default ...

此外,在LLVM中,程序中代码块的frequency也主要是根据 Branch Probability 来计算的,精确的frequency对 寄存器分配(spill的抉择)等 优化也至关重要。

Branch Probability 的获取
分支概率的获取主要分静态和动态两种
静态主要是根据 关键字(hot/cold) 或者 分支的场景 来计算或预估。

declare void @trap() cold noreturn nounwind
declare void @bar() hot noreturn nounwind
while (i <= 0) {...}

动态则一般以FDO (feedback optimization)的形式来获取和使用,比如PGO (profile generate optimization),一般是先编译出能够收集分支信息的执行文件,然后执行、训练数据集,生成分支执行信息。最后依据这些生成的分支执行信息 重新编译、执行原程序。

LLVM中Branch Probability的计算

LLVM 中对分支概率的计算主要 在 BranchProbabilityInfo::calculate 函数 中完成。

lib/Analysis/BranchProbabilityInfo.cpp
1217 void BranchProbabilityInfo::calculate(const Function &F, const LoopInfo &LoopI,
1218                                       const TargetLibraryInfo *TLI,
1219                                       DominatorTree *DT,
1220                                       PostDominatorTree *PDT) {
       // 该函数主要根据一些已知的静态信息来 粗略地 计算,传递 分支概率。
       // 这些已知的静态信息主要有 'unreachable', 'noreturn', 'cold', 'unwind' blocks等。
1244   computeEestimateBlockWeight(F, DT, PDT);

       // 倒序对 有条件分支的 BB进行遍历
1248   for (const auto *BB : post_order(&F.getEntryBlock())) {
         // FDO的分支运行信息一般都保存在Metadata中,因此优先根据FDO的分支信息(如果有的话)来建立分支概率。
         // 可以参考我的另一篇文章:《LLVM 如何利用 Profile Guided Optimization (PGO)信息》
1254     if (calcMetadataWeights(BB))
1255       continue;
         下面四个可以理解为根据分支的特定场景、模型来估计分支的概率
         // 该部分主要对loop场景进行预估,比如回边的概率一般要比退出边的概率高很多。
         //    block BB2.
         //          |
         //          V
         //         BB1<-+
         //          |   |
         //          |   | (Weight = 124)
         //          V   |
         //         BB2--+
         //          |
         //          | (Weight = 4)
         //          V
         //         BB3
         //
         // Probability of the edge BB2->BB1 = 124 / (124 + 4) = 0.96875
         // Probability of the edge BB2->BB3 = 4 / (124 + 4) = 0.03125
1256     if (calcEstimatedHeuristics(BB))
1257       continue;
         // 该部分主要对指针(2个)场景进行估计,经典的比如 “if(ptr != null)”我们认为总是大概率为真的。
         // “if(ptr != null)”/“if(ptr == null)” 为真的概率 37.50% 假的概率 62.50%
         // 在函数实现中,这些概率主要是通过 查表 来确定的,其它函数也类似。PointerTable表:
         // {ICmpInst::ICMP_NE, {PtrTakenProb, PtrUntakenProb}}, /// p != q -> Likely
         // {ICmpInst::ICMP_EQ, {PtrUntakenProb, PtrTakenProb}}, /// p == q -> Unlikely
1258     if (calcPointerHeuristics(BB))
1259       continue;
         // 该部分主要针对 与常数0,1,-1的比较场景来进行 概率预估。具体见 表:
         // (目前 Unlikely 12/32 (37.50%); Likely:20/32 (62.50%))
         // {CmpInst::ICMP_EQ, {ZeroUntakenProb, ZeroTakenProb}},  /// X == 0 -> Unlikely
         // {CmpInst::ICMP_NE, {ZeroTakenProb, ZeroUntakenProb}},  /// X != 0 -> Likely
         // {CmpInst::ICMP_SLT, {ZeroUntakenProb, ZeroTakenProb}}, /// X < 0  -> Unlikely
         // {CmpInst::ICMP_SGT, {ZeroTakenProb, ZeroUntakenProb}}, /// X > 0  -> Likely
         // {CmpInst::ICMP_SLT, {ZeroUntakenProb, ZeroTakenProb}}, /// X < 1 -> Unlikely
         // {CmpInst::ICMP_EQ, {ZeroUntakenProb, ZeroTakenProb}},  /// X == -1 -> Unlikely
         // {CmpInst::ICMP_NE, {ZeroTakenProb, ZeroUntakenProb}},  /// X != -1 -> Likely
         // {CmpInst::ICMP_SGT, {ZeroTakenProb, ZeroUntakenProb}}, /// X >= -1  -> Likely
         // 此外,该函数还对 比较对象 做了区分,比如对“X == 0”正常来说是Unlikely的,但如果X是
         // strcmp等一些标准函数的返回值,则认为等于0(字符串相等)的概率大。
1260     if (calcZeroHeuristics(BB, TLI))
1261       continue;
         // 该部分主要针对 浮点数的比较场景 来预估概率:
         // f1 == f2 -> Unlikely  (目前 Unlikely 12/32 (37.50%); Likely:20/32 (62.50%))
         // f1 != f2 -> Likely
         // {FCmpInst::FCMP_ORD, {FPOrdTakenProb, FPOrdUntakenProb}}, /// !isnan -> Likely
         // {FCmpInst::FCMP_UNO, {FPOrdUntakenProb, FPOrdTakenProb}}, /// isnan -> Unlikely
1262     if (calcFloatingPointHeuristics(BB))
1263       continue;
1264   }
...}
要成功地进行Xilinx Zynq-7000 SoC的集成开发,你将需要熟悉TLZ7xH-EVM开发板的硬件特性以及相应的软件编程。在此,我们推荐参考以下资源《创龙TLZ7xH-EVM开发板:Xilinx Zynq-7000双核Cortex-A9+Kintex-7》,这将为你的项目提供详尽的支持。 参考资源链接:[创龙TLZ7xH-EVM开发板:Xilinx Zynq-7000双核Cortex-A9+Kintex-7](https://wenku.youkuaiyun.com/doc/80nyorov3y) 首先,在硬件编程方面,你需要了解开发板的硬件架构和资源。TLZ7xH-EVM开发板集成了双核ARM Cortex-A9处理器和Kintex-7 FPGA。你应该首先阅读Zynq-7000开发板规格书,了解各个硬件接口和信号引脚的详细信息。根据你的项目需求,进行硬件资源配置,包括配置处理器的时钟频率、电源管理、存储接口以及外设接口等。 其次,在软件编程方面,Xilinx提供了Vivado和SDK套件,用于硬件逻辑设计和软件应用开发。在Vivado中,你需要完成硬件平台的设计和生成,包括创建项目、综合、实现和生成比特流文件。完成硬件设计后,你可以通过Xilinx SDK进行软件编程,创建应用程序和驱动,以与硬件平台交互。编写代码时,你需要参考开发板提供的Demo程序,这些示例程序展示了如何加载和运行用户代码。 确保你具备相关的硬件编程经验,以及掌握至少一种用于嵌入式开发的编程语言,如C/C++。在软件开发过程中,你还需要了解操作系统的选择和配置,比如使用PetaLinux等。 集成开发成功的关键在于硬件和软件的紧密配合,这通常需要进行多次迭代和调试。使用TLZ7xH-EVM开发板上的调试接口,比如JTAG和串口,进行代码调试和性能分析。 在开发过程中,不妨利用创龙科技提供的技术支持和服务,及时解决开发中遇到的问题。此外,你可以利用公司提供的增值服务平台,如定制化开发、培训等,进一步提升开发效率和产品品。 综上所述,通过阅读相关规格书,使用Vivado和SDK进行硬件设计和软件编程,结合创龙科技的技术支持,你将能够高效地完成Zynq-7000 SoC的集成开发任务。对于那些希望深入学习和探索更多高级功能和技巧的读者,我们再次推荐《创龙TLZ7xH-EVM开发板:Xilinx Zynq-7000双核Cortex-A9+Kintex-7》,这份资料不仅帮助你入门,还将引导你掌握更深层次的知识。 参考资源链接:[创龙TLZ7xH-EVM开发板:Xilinx Zynq-7000双核Cortex-A9+Kintex-7](https://wenku.youkuaiyun.com/doc/80nyorov3y)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值