【问题标题】:Why function call takes so much time?为什么函数调用需要这么多时间?
【发布时间】:2016-06-04 15:49:02
【问题描述】:

我有以下插入排序算法实现:

private static void InsertionSort(List<int> array)
{
    for (int i = 1; i < array.Count; ++i)
    {
        for (int j = i; j > 0 && array[j - 1] > array[j]; --j)
        {
             //swap(array, j, j-1);

             int temp = array[j];
             array[j] = array[j-1];
             array[j-1] = temp;
        }
     }
}

private static void swap(List<int> array, int i, int j)
{
    int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}

当我使用swap(array, j, j-1); 运行我的算法时,它比我使用内联函数体需要更多的时间(50000 个元素+2 秒)。

为什么?

【问题讨论】:

标签: c# algorithm performance


【解决方案1】:

手动内联方法并没有错,只是没有必要。内联小方法是抖动执行的standard optimizations 之一。这并不总是发生,但在 .NET 4.6.1 上,此示例代码中的 x86 和 x64 抖动 do 内联 swap()。此外,他们还展开内部循环,每次通过产生 两次 交换,这种手动优化程序员通常会跳过。

正确地对 .NET 应用程序进行基准测试并不总是那么简单。 非常对运行程序的 Release 版本很重要。并且使用调试器。尽管后者很容易修复,但使用工具 > 选项 > 调试 > 常规 > 取消选中“抑制 JIT 优化”选项。没有充分的理由重新打开它。

您现在还可以查看生成的机器代码,在 InsertionSort() 上设置断点,并在命中时使用 Debug > Windows > Disassembly。往往会让人眼花缭乱,但很容易看到你得到了两个内联的 swaps()。我会省去你组装转储的,只是看看。你应该清楚地看到测量的差异。这是我得到的:

使用 swap() 在 x64 上具有 50,000 个随机整数的列表上运行 5 次:

00:00:05.4447216
00:00:05.2928558
00:00:05.6960587
00:00:05.2835343
00:00:05.2809591

相同的测试,但现在手动内联 swap():

00:00:05.3015856
00:00:05.2877402
00:00:05.6369775
00:00:05.2603384
00:00:05.2616389

需要尽可能长的时间。

如果不显示使用 List.Sort() 得到的结果,我会失职:

00:00:00.0075878
00:00:00.0073398
00:00:00.0076528
00:00:00.0078046
00:00:00.0066319

【讨论】:

  • 哦,我运行我的程序的调试版本!有什么区别?
  • List.Sort()。它使用什么类型的排序算法?快速排序?
  • 好吧,你现在知道区别是什么了,我链接的那些优化没有执行,所以你确实看到了方法调用的成本。它使用introspection sort
  • 为什么它会展开循环以每次通过 两次 交换?
  • 循环内部的确切副本数量各不相同。我见过的最多的是 4,swap() 在机器代码中有点强大,所以 2 似乎是一个合理的选择
猜你喜欢
  • 1970-01-01
  • 2012-08-09
  • 1970-01-01
  • 2015-10-03
  • 1970-01-01
  • 1970-01-01
  • 2021-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多