gpt4 book ai didi

c++ - 在 C++ 中样板化 "cold/never_inline"错误处理技术的最佳方法是什么?

转载 作者:行者123 更新时间:2023-12-01 14:13:11 26 4
gpt4 key购买 nike

this文章中,描述了一种在 gcc 中移动错误代码的技术,以帮助尽可能地优化热路径的大小。这方面的一个例子是:

#define unlikely(x)  __builtin_expect (!!(x), 0)

bool testForTerriblyUnlikelyEdgeCase() {
//test for error condition here
}

void example() {
if (unlikely(testForTerriblyUnlikelyEdgeCase())) {
[&]() __attribute__((noinline,cold)) {
//error handling code here
}();
}
}
这是一项很棒的技术,但需要大量的样板文件。包装它以尽可能减少样板的最佳方法是什么?理想情况下 C++14 兼容允许特定于 gcc 的功能。
额外问题: if 语句中的不太可能(...) 是否多余,因为 lambda 被显式标记为冷?

最佳答案

想到了两种方法:

  • 函数包装器方法,以及
  • 基于宏的方法

  • 函数包装器
    最好的设计是将此功能包装到封装属性和处理的函数中。为此,您需要传递一个要作为冷处理程序调用的回调(在本例中为 lambda)。它看起来很简单(使用 C++11 属性而不是 __attribute__ 语法):
    template <typename Fn>
    [[gnu::cold]] [[gnu::noinline]]
    void cold_path(Fn&& fn)
    {
    std::forward<Fn>(fn)();
    }
    您还可以扩展此解决方案以利用要测试的条件,例如:
    template <typename Expr, typename Fn>
    void cold_path_if(Expr&& expr, Fn&& fn)
    {
    if (unlikely(std::forward<Expr>(expr))) {
    cold_path(std::forward<Fn>(fn));
    }
    }
    把它们放在一起,你有:
    void example() {
    cold_path_if(testForTerriblyUnlikelyEdgeCase(), [&]{
    std::cerr << "Oh no, something went wrong" << std::endl;
    std::abort();
    });
    }
    这是它在 Compiler Explorer 上的样子.
    基于宏观的方法
    如果不希望传递显式 lambda,那么想到的唯一替代方案是基于宏的解决方案,为您创建 lambda。为此,您需要一个可以立即调用 lambda 的实用程序,以便您只需定义函数的主体:
    // A type implicitly convertible to any function type, used to make the 
    // macro below not require '()' to invoke the lambda
    namespace detail {
    class invoker
    {
    public:
    template <typename Fn>
    /* IMPLICIT */ invoker(Fn&& fn){ fn(); }
    };
    }
    这是作为一个可从函数隐式转换的类完成的,因此您可以编写类似 detail::invoker foo = []{ ... } 的代码。 .然后我们想把定义的第一部分带到捕获,并将它包装成一个宏。
    为此,我们需要一个唯一的变量名称,否则如果多个处理程序在同一范围内,我们可能会隐藏或重新定义变量。为了解决这个问题,我附加了 __COUNTER__名称的宏;但这是非标准的:
    #define COLD_HANDLER ::detail::invoker some_unique_name ## __COUNTER__ = [&]() __attribute__((noinline,cold))
    这只是将自动调用程序的创建包装起来,直到定义 lambda 为止,因此您需要做的就是编写 COLD_HANDLER { ... }使用现在看起来像:
    void example() {
    if (unlikely(testForTerriblyUnlikelyEdgeCase())) {
    COLD_HANDLER {
    //error handling code here
    };
    }
    }
    这是 compiler explorer 上的示例

    两种方法都会产生 identical assembly直接使用 lambda,只有标签和名称不同。 (注意:此比较使用 std::fprintf 而不是 stds::cerr,因此组件更小且更易于比较)

    Bonus Question: Is the unlikely(...) in the if statement redundant since the lambda is explicitly marked cold?


    阅读 GCC 的文档 __attribute__((cold))似乎表明所有导致冷函数的分支都被标记为 unlikely ,这应该使用 unlikely宏是多余的和不必要的——尽管拥有它应该不会有什么坏处。
    来自 the attributes page :

    The cold attribute is used to inform the compiler that a function is unlikely executed. The function is optimized for size rather than speed and on many targets it is placed into special subsection of the text section so all cold functions appears close together improving code locality of non-cold parts of program. The paths leading to call of cold functions within code are marked as unlikely by the branch prediction mechanism. It is thus useful to mark functions used to handle unlikely conditions, such as perror, as cold to improve optimization of hot functions that do call marked functions in rare occasions.


    强调我的。

    关于c++ - 在 C++ 中样板化 "cold/never_inline"错误处理技术的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62761997/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com