繁体   English   中英

神秘的 StringReader 性能

[英]Mysterious StringReader performance

您好,感谢您的阅读。

I was doing some research on the StringReader class in .NET and C#, using the implementation found here: https://referencesource.microsoft.com/#mscorlib/system/io/stringreader.cs

我做了一个小的 class,我认为它使用相同的基本实现来读取字符串,但令我惊讶的是,我的代码比 .NET StringReader 慢两倍多。

这是我的 class:

public class DataReader
{
    private String source;
    private int pos;
    private int length;

    public DataReader(string data)
    {
        source = data;
        length = source.Length;
    }

    public int Peek()
    {
        if (pos == length) return -1;
        return source[pos];
    }

    public int Read()
    {
        if (pos == length) return -1;
        return source[pos++];
    }
}

这是我的测试代码:

using System;
using System.IO;
using System.Diagnostics;
using System.Text;
using System.Collections.Generic;                   

public class Program
{
    public static void Main()
    {
        var s = new String('x', 10000000);

        StringReaderTest(s);
        
        DataReaderTest(s);
    }
    
    private static void StringReaderTest(string s)
    {
        var stopwatch = new Stopwatch();
        
        stopwatch.Start();
        
        var reader = new StringReader(s);
        
        while (reader.Peek() > -1)
        {
            reader.Read();
        }
        
        stopwatch.Stop();
        
        Console.WriteLine(stopwatch.ElapsedMilliseconds);
    }

    private static void DataReaderTest(string s)
    {
        var stopwatch = new Stopwatch();
        
        stopwatch.Start();

        var reader = new DataReader(s);

        while (reader.Peek() > -1)
        {
            reader.Read();
        }

        stopwatch.Stop();
        
        Console.WriteLine(stopwatch.ElapsedMilliseconds);
    }
}

这是整个事情的 .NET 小提琴。 https://dotnetfiddle.net/MqbU5q

这是 Fiddle 的 output。 我的实现速度慢了一倍。

77
159

我一定错过了什么,有人可以解释一下吗?

谢谢!

所以首先, Stopwatch不是一个合法的基准测试工具,它不合适的原因有很多。

我已经做了一件衬衫,以备将来使用。 :)

在此处输入图像描述

应该使用BenchmarkDotNet或类似的东西,它可以预热pre-JIT ,每次运行前进行垃圾收集,多次运行测试,并在调试时提醒您等。

这是一个示例,说明如何生成更可靠的基准

[SimpleJob(RuntimeMoniker.Net60, baseline: true)]
public class ReaderTest
{

    [Params(1000, 10000)]
    public int N;

    private StringReader _stringReader;
    private DataReader _dataReader;

    [GlobalSetup]
    public void Setup()
    {
        var s = new string('x', N);
        _stringReader = new StringReader(s);
        _dataReader = new DataReader(s);
    }

    [Benchmark]
    public void StringReader()
    {      
        while (_stringReader.Peek() > -1)
            _stringReader.Read();       
    }


    [Benchmark]
    public void  DataReader()
    {
        while (_dataReader.Peek() > -1)
            _dataReader.Read();    
    }
}

用法

BenchmarkRunner.Run<ReaderTest>();

结果

环境

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1348 (21H2)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.100
  [Host]   : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT  [AttachedDebugger]
  .NET 6.0 : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT

Job=.NET 6.0  Runtime=.NET 6.0

结果

方法 ñ 意思是 错误 标准差 比率
字符串读取器 1000 1.7247 纳秒 0.0545 纳秒 0.0455 纳秒 1.00
数据读取器 1000 0.5604 纳秒 0.0434 纳秒 0.0580 纳秒 1.00
字符串读取器 10000 2.4557 纳秒 0.0353 纳秒 0.0330 纳秒 1.00
数据读取器 10000 0.5690 纳秒 0.0345 纳秒 0.0306 纳秒 1.00

免责声明:在一个好的测试中应该有更多的理由,比如使用更真实的表示你的实际数据和用例。

如您所见(如您所料),您的实现更快,检查更少,并且不必通过 go 层 inheritance 来实现相同的结果,因此编译器不会发出CallVirt来查找vtable在运行时

callvirt 指令调用 object 上的后期绑定方法。 也就是说,方法是根据 obj 的运行时类型而不是方法指针中可见的编译时 class 来选择的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM