65.9K
CodeProject 正在变化。 阅读更多。
Home

基准测试探险 - 性能高尔夫

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2016年9月10日

CPOL

4分钟阅读

viewsIcon

8879

基准测试探险 - 性能高尔夫

最近,Stack Overflow 的开发者之一 Nick Craver 一直在 推特上发布代码片段,来自他们的源代码,上周发布了以下代码

这段代码是对你通常编写的代码的优化版本,专门编写以确保它不分配内存。此前,Stack Overflow 遇到过 .NET GC 导致的大型暂停 问题,因此,看来在适当的情况下,他们会尽力编写不进行不必要内存分配的代码。

我还必须感谢 Nick 让我了解了术语 “性能高尔夫”,我听说过 代码高尔夫,但没有听说过性能版本。

旁注:如果你想查看完整的讨论以及所有不同条目的代码,可以查看 这个gist。此外,为了真正深入了解最快版本实际上做了什么,我强烈建议查看 Kevin Montrose 的博文 “优化练习”,其中有一些非常酷的技巧,尽管此时他基本上是在编写 C/C++ 代码,而不是你认为的 C# 代码。

优秀的基准测试工具

在这篇文章中,我不会过多关注这个特定的基准测试,而是将它作为一个例子,来说明我认为一个好的基准测试库应该为你提供什么。完全免责声明,我是 BenchmarkDotNet 的作者之一,所以我承认我可能会有偏见!

我认为一个好的基准测试工具应该提供以下功能

基准测试脚手架

通过使用 BenchmarkDotNet,或者任何基准测试工具,你可以专注于编写基准测试,而不用担心准确测量代码的任何机制。这很重要,因为通常当有人在 Stack Overflow 上发布优化和配套基准测试时,一些评论会指出他们的测量结果为什么不准确或完全错误。

对于 BenchmarkDotNet 来说,这很简单,只需向要进行基准测试的方法添加一个[Benchmark] 属性,然后添加几行代码来启动运行即可。

[Benchmark(Baseline = true)]
public bool StringSplit()
{
    var tokens = Value.Split(delimeter);
    foreach (var token in tokens)
    {
        if (token == Match)
            return true;
    }
    return false;
}

static void Main(string[] args)
{
    var summary = BenchmarkRunner.Run

它还为高级场景提供了一些工具,例如,你可以使用[Params] 属性装饰字段/属性,如下所示

[Params("Foo;Bar", 
        "Foo;FooBar;Whatever", 
        "Bar;blaat;foo", 
        "blaat;foo;Bar", 
        "foo;Bar;Blaat", 
        "foo;FooBar;Blaat", 
        "Bar1;Bar2;Bar3;Bar4;Bar", 
        "Bar1;Bar2;Bar3;Bar4;NoMatch", 
        "Foo;FooBar;Whatever", 
        "Some;Other;Really;Interesting;Tokens")]     
public string Value { get; set; }

然后每个基准测试都将运行多次,Value 设置为不同的字符串。这为你提供了一种非常简单的方法来尝试不同输入的基准测试。例如,一些方法始终很快,而其他方法在对它们来说是最坏情况的输入上表现不佳。

诊断正在发生的事情

如果你声明优化代码的目的是“检查string中的标记,**无需**分配内存”,你真的希望能够证明这是否属实。我之前写过关于 BenchmarkDotNet 如何 提供此信息,在这种情况下,我们得到以下结果(点击查看全尺寸图像)

Results showing memory allocations

所以你可以看到ContainTokenFransBouma 基准测试不是无分配的,在这种情况下这是一个问题。

一致、可靠和清晰的结果

另一个重要方面是你应该能够依赖结果。这部分是信任工具,希望随着时间的推移,人们会 信任 BenchmarkDotNet

此外,你应该能够获得清晰的结果,因此除了提供易于粘贴到 GitHub 问题或 Stack Overflow 答案中的基于文本的结果表外,BenchmarkDotNet 还将使用 R 统计和绘图库 提供几个图表。有时,一大段文字并不容易解释,但彩色图表可以提供帮助(点击查看全图)。

Graph of different benchmarks - with varying inputs

在这里,我们可以看到原始的ContainsToken 代码在某些情况下“更慢”(尽管值得指出的是 Y 轴以纳秒为单位)。

摘要

我是否建议在日常场景中编写像这些优化一样的代码?不会。

毫无例外,优化的代码可读性较差,难以调试,并且可能包含更多错误。当然,当你到达 最快版本 时,你不再编写可识别的 C# 代码了,它基本上是伪装成 C# 的 C++/C 代码。

但是,为了学习、娱乐或仅仅因为你喜欢竞争,那就没问题。只要确保你使用一个像样的工具,让你能够专注于编写尽可能优化的代码的乐趣部分!

文章 基准测试探险 - 性能高尔夫 最初发表在我的博客 性能是一项特性! 上。

© . All rights reserved.