作为一种学习经历,我最近尝试在C#中实现Quicksort with 3 way partitioning。
除了需要在递归调用之前对左/右变量添加额外的范围检查外,它似乎运行良好。
我事先知道框架在List<> .Sort(通过Array.Sort)中提供built-in Quicksort implementation。所以我尝试了一些基本的分析来比较性能。结果:在同一列表上运行的内置List<> .Sort方法的执行速度比我自己的手动实现快10倍。
使用反射器,我发现List<> .Sort中的实际排序是在外部代码中实现的,而不是IL(在名为tryszsort()的函数中)。
看看我自己的Quicksort实现,我希望用迭代替换递归调用可能会有所改进。此外,禁用数组边界检查(如果可能)也可以带来一些好处。也许这会更接近内置实现,但我不自信。
所以我的问题:期望在优化算法(用.NET IL编写,与本机代码相匹配)中的性能是否可以与外部实现的算法的性能竞争?
再一次,我意识到Quicksort是作为框架的一部分提供的,这对我来说只是一次学习经历。然而,还有许多算法(CRC32可以想到)没有提供,但仍然可能对许多应用程序有很大价值。以下是有关implementing CRC32 in .NET和效果问题的相关问题。
因此,如果您需要在.NET中实现这样的算法,需要了解哪些主要的性能注意事项,以便您的算法至少可以接近外部代码的性能?
[更新]
通过更改算法以在Int的简单数组上操作而不是List,我将执行速度提高到内置Array.Sort的大约10%以内。在Reflector中,我可以看到这避免了对列表中的每个get或set上的Callvirt()操作。我认为这可能会改善一些事情,但我对此感到惊讶。
答案 0 :(得分:7)
通过使用非递归代码,特别是使用“不安全”块和指针算法(如果适用),可以实际上使用C#编写的算法看到x5或x10性能增益。 与性能一样(在处理托管环境时更是如此),在您尝试并对其进行基准测试之前,您永远不会知道。
现在,一般来说,你应该主要用C#编写内容,然后对其进行优化,再进行一些优化,如果还不够好,请确定准确的关键代码并将其移植到C ++(同时要小心限制托管/本机呼叫边界的数量。)
答案 1 :(得分:3)
出于好奇,尽管我有9年的.NET经验,但我仍然经常犯这样的错误:您是否在发布模式下编译代码并进行了优化?调试代码的性能明显低于优化的发布代码。
假设您在发布模式下进行DID编译,如果您以类似方式实现算法(即迭代与迭代或递归与递归),则性能应该没有太大差异。如果您希望查看.NET实现并弄清楚,可以下载SSCLI Share-Source Common Language Infrastructure。这是Microsoft公开的符合ECMA标准的CLI实现。它不是我们都知道和喜爱的.NET框架的100%,但它是它的重要组成部分。它可以提供Reflector无法提供的大量信息,包括内部实现。所有类型的代码都可用,包括C#,C ++,甚至一些汇编程序。
答案 2 :(得分:1)
确保你比较苹果和苹果。
排序时,比较功能可能占主导地位,实现之间可能会有所不同。
假设两种情况下的比较函数都足够快,不会成为问题,那么时间可以由数组边界检查等主导,这很容易产生很大的不同。