`static` 的使用如何影响我的代码速度?
How does the use of `static` affect the speed of my code?
我正在在线解决一个练习,有一次我需要从字符串的开头和结尾删除“”。这是我的代码:
void static inline process_value(std::string &value) {
if (value.back() !='>') {
value = value.substr(1, value.size()-2);
}
}
从此基准循环调用:
static void UsingStatic(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
std::string valor("\"Hola\"");
process_valueS(valor);
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(valor);
}
}
只是因为好奇我did a benchmark.
- 编译器: Clang-9.0
- std:c++20
- 优化:O3
- STL: libstdc++(GNU)
我决定从 process_value
中删除 static
,使 void inline process_value
保持不变。令我惊讶的是它变慢了。
我认为static 仅表示该函数仅用于文件。但是 here 它说“ 'static' 意味着如果可能,编译器应该内联该函数”。但是在那种情况下,当我删除静态时,我认为结果不应该改变。现在我很困惑,除了将函数分隔为单个 .cpp
之外,static 还做了什么其他事情,这对性能有何影响?
QuickBench 上的反汇编显示 NoUsingStatic
循环实际上调用 process_value
而不是内联它,尽管 inline
关键字使编译器这样做是合法的。但是 UsingStatic
会 内联对 process_valueS
的调用。编译器决策的差异大概解释了性能差异,但是 为什么 clang 选择不内联一个声明为 void inline process_value(std::string &value){ ... }
?
的简单函数
编辑:由于问题不够清楚而被关闭,我删除了与问题无关的部分。但如果我遗漏了一些信息,请在评论中告诉我
Clang 使用基于成本的函数决定是否内联函数。这个成本受很多因素的影响。它受 static
.
影响
幸运的是,clang 有一个输出,我们可以在其中观察到这一点。看看这个神栓 link:
void call();
inline void a() {
call();
}
static inline void b() {
call();
}
void foo() {
a();
b();
}
在这个小例子中,a()
和b()
是一样的,唯一的例外是b()
是静态的。
如果你将鼠标移动到 godbolt 上的调用 a()
或 b()
上(在 OptViewer
window 中),你可以阅读:
a()
:成本=0,阈值=487
b()
:成本=-15000,阈值=487
(如果成本低于阈值,clang 将内联调用。)
clang 使 b()
成本低得多,因为它是静态的。似乎 clang 只会为静态函数减少一次 -15000 的成本。如果 b()
被多次调用,所有 b()
的成本将为零,除了一个。
这是您的案例的编号,link:
process_value():
cost=400, threshold=325 -> 刚刚超过阈值,不会被内联
process_valueS():
:成本=-14600,阈值=325 -> 可以内联
所以,显然,static
如果只调用一次,会产生很大的影响。这是有道理的,因为一次内联静态函数不会增加代码大小。
提示:如果您想强制 clang 内联一个函数,请使用 __attribute__((always_inline))
。
inline
只是对编译器的建议,它可能会或可能不会真正内联您的特定代码。
关于 static
关键字,如果它应用于全局变量,那么如果您将代码编译为单独的编译单元,则它具有文件范围(如您所提到的)。因此,如果将它们编译为单个编译单元,甚至可以从其他文件访问静态全局变量。这意味着实际上,全局静态变量的范围不是文件而是编译单元(可能是也可能不是单个文件)。
但是,由于您有一个全局静态函数,而不是一个变量,因此它可以作为全局静态函数从任何地方访问。
编辑:
正如@Peter Cordes 在下面的评论中所建议的那样,同时使用 inline 和 static 可能真的是一团糟,所以官方文档( https://en.cppreference.com/w/cpp/language/inline )说内联函数的重新定义(以及 C++ 以来的变量) 17) 必须是非静态的。
我正在在线解决一个练习,有一次我需要从字符串的开头和结尾删除“”。这是我的代码:
void static inline process_value(std::string &value) {
if (value.back() !='>') {
value = value.substr(1, value.size()-2);
}
}
从此基准循环调用:
static void UsingStatic(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
std::string valor("\"Hola\"");
process_valueS(valor);
// Make sure the variable is not optimized away by compiler
benchmark::DoNotOptimize(valor);
}
}
只是因为好奇我did a benchmark.
- 编译器: Clang-9.0
- std:c++20
- 优化:O3
- STL: libstdc++(GNU)
我决定从 process_value
中删除 static
,使 void inline process_value
保持不变。令我惊讶的是它变慢了。
我认为static 仅表示该函数仅用于文件。但是 here 它说“ 'static' 意味着如果可能,编译器应该内联该函数”。但是在那种情况下,当我删除静态时,我认为结果不应该改变。现在我很困惑,除了将函数分隔为单个 .cpp
之外,static 还做了什么其他事情,这对性能有何影响?
QuickBench 上的反汇编显示 NoUsingStatic
循环实际上调用 process_value
而不是内联它,尽管 inline
关键字使编译器这样做是合法的。但是 UsingStatic
会 内联对 process_valueS
的调用。编译器决策的差异大概解释了性能差异,但是 为什么 clang 选择不内联一个声明为 void inline process_value(std::string &value){ ... }
?
编辑:由于问题不够清楚而被关闭,我删除了与问题无关的部分。但如果我遗漏了一些信息,请在评论中告诉我
Clang 使用基于成本的函数决定是否内联函数。这个成本受很多因素的影响。它受 static
.
幸运的是,clang 有一个输出,我们可以在其中观察到这一点。看看这个神栓 link:
void call();
inline void a() {
call();
}
static inline void b() {
call();
}
void foo() {
a();
b();
}
在这个小例子中,a()
和b()
是一样的,唯一的例外是b()
是静态的。
如果你将鼠标移动到 godbolt 上的调用 a()
或 b()
上(在 OptViewer
window 中),你可以阅读:
a()
:成本=0,阈值=487
b()
:成本=-15000,阈值=487
(如果成本低于阈值,clang 将内联调用。)
clang 使 b()
成本低得多,因为它是静态的。似乎 clang 只会为静态函数减少一次 -15000 的成本。如果 b()
被多次调用,所有 b()
的成本将为零,除了一个。
这是您的案例的编号,link:
process_value():
cost=400, threshold=325 -> 刚刚超过阈值,不会被内联
process_valueS():
:成本=-14600,阈值=325 -> 可以内联
所以,显然,static
如果只调用一次,会产生很大的影响。这是有道理的,因为一次内联静态函数不会增加代码大小。
提示:如果您想强制 clang 内联一个函数,请使用 __attribute__((always_inline))
。
inline
只是对编译器的建议,它可能会或可能不会真正内联您的特定代码。
关于 static
关键字,如果它应用于全局变量,那么如果您将代码编译为单独的编译单元,则它具有文件范围(如您所提到的)。因此,如果将它们编译为单个编译单元,甚至可以从其他文件访问静态全局变量。这意味着实际上,全局静态变量的范围不是文件而是编译单元(可能是也可能不是单个文件)。
但是,由于您有一个全局静态函数,而不是一个变量,因此它可以作为全局静态函数从任何地方访问。
编辑: 正如@Peter Cordes 在下面的评论中所建议的那样,同时使用 inline 和 static 可能真的是一团糟,所以官方文档( https://en.cppreference.com/w/cpp/language/inline )说内联函数的重新定义(以及 C++ 以来的变量) 17) 必须是非静态的。