如何真正区分编译时间和运行时间信息?

How really distinguish compile time and run time information?

这个问题可能看起来很宽泛,但事实并非如此。
更具体地说,如果我们使用模板,我们如何区分什么是编译时间信息和运行时间信息?

我在观看有关优化和 HFT 系统的 CppCon 视频时偶然发现了这个示例。如果您不熟悉高频交易系统,并且洞察力是他们真的试图从基准中挤出最后几纳秒,通常违反经典的 OOD 原则以获得性能。

我想详细说明的例子如下:

template<Side T>
void Strategy<T>::run()
{
    const float orderPrice = calcPrice(fairValue, credit);
    //....
    sendOrder(orderPrice);
}

template<>
float Strategy<Side::buy>::calcPrice(float value, float credit)
{
    return value - credit;
}

template<>
float Strategy<Side::sell>::calcPrice(flaot value, float credit)
{
    return value + credit;
}

根据作者的说法,此示例说明了避免 运行 时间 if-else 分支的经典方法。
但是在编译的时候,我们真的不知道我们是要买还是要细胞对吧?根据市场走势在 运行 时间内做出决定。
有人能解释一下 运行 时间和编译时间之间的 link 吗,具体来说,我们如何根据 运行 时间信息创建编译时间分支?

Strategy 专门用于 buysell 情况。因此,一旦您决定是买入还是卖出,您就可以使用其中之一,并且 class 方法中不再有分支。它实质上是生成 2 个代码路径,您可以使用一个分支选择其中一个:

if (buying) {
  Strategy<Side::buy> strat;
  strat.run();
} else {
  Strategy<Side::sell> strat;
  strat.run();
}

对于您展示的这个简化示例,它可能不会产生太大影响。

但是对于 HFT,您有一项任务是卖出或买入,并且该任务的所有操作都应以尽可能短的延迟完成。并且您想为这些任务中的每一个都获得最佳优化。

这两个代码流在很多地方可能相似。但是如果你有一个更大的代码流,你会有多个 if 条件来检查你是卖还是买,那么你有两种可能性:

  1. 您可以希望编译器的优化器将创建两个不同的代码流,一个用于销售,一个用于购买,并减少分支可能性和延迟。一个好的编译器可能确实能够做到这一点,但并不能保证做到这一点,并且对该代码进行一些更改可能会导致编译器出于某种原因停止进行该优化。

  2. 如图所示创建一个使用模板的结构。这将保证您在决定要执行哪些任务时只对 sell 进行一次检查,对 buy 进行一次检查。而且您不需要依赖编译器为您进行优化,您仍然可以防止源代码中的代码重复。

2. 具有额外的好处,即编译器可能能够进行更进一步的优化,如内联、指令重新排序……因为您删除了对 [=12= 的任何额外检查]/sell 在你做了一次检查之后。而减少分支的可能性也可以帮助CPU.

的分支预测

一般原则你是对的:在编译时,你不知道是卖还是买。这需要一个 if 语句。

但是,当控制 buy/sell 的单个 if 语句控制两个大块相似代码时,这里使用的技术仍然有用。您现在可以显式地写出这些块(代码重复),只写一次,但对两个块分叉的所有部分重复 if 语句(慢),或者使用模板让编译器为您生成两个代码块.

我在自己的代码库中有一个更好的例子,我在外面使用一个开关来选择 7 个模板实例之一。每个模板实例化都是数百个元素的循环,外面的开关意味着我没有循环内的开关。当然,我有 7 个模板实例化,但 RAM 很便宜。