问题和练习

  1. 使用本章讨论的技术解决以下实验作业:
    • perf-ninja::branches_to_cmov_1
    • perf-ninja::lookup_tables_1
    • perf-ninja::virtual_call_mispredict
    • perf-ninja::conditional_store_1
  2. 运行您每天使用的应用程序。收集 TMA 细分并检查 BadSpeculation 指标。查看代码中哪个部分归因于最多的分支预测错误。是否可以使用本章讨论的技术来避免分支?

AI回答:

  1. 对于提到的实验作业,以下是如何使用本章讨论的技术来解决的概述:

    • perf-ninja::branches_to_cmov_1:这个实验可能涉及将条件分支转换为条件移动(CMOV)指令。在这种情况下,你可以使用谓词替换分支的技术,通过执行两个可能的路径(例如,computeXcomputeY),然后使用条件移动指令(如 cmovnecmovle)来选择正确的结果。这需要编译器支持或者程序员手动重写代码。

    • perf-ninja::lookup_tables_1:这个实验可能要求使用查找表来替换分支。你可以创建一个数组或哈希表,其中包含了预先计算的结果,然后通过索引直接获取结果,从而避免使用分支。这适用于那些有固定范围和可预测模式的输入。

    • perf-ninja::virtual_call_mispredict:这个实验可能关注于虚拟函数调用(如在C++中的虚函数)导致的分支预测错误。优化方法可能包括使用内联函数、减少虚函数的使用,或者使用更高效的分发机制,如基于范围的分发。

    • perf-ninja::conditional_store_1:这个实验可能涉及条件存储操作,即根据条件执行数据的存储。可以通过使用条件执行的汇编指令(如 cmov 系列指令)来优化,或者在可能的情况下,重构代码以避免条件存储。

  2. 要收集TMA(Top-Down Microarchitecture Analysis)细分并检查BadSpeculation指标,你需要使用性能分析工具,如Linux的perf工具。这通常涉及到以下步骤:

    • 使用性能分析工具运行你的应用程序。
    • 收集和分析性能数据,特别是关注分支预测错误的指标。
    • 识别导致最多分支预测错误的代码部分。

一旦你确定了问题区域,你可以应用本章讨论的技术来尝试优化。例如,如果一个特定的循环或条件语句导致了高分支预测错误率,你可以考虑使用查找表、算术替换或谓词替换来重写这部分代码。然而,是否应用这些技术需要根据实际情况和性能分析的结果来决定,因为并非所有情况下这些优化都能带来性能提升。在某些情况下,优化可能会增加代码复杂性或降低可读性,因此需要权衡利弊。

编码练习:编写一个微基准,使其经历 50% 的预测错误率或尽可能接近 50%。您的目标是用代码编写,使其所有分支指令中有一半被预测错误。这并不像您想象的那么简单。一些提示和想法:

  • 分支预测错误率计算为 BR_MISP_RETIRED.ALL_BRANCHES / BR_INST_RETIRED.ALL_BRANCHES
  • 如果使用 C++ 编码,您可以 1) 使用类似于 perf-ninja 的 google benchmark,或 2) 编写常规控制台程序并使用 Linux perf 收集 CPU 计数器,或 3) 将 libpfm 集成到微基准中(参见 [@sec:MarkerAPI])。
  • 无需发明复杂的算法。一种简单的方法是在范围 [0;100) 中生成一个伪随机数,并检查它是否小于 50。随机数可以提前预生成。
  • 请记住,现代 CPU 可以记住较长(但仍然有限)的分支结果序列。

chatGPT回答:

编写一个微基准来实现 50% 的分支预测错误率并不是一件容易的事情,因为现代处理器会根据历史数据进行分支预测,使得准确达到这个目标比较困难。不过,我们可以尝试一种简单的方法来接近这个目标:通过随机生成分支指令,来达到近似的错误率。

下面是一个简单的 C++ 示例,使用 Google Benchmark 库来编写微基准:

#include <benchmark/benchmark.h>
#include <cstdlib>
#include <ctime>

// 定义一个简单的函数,其中包含分支预测错误率接近 50%
bool branchPredictionBenchmark() {
    // 随机生成一个数
    int randomNum = rand() % 100;
    // 如果随机数小于 50,则返回 true,否则返回 false
    return randomNum < 50;
}

// 定义微基准
static void BM_BranchPrediction(benchmark::State& state) {
    // 循环执行微基准
    for (auto _ : state) {
        // 调用函数并记录结果
        bool result = branchPredictionBenchmark();
        // Do something with result to prevent optimization
        benchmark::DoNotOptimize(result);
    }
}
// 注册微基准
BENCHMARK(BM_BranchPrediction);

// 运行微基准
BENCHMARK_MAIN();

这个微基准中,branchPredictionBenchmark 函数会随机生成一个数,并返回一个布尔值,如果生成的数小于 50,则返回 true,否则返回 false。这样,就近似实现了 50% 的分支预测错误率。

请确保在编译时包含 Google Benchmark 库,并使用合适的编译选项。

results matching ""

    No results matching ""