首次在Linux上执行C ++代码的时间极慢

时间:2019-02-20 07:46:21

标签: c++ linux

我相信很多人都经历过这种情况。第一次在Linux上执行c ++代码总是要花费更长的时间。

在我的Linux机器上,像第一次调用::clock_gettime(CLOCK_REALTIME, &ts);大约比第三次慢大约五倍。

第一次分配内存比第二次慢100倍。

我尝试了预分配并在我的应用程序中使用了mlockall,但是即使如此,一个函数的第一次执行比第二个函数慢大约160倍,第二个函数却慢了两倍。 >

该函数的伪代码如下。 msg在堆上分配。但是它不包含在时间测量中。 msg2是POD,因此slow_for_the_first_time中根本没有内存分配。

void slow_for_the_first_time(Message * msg) {
     Msg2 msg2;
     //set msg2 using msg
  .... }

想知道,是什么原因导致第一次执行的缓慢?还有避免这种情况的方法吗?

erenon的答案很有帮助。我认为可能是因为Msg2是在so库中定义的。

在使用LD_BIND_NOW = 1之前,第一个执行时间约为8000纳秒,第二个执行时间约为500纳秒,第三个执行时间约为200纳秒。

现在,第一个执行时间约为2000纳秒,而第二个和第三个执行时间保持不变。因此它仍比第三次执行慢10倍,应该有其他因素影响第一次执行时间。

一些有趣的发现。

slow_for_the_first_time之前的

调用方法可以将首次执行时间再减少1微秒

void dummySet(Msg2& msg2)
{
    //set all fields of msg2. msg2 has about 30 fields it won't work if only set one field of msg2.
}

值得一提的是,第一次执行的速度绝对与msg无关,因为下面的代码中的第二次slow_for_the_first_time

char buffer[sizeof(Message)];
memset(buffer, 0, sizeof(buffer));
slow_for_the_first_time((Message*)buffer);//calling the method with a dummy buffer.
.....
slow_for_the_first_time(msg);//calling the method for the second time with a real msg.

与下面代码中的第二个slow_for_the_first_time一样快

slow_for_the_first_time(msg);//the first time takes around 2000 nanoseconds
.....
slow_for_the_first_time(msg);//the second time takes around 500 nanoseconds.

2 个答案:

答案 0 :(得分:2)

第一次链接引用的符号时,需要在动态加载的符号集中查找动态链接的符号。要查看这是否确实是问题所在,请执行以下操作:

$ LD_BIND_NOW=1 ./your_program

LD_BIND_NOW将指示链接器在GOT和PLT中的每个条目都固定地址:这将使启动速度稍慢,但也可能解决交换中的“首次呼叫速度慢”的问题。

如果事实证明是问题所在,则可以尝试静态链接库或预链接。

答案 1 :(得分:2)

除了懒惰链接erenon谈论in their answer之外,还有其他两个因素会导致首次运行时执行缓慢:冷缓存和冷分支预测。

总体而言,后续通话的加速来自:

  • 外部符号:链接器解析符号后,该符号在程序的整个生命周期内有效,此后实际上是无操作的操作; < / li>
  • 数据:CPU处理数据时,数据会临时存储在 CPU缓存中。将内存加载到该缓存中是一项昂贵的操作。但是一旦进入缓存,由于缓存确实是非常接近CPU的快速内存,因此下次可以快速使用相同的数据。您可以阅读此other answer about cache
  • CPU 分支预测通过尝试并预测代码的分支方式来显着改善代码执行。这也需要热身。这是an excellent answer about branch prediction

总体而言,代码在首次执行时往往会变慢。如果这是一个问题,则解决方案是:

  • LD_BIND_NOW,在启动时链接;
  • 缓存预热;
  • 分支预测热身