在我的测试中,我创建了一个包含32000个字符的字符串。 在重复执行测试之后,BCL StringReader在350us时始终执行,而我的400us则运行。他们隐藏了什么样的秘密?
private void SpeedTest()
{
String r = "";
for (int i = 0; i < 1000; i++)
{
r += Randomization.GenerateString();
}
StopWatch s = new StopWatch();
s.Start();
using (var sr = new System.IO.StringReader(r))
{
while (sr.Peek() > -1)
{
sr.Read();
}
}
s.Stop();
_Write(s.Elapsed);
s.Reset();
s.Start();
using (var sr = new MagicSynthesis.StringReader(r))
{
while (sr.PeekNext() > Char.MinValue)
{
sr.Next();
}
}
s.Stop();
_Write(s.Elapsed);
}
public unsafe class StringReader : IDisposable
{
private Char* Base;
private Char* End;
private Char* Current;
private const Char Null = '\0';
/// <summary></summary>
public StringReader(String s)
{
if (s == null)
throw new ArgumentNullException("s");
Base = (Char*)Marshal.StringToHGlobalUni(s).ToPointer();
End = (Base + s.Length);
Current = Base;
}
/// <summary></summary>
public Char Next()
{
return (Current < End) ? *(Current++) : Null;
}
/// <summary></summary>
public String Next(Int32 length)
{
String s = String.Empty;
while (Current < End && length > 0)
{
length--;
s += *(Current++);
}
return s;
}
/// <summary></summary>
public Char PeekNext()
{
return *(Current);
}
/// <summary></summary>
public String PeekNext(Int32 length)
{
String s = String.Empty;
Char* a = Current;
while (Current < End && length > 0)
{
length--;
s += *(Current++);
}
Current = a;
return s;
}
/// <summary></summary>
public Char Previous()
{
return ((Current > Base) ? *(--Current) : Null);
}
/// <summary></summary>
public Char PeekPrevious()
{
return ((Current > Base) ? *(Current - 1) : Null);
}
/// <summary></summary>
public void Dispose()
{
Marshal.FreeHGlobal(new IntPtr(Base));
}
}
答案 0 :(得分:4)
也许Reflector会帮助您找到答案?
答案 1 :(得分:4)
您始终可以查看source code
答案 2 :(得分:4)
我敢打赌,Marshal.StringToHGlobalUni()
和Marshal.FreeHGlobal(new IntPtr(Base))
与差异有很大关系。我不确定StringReader如何管理字符串,但我敢打赌它不是将它复制到非托管内存。
查看Reflector中的StringReader.Read()方法显示:
public override int Read()
{
if (this._s == null)
{
__Error.ReaderClosed();
}
if (this._pos == this._length)
{
return -1;
}
return this._s[this._pos++];
}
构造函数也只是:
public StringReader(string s)
{
if (s == null)
{
throw new ArgumentNullException("s");
}
this._s = s;
this._length = (s == null) ? 0 : s.Length;
}
因此,看起来StringReader只维护当前位置并使用常规索引返回值。
修改强>
在回复您的评论时,您的Next()方法会进行比较和不安全的强制转换,这可能没有以任何方式进行优化。 StringReader.Read()进行简单的比较,并将字符作为_pos索引返回到字符串中,编译器可能会进行一些优化。
答案 3 :(得分:2)
简单地查看代码后无法分辨,但这是StringReader.Read()的代码:
public override int Read()
{
if (this._s == null)
{
__Error.ReaderClosed();
}
if (this._pos == this._length)
{
return -1;
}
return this._s[this._pos++];
}
与值检查和指针增量相比,它们有两个简单的值检查和一个数组访问加上增量。也许查看IL并查看每个编译的操作数量是有用的。
答案 4 :(得分:2)
您是否尝试过剖析您的StringReader以查看是否有可以节省时间的明显地方?这是确定代码中瓶颈的最可靠方法。
通常情况下,我会建议您针对另一方分析您的解决方案,但我不确定分析BCL的可行性。这是GAC和强烈签名,这使得仪器很难,所以你不得不依赖抽样。