这只是我一直在挖掘的个人项目。基本上,我使用StreamReader解析文本文件(例如从20mb到大约1gb)。性能相当稳定,但仍然......我一直渴望看到如果我用二进制解析会发生什么。不要误解,我不会过早地优化。我是故意微观优化的,只是为了“看”。
所以,我正在使用字节数组读取文本文件。快来看看,新的线路可以是(Windows)标准的CR / LF或CR或LF ......非常凌乱。我希望能够在CR上使用Array.IndexOf,然后跳过LF。相反,我发现自己编写的代码与IndexOf非常相似,但检查任何一个并根据需要返回一个数组。
所以关键:使用与IndexOf非常相似的代码,我的代码仍然最终变得非常慢。使用800mb文件将其放入透视图中:
这是带有for循环的代码(~150mb / s):
IEnumerator<byte[]> IEnumerable<byte[]>.GetEnumerator() {
using(FileStream fs = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, _bufferSize)) {
byte[] buffer = new byte[_bufferSize];
int bytesRead;
int overflowCount = 0;
while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
int bufferLength = bytesRead + overflowCount;
int lastPos = 0;
for(int i = 0; i < bufferLength; i++) {
if(buffer[i] == 13 || buffer[i] == 10) {
int length = i - lastPos;
if(length > 0) {
byte[] line = new byte[length];
Array.Copy(buffer, lastPos, line, 0, length);
yield return line;
}
lastPos = i + 1;
}
}
if(lastPos > 0) {
overflowCount = bufferLength - lastPos;
Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
}
}
}
}
这是更快的代码块(~320mb / s):
while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
int bufferLength = bytesRead + overflowCount;
int pos = 0;
int lastPos = 0;
while(pos < bufferLength && (pos = Array.IndexOf<byte>(buffer, 13, pos)) != -1) {
int length = pos - lastPos;
if(length > 0) {
byte[] line = new byte[length];
Array.Copy(buffer, lastPos, line, 0, length);
yield return line;
}
if(pos < bufferLength - 1 && buffer[pos + 1] == 10)
pos++;
lastPos = ++pos;
}
if(lastPos > 0) {
overflowCount = bufferLength - lastPos;
Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
}
}
(不,它不是生产准备好的,某些情况会让它爆炸;我使用128kb大小的缓冲区来忽略大部分。)
所以我的大问题是......为什么Array.IndexOf的工作速度要快得多?它基本上是相同的,一个for循环行走一个数组。是否有关于mscorlib代码执行方式的内容?甚至改变上面的代码来真正复制IndexOf并寻找只是CR然后像我使用IndexOf一样跳过LF也无济于事。呃......我一直在经历各种各样的排列,而且已经很晚了,或许有一些我不知道的明显错误?
顺便说一句,我查看了ReadLine并注意到它使用了一个开关块而不是一个if块...当我做类似的事情时,奇怪的是它确实将性能提高了大约15mb / s。这又是另一个问题的另一个问题(为什么切换速度比如果更快?)但我想我会指出我确实看过它。另外,我正在测试VS之外的版本构建,因此没有进行调试。
答案 0 :(得分:2)
安装期间mscorlib文件是ngen'd。 尝试使用Ngen.exe实用程序(与我假设的.NET框架一起提供)来创建文件...然后检查基准测试。 可能会稍快一些。
为了使您的.NET代码以接近原生的速度运行,Microsoft建议您在应用安装期间“Ngen”代码......
答案 1 :(得分:2)
这是一个很好的问题。简短的版本是它归结为IndexOf将使用的IEqualityComparer的实现。让我们看看以下代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
class Program {
static int [] buffer = new int [1024];
const byte mark = 42;
const int iterations = 10000;
static void Main ()
{
buffer [buffer.Length -1] = mark;
Console.WriteLine (EqualityComparer<int>.Default.GetType ());
Console.WriteLine ("Custom: {0}", Time (CustomIndexOf));
Console.WriteLine ("Builtin: {0}", Time (ArrayIndexOf));
}
static TimeSpan Time (Action action)
{
var watch = new Stopwatch ();
watch.Start ();
for (int i = 0; i < iterations; i++)
action ();
watch.Stop ();
return watch.Elapsed;
}
static void CustomIndexOf ()
{
for (int i = 0; i < buffer.Length; i++)
if (buffer [i] == mark)
break;
}
static void ArrayIndexOf ()
{
Array.IndexOf (buffer, mark);
}
}
您需要使用 csc / optimize + 编译它。
这是我的结果:
C:\Tmp>test
System.Collections.Generic.GenericEqualityComparer`1[System.Int32]
Custom: 00:00:00.0386403
Builtin: 00:00:00.0427903
现在,将数组和EqualityComparer的类型更改为byte,这是我的结果:
C:\Tmp>test
System.Collections.Generic.ByteEqualityComparer
Custom: 00:00:00.0387158
Builtin: 00:00:00.0165881
如您所见,字节数组是特殊的,可能已经过优化,可以在字节数组中查找字节。由于我无法反编译.net框架,我在这里停止了分析,但我想这是一个非常好的线索。