[英]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.