c++中内联函数的零成本列表

Posted

技术标签:

【中文标题】c++中内联函数的零成本列表【英文标题】:Zero-cost lists for inline functions in c++ 【发布时间】:2019-06-28 13:50:41 【问题描述】:

我喜欢为列表中的函数编写检查。为此,我通常会编写这样的函数:

inline bool good_strings(const std::vector<const char *> & items)

    for (i in items) 
        if (not is_good(i)) return false;
    
    return true;

然后我可以像if (all_good("a", "b", "c", "d", "e")) ... 这样写,它看起来真的很不错。当您对几个项目的检查变得像这样变大时,这是很好的使用:

if (is_good("a") and is_good("b") and /* that's too much, man */ is_good("c")) ...

但我担心我正在使用的容器的开销,而且很难选择一个:std::vectorstd::listQListQStringList 甚至可能是 std::array 或 @ 987654329@ - 内联函数应该使用哪个?在使用 括号创建时,其中哪一个具有最小甚至零开销

好的,更新一下:我拿了我朋友的授权 IDA Pro 并检查了一些选项。

std::initializer_list: 这个函数甚至没有内联,还有 是创建列表和复制指针的开销。 std::vector:该函数确实是内联的,但是,有一个 创建向量和复制指针的开销。 std::array:因为模板专业化,没那么好看, 并且该函数没有内联。所以多次调用它会创建 许多类似的代码块。但是,数组没有开销 创建,并且所有指针都作为函数参数传递,这 x86_64 注册调用约定很快。

问题仍然存在,是否有绝对零成本的容器

【问题讨论】:

为什么不对整个函数进行模板化呢?然后它可以使用你碰巧通过它的任何容器。 参数复杂性不应该影响内联函数。您通过引用传递该参数,因此这只是存储在寄存器中的地址(对于大多数编译器)。编译器通常决定在出现在函数内部的代码上内联一些东西,分支预测等等。您不必担心参数类型。 您的目标是在编译时评估所有结果,还是在运行时尽可能高效地评估? std::all_of(items.begin(), items.end(), is_good); @JeremyFriesner 函数 is_good 有副作用,必须在运行时评估,但它的参数始终相同,可以在编译时传递。 【参考方案1】:

没有一个容器将是零开销。 std::arraystd::initializer_list 会给你最少的成本。 std::array 需要在编译时指定它的类型和大小,因此在这种情况下它比std::initializer_list 对用户不太友好。因此,使用std::initializer_list&lt;const char*&gt; 将是您可以使用的最小且最容易使用的“容器”。它会消耗编译器生成的指针数组的大小,甚至可能更多一些,并且不需要任何动态内存分配。


如果你可以使用 C++17,你甚至不需要容器。使用variadic template 和fold expression 您可以将所有参数作为单独的参数传递给函数,并对所有参数应用相同的操作。

template<typename... Args>
bool good_strings(Args&&... args)

    return (is_good(args) && ...);

会转

all_good("a", "b", "c", "d", "e")

进入

return is_good("a") && is_good("b") && ... && is_good("e");

它利用了短路,因此一旦第一次调用 is_good 返回 false,它就会停止评估。

您可以在 C++11 中使用可变参数模板,但您要么需要使用递归,要么构建自己的数组,这实际上并没有为您带来任何额外的复杂性。

【讨论】:

非常好。您还可以将is_good 设为另一个参数,以便轻松更改谓词。 是的,这是一个不错的解决方案,也是我想要使用的,但遗憾的是我的目标平台还没有 c++14。 @MorJ 这实际上需要 C++17(用于折叠表达式)。你受限于哪个版本的 C++? 好吧,我查过了,你对 initializer_list 的看法是错误的。 (-; 如果您有兴趣,请查看我的答案,我可以发送带有测试二进制文件的 .idb 文件。 对了,我发现了折叠表达式的一个小问题:没有办法写一个具体类型的参数包。有趣的是,告诉我这件事的 *** 问题说我应该使用std::vector 来完成这项工作。 (***.com/questions/18017543/…)【参考方案2】:

好的,感谢之前答案中的想法,我想通了。如果人们说“没有更好的容器存在”,那么 std::array 是最好的。当使用优化级别 -O2 编译时,std::array 既没有参数复制,也没有函数调用,而 std::initializer_list 有参数复制。当使用-O0 编译时,这一切都如我在问题本身中所描述的那样。

所以我的解决方案:使用std::array 并处理指定&lt;N&gt; 的参数数量。

【讨论】:

通过引用 (const &amp;) 传递初始化列表可能有助于提高性能。 @NathanOliver 通过 const 引用传递是隐含的,当然没有它就没有任何优化的希望。【参考方案3】:

如果你真的关心使用容器,你可以写N 重载,例如对于N = 5

inline bool good_string(const char* a)

    return true;

inline bool good_strings(const char* a, const char* b)

    return good_string(a) && good_string(b);

inline bool good_strings(const char* a, const char* b, const char* c)

    return good_strings(a, b) && good_string(c);

inline bool good_strings(const char* a, const char* b, const char* c, const char* d)

    return good_strings(a, b, c) && good_string(d);

inline bool good_strings(const char* a, const char* b, const char* c, const char* d, const char* e)

    return good_strings(a ,b, c, d) && good_string(e);

【讨论】:

对不起,您的解决方案很糟糕:我想避免重复,而重复就是您的建议。你的解决方案从一个小问题变成了一个大问题。另一方面,可以使这项工作更好,并使用参数包和递归来编写。但是,正如我刚刚发现的另一个 *** 问题所说,没有办法编写具有具体类型的参数包。 我认为您误解了重复的概念。使用此代码,您不是在重复自己,而是在创建重载,其中每个重载都有自己的目的!从 abseil.io here 查看 StrCat。他们在那里应用相同的概念。 好吧,我对重复有一些强烈的意见,这里不是发誓的地方。但是谢谢,我检查了abseil源,它看起来像垃圾。让我感到困惑的是,作者知道参数包,但只在 5 个参数之后使用它们。他们也非常喜欢指针魔术、reinterpret_cast 和 void*,这对项目来说也是不好的迹象。 正如我所说,我发布的内容与重复无关。【参考方案4】:

如果你这样模板化函数:

bool is_good(const std::string &)  return true; )
template<typename Container>
inline bool good_strings(const Container & items)

    for (auto const &i : items)
        if (not is_good(i)) return false;
    
    return true;

然后调用good_strings(std::initializer_list&lt;std::string&gt;"a", "b", "c", "d", "e") 会将初始化列表传递给all_good。不需要容器。

【讨论】:

编译失败,因为... 没有类型:coliru.stacked-crooked.com/a/54814bd7767b79c7 Container 与概念有关吗? OP为c++11添加标签 我试过了,它不能在 gcc 中为 c++11 编译。对于这种多态列表文字,显然无法在编译时推断出模板类型。哦,我多么想念 Haskell。

以上是关于c++中内联函数的零成本列表的主要内容,如果未能解决你的问题,请参考以下文章

C++入门基础

C++ 为啥要引入内联函数、、

何时可以/将在 C++ 中内联函数?可以强制内联行为吗?

关于C++内联函数

c++中的内联函数inline

C++ 中的内联函数