与使用相同数据的连续调用相比,第一种方法调用花费的时间是其十倍

时间:2019-03-13 20:23:32

标签: c++ performance sorting benchmarking branch-prediction

我正在为我的quicksort实施执行一些执行时间基准测试。在完全相同的输入数据上进行的100次连续测量中,第一次调用quicksort似乎比所有连续调用花费的时间长大约10倍。这是操作系统准备执行该程序的结果,还是有其他解释?而且,在计算平均运行时间时是否丢弃第一次测量值是否合理?

下面的条形图说明了执行时间(毫秒)与方法调用号的关系。每次调用该方法时,它都会处理完全相同的数据。

execution time vs. method call number

要生成此特定图形,主要方法将调用quicksort_timer::time_fpi_quicksort(5, 100),其实现如下所示。

static void time_fpi_quicksort(int size, int runs)
{
    std::vector<int> vector(size);
    for (int i = 0; i < runs; i++)
    {
        vector = utilities::getRandomIntVectorWithConstantSeed(size);
        Timer timer;
        quicksort(vector, ver::FixedPivotInsertion);
    }
}

getRandomIntVectorWithConstantSeed的实现方式如下

   std::vector<int> getRandomIntVectorWithConstantSeed(int size)
   {
      std::vector<int> vector(size);
      srand(6475307);
      for (int i = 0; i < size; i++)
         vector[i] = rand();
      return vector;
   }

CPU和编译

CPU:Broadwell 2.7 GHz Intel Core i5(5257U)

编译器版本:Apple LLVM版本10.0.0(clang-1000.11.45.5)

编译器选项:-std=c++17 -O2 -march=native

2 个答案:

答案 0 :(得分:3)

是的,可能是页面上的页面错误,其中包含排序功能的代码(以及计时代码本身)。 10倍可能还包括加速到最大Turbo时钟速度。

不过,缓存是不合理的:但是,您是在定时区域之外编写(小)数组,除非编译器以某种方式用d2的构造函数对init进行了重新排序。第一次的内存分配要慢得多,这很容易解释,也许第一次必须进行系统调用才能获得新页面,但是后来调用Timer(以构造std :: vector)已经抓到了-空闲列表中的高速缓存中的内存。

训练分支预测器也可能是一个重要因素,但是您希望它花费超过1次才能运行现代Intel CPU中的TAGE分支预测器,或者使用感知器预测器中的感知器。现代AMD“学习”了所有分支的完整模式。但是也许它们在第一次运行后就接近了。

请注意,您每次每次调用都会使用new来生成 same 随机数组。要测试分支预测是否是解释,请删除srand(),以便您每次都获得不同的数组,并查看时间是否仍然更长。

您使用什么CPU,编译器版本/选项等?

答案 1 :(得分:0)

可能是由于缓存的缘故,因为需要从DRAM提取内存并在第一次将其分配到CPU的数据缓存中。与在CPU缓存中命中的负载相比,这需要更多(更多)的延迟。

然后,由于您的指令位于管道中,因此它们遵循相同的分支,因为它是来自同一内存源的指令,因为它是相同的指针,因此不需要使它无效。

如果您实现4种方法或多或少具有相同的功能,然后在它们之间进行互换以查看会发生什么,将会很有趣。