为什么函数在 c++ 文件中的位置会影响其性能?特别是在下面给出的示例中,我们有两个相同的函数,它们具有不同的、一致的性能配置文件。如何对此进行调查并确定为什么性能如此不同?
该示例非常简单,因为我们有两个函数:a 和 b。每个都在一个紧密的循环中运行多次,并进行了优化 ( -O3 -march=corei7-avx
) 和定时。这是代码:
#include <cstdint>
#include <iostream>
#include <numeric>
#include <boost/timer/timer.hpp>
bool array[] = {true, false, true, false, false, true};
uint32_t __attribute__((noinline)) a() {
asm("");
return std::accumulate(std::begin(array), std::end(array), 0);
}
uint32_t __attribute__((noinline)) b() {
asm("");
return std::accumulate(std::begin(array), std::end(array), 0);
}
const size_t WARM_ITERS = 1ull << 10;
const size_t MAX_ITERS = 1ull << 30;
void test(const char* name, uint32_t (*fn)())
{
std::cout << name << ": ";
for (size_t i = 0; i < WARM_ITERS; i++) {
fn();
asm("");
}
boost::timer::auto_cpu_timer t;
for (size_t i = 0; i < MAX_ITERS; i++) {
fn();
asm("");
}
}
int main(int argc, char **argv)
{
test("a", a);
test("b", b);
return 0;
}
一些显着的特点:
- 函数 a 和 b 是相同的。它们执行相同的累加操作并编译成相同的汇编指令。
- 在计时开始尝试消除任何与预热缓存有关的问题之前,每次测试迭代都有一个预热期。
当它被编译并运行时,我们得到以下输出,显示 a 比 b 慢得多:
[me@host:~/code/mystery] make && ./mystery
g++-4.8 -c -g -O3 -Wall -Wno-unused-local-typedefs -std=c++11 -march=corei7-avx -I/usr/local/include/boost-1_54/ mystery.cpp -o mystery.o
g++-4.8 mystery.o -lboost_system-gcc48-1_54 -lboost_timer-gcc48-1_54 -o mystery
a: 7.412747s wall, 7.400000s user + 0.000000s system = 7.400000s CPU (99.8%)
b: 5.729706s wall, 5.740000s user + 0.000000s system = 5.740000s CPU (100.2%)
如果我们颠倒这两个测试(即 calltest(b)
和 then test(a)
)a 仍然比 b 慢:
[me@host:~/code/mystery] make && ./mystery
g++-4.8 -c -g -O3 -Wall -Wno-unused-local-typedefs -std=c++11 -march=corei7-avx -I/usr/local/include/boost-1_54/ mystery.cpp -o mystery.o
g++-4.8 mystery.o -lboost_system-gcc48-1_54 -lboost_timer-gcc48-1_54 -o mystery
b: 5.733968s wall, 5.730000s user + 0.000000s system = 5.730000s CPU (99.9%)
a: 7.414538s wall, 7.410000s user + 0.000000s system = 7.410000s CPU (99.9%)
如果我们现在反转 C++ 文件中函数的位置(将 b 的定义移到 a 之上),结果会反转并且 a 变得比 b 快!
[me@host:~/code/mystery] make && ./mystery
g++-4.8 -c -g -O3 -Wall -Wno-unused-local-typedefs -std=c++11 -march=corei7-avx -I/usr/local/include/boost-1_54/ mystery.cpp -o mystery.o
g++-4.8 mystery.o -lboost_system-gcc48-1_54 -lboost_timer-gcc48-1_54 -o mystery
a: 5.729604s wall, 5.720000s user + 0.000000s system = 5.720000s CPU (99.8%)
b: 7.411549s wall, 7.420000s user + 0.000000s system = 7.420000s CPU (100.1%)
因此,本质上,位于 c++ 文件顶部的任何函数都较慢。
您可能有的问题的一些答案:
- 对于 a 和 b,编译的代码是相同的。已检查拆卸。(对于那些感兴趣的人:http: //pastebin.com/2QziqRXR)
- 该代码是在 ubuntu 13.04、ubuntu 13.10 和 ubuntu 12.04.03 上使用 gcc 4.8、gcc 4.8.1 编译的。
- 在 Intel Sandy Bridge i7-2600 和 Intel Xeon X5482 cpu 上观察到的效果。
为什么会发生这种情况?有什么工具可以调查这样的事情?