大家肯定完全没有耐心完成整个过程,大概看到第一个输出之后,就基本上“死机”了。等了五六分钟时在不耐烦了,断点调试一看,fs.Position才到xxKB到xxxKB之间,基本上绝望了,这就是我当时的感觉。但是千万不要放弃,请把while语句直接读FileStream的那一段去掉,重新来一遍。这一次怎么样呢?奇怪吧,绕了一个大弯,申请了一块巨大的内存,然后再用MemoryStream来读,速度远比FileStream快很多。我猜大家刚才还在注意那个BinaryReader,觉得那是一个累赘,一个可能的效率瓶颈。其实问题不在那个BinaryReader身上,相信大家已经知道问题在FileStream的Position属性上面了。用Reflector一看,果然问题多多:FileStream上面的Position计算非常麻烦,尤其是如果你一遍写一遍读的话问题会更加严重(不过本来问题已经够严重了,估计你不会觉得会有什么差别,麻木了)。
public override long get_Position()

{
if (this._handle == HostNative.INVALID_HANDLE_VALUE)
{
__Error.FileNotOpen();
}
if (!this.CanSeek)
{
__Error.SeekNotSupported();
}
this.VerifyOSHandlePosition();
return (this._pos + ((this._readPos - this._readLen) + this._writePos));
}首先需要经过两个判断,虽然没有多大的性能损失,但是我还是觉得不雅观。要命的就在那个VerifyOSHandlePosition里面:
private void VerifyOSHandlePosition()

{
if (this.CanSeek)
{
long num1 = this._pos;
long num2 = this.SeekCore((long) 0, SeekOrigin.Current);
if (num2 != num1)
{
this._readPos = 0;
this._readLen = 0;
}
}
}到这里层次还嫌不够深,还要调用一个SeekCore。我真是佩服写这段代码的人,也许这是一个无可奈何的事情,但是这么写我还是觉得比较让人感到难受得——明明是要得到当前的位置,却不得不首先调用一下Seek(0, SeekOrigin.Current)把位置“重新定位到当前位置”。也许是因为操作系统会缓冲,或者是操作系统会跑飞?这个十分有趣的写法给我非常深刻的印象,有点像看到了脑白金的广告一样。让我们继续跟踪下去:
private long SeekCore(long offset, SeekOrigin origin)

{
int num1;
int num2 = PAL.File_Seek(this._handle, (int) offset, (int) origin, out num1);
if (num2 != 0)
{
__Error.WinIOError(num2);
}
this._pos = num1;
return (long) num1;
}终于看到PAL的东西了……不是仙剑奇侠传的PAL,确切是什么意思我也不知道,金山词霸说是太平洋航空图书馆,我猜可能是什么抽象层的意思,反正我知道PAL里面都是执行引擎里面的Native方法的P/Invoke。也就是说每次我们想得到当前的Position之前,首先要通过三次调用进入执行引擎,才能够得到确切位置。这就是造成IO效率的罪魁祸首。
待续……
打印 | 张贴于 2004-10-23 01:24:00 | Tag:.NET CF

留言反馈
以后用到的时候来慢慢看
我记得是在看SSCLI的时候了解这个东西的,它提供了整个CLR所依赖的操作系统平台的一个抽象,由具体的操作系统提供其具体实现(这也是CLR为跨平台应用而进行的底层设计)——可以说,PAL是一个DIP(Dependency Inversion Principle)的实际应用(通过引入抽象把主动权收拢在CLR一边而不是Platform一边)。