在 Linux 内核中使用 Propeller

当使用 Clang 编译器时,这使内核支持 Propeller 构建。Propeller 是一种配置文件引导优化(PGO)方法,用于优化二进制可执行文件。与 AutoFDO 类似,它利用硬件采样来收集有关二进制文件中不同代码路径的执行频率的信息。与 AutoFDO 不同,此信息在链接阶段之前被用来优化(包括)函数内部和跨函数的块布局。

关于采用 Propeller 优化的一些重要说明

  1. 虽然它可以作为独立的优化步骤使用,但强烈建议在 AutoFDO、AutoFDO+ThinLTO 或 Instrument FDO 的基础上应用 Propeller。本文档的其余部分假设这种范例。

  2. Propeller 在 AutoFDO/AutoFDO+ThinLTO/iFDO 之上使用另一轮分析。整个构建过程涉及“build-afdo - train-afdo - build-propeller - train-propeller - build-optimized”。

  3. Propeller 要求 LLVM 19 版本或更高版本用于 Clang/Clang++ 和链接器 (ld.lld)。

  4. 除了 LLVM 工具链之外,Propeller 还需要一个配置文件转换工具:https://github.com/google/autofdo,版本为 v0.30.1 之后:https://github.com/google/autofdo/releases/tag/v0.30.1

Propeller 优化过程涉及以下步骤

  1. 初始构建:像往常一样构建 AutoFDO 或 AutoFDO+ThinLTO 二进制文件,但使用一组编译时/链接时标志,以便在内核二进制文件中创建一个特殊的元数据部分。特殊部分仅供分析工具使用,它不是运行时镜像的一部分,也不会更改内核运行时文本段。

  2. 分析:然后运行上述内核以及具有代表性的工作负载以收集执行频率数据。此数据通过 perf 使用硬件采样来收集。Propeller 在支持高级 PMU 功能(如 Intel 机器上的 LBR)的平台上最有效。此步骤与为 AutoFDO 分析内核的步骤相同(确切的 perf 参数可能不同)。

  3. Propeller 配置文件生成:Perf 输出文件通过脱机工具转换为一对 Propeller 配置文件。

  4. 优化构建:像往常一样构建 AutoFDO 或 AutoFDO+ThinLTO 优化的二进制文件,但使用一个编译时/链接时标志来获取 Propeller 编译时和链接时配置文件。此构建步骤使用 3 个配置文件 - AutoFDO 配置文件、Propeller 编译时配置文件和 Propeller 链接时配置文件。

  5. 部署:优化的内核二进制文件被部署并用于生产环境,从而提供更高的性能和更低的延迟。

准备工作

使用以下配置内核

CONFIG_AUTOFDO_CLANG=y
CONFIG_PROPELLER_CLANG=y

自定义

默认的 CONFIG_PROPELLER_CLANG 设置涵盖 Propeller 构建的内核空间对象。但是,可以通过向相应的内核 Makefile 添加类似于以下行的行来为单个文件和目录启用或禁用 Propeller 构建

  • 用于启用单个文件(例如 foo.o)

    PROPELLER_PROFILE_foo.o := y
    
  • 用于启用一个目录中的所有文件

    PROPELLER_PROFILE := y
    
  • 用于禁用一个文件

    PROPELLER_PROFILE_foo.o := n
    
  • 用于禁用一个目录中的所有文件

    PROPELLER__PROFILE := n
    

工作流程

以下是构建 AutoFDO+Propeller 内核的工作流程示例

  1. 假设已按照 AutoFDO 文档中的说明收集了 AutoFDO 配置文件,请在主机上使用 AutoFDO 和 Propeller 构建配置构建内核

    CONFIG_AUTOFDO_CLANG=y
    CONFIG_PROPELLER_CLANG=y
    

    并且

    $ make LLVM=1 CLANG_AUTOFDO_PROFILE=<autofdo-profile-name>
    
  2. 在测试机上安装内核。

  3. 运行负载测试。perf 中的 “-c” 选项指定采样事件周期。我们建议为此使用合适的质数,如 500009。

    • 对于 Intel 平台

      $ perf record -e BR_INST_RETIRED.NEAR_TAKEN:k -a -N -b -c <count> -o <perf_file> -- <loadtest>
      
    • 对于 AMD 平台

      $ perf record --pfm-event RETIRED_TAKEN_BRANCH_INSTRUCTIONS:k -a -N -b -c <count> -o <perf_file> -- <loadtest>
      

    请注意,您可以重复上述步骤来收集多个 <perf_file>。

  4. (可选)将原始 perf 文件下载到主机。

  5. 使用 create_llvm_prof 工具 (https://github.com/google/autofdo) 来生成 Propeller 配置文件。

    $ create_llvm_prof --binary=<vmlinux> --profile=<perf_file>
                       --format=propeller --propeller_output_module_name
                       --out=<propeller_profile_prefix>_cc_profile.txt
                       --propeller_symorder=<propeller_profile_prefix>_ld_profile.txt
    

    “<propeller_profile_prefix>” 可以是诸如“/home/user/dir/any_string”之类的东西。

    此命令生成一对 Propeller 配置文件:“<propeller_profile_prefix>_cc_profile.txt” 和 “<propeller_profile_prefix>_ld_profile.txt”。

    如果在上一步中收集了多个 perf_file,则可以创建一个临时列表文件 “<perf_file_list>”,其中每行包含一个 perf 文件名,然后运行

    $ create_llvm_prof --binary=<vmlinux> --profile=@<perf_file_list>
                       --format=propeller --propeller_output_module_name
                       --out=<propeller_profile_prefix>_cc_profile.txt
                       --propeller_symorder=<propeller_profile_prefix>_ld_profile.txt
    
  6. 使用 AutoFDO 和 Propeller 配置文件重新构建内核。

    CONFIG_AUTOFDO_CLANG=y
    CONFIG_PROPELLER_CLANG=y
    

    并且

    $ make LLVM=1 CLANG_AUTOFDO_PROFILE=<profile_file> CLANG_PROPELLER_PROFILE_PREFIX=<propeller_profile_prefix>