文件I-O优化技巧

一般来说,所有工程都会有对文件进行读写的操作。如果你不仅是要存储少量字节(例如JSON文件),就有必要考虑性能问题了。因为这种情况下很容易写出低效率的文件读写代码,而且编译器和Unity都无法帮助你优化。今天这篇文章将分享文件读写代码中一些常见的误区,希望对大家有所帮助。

.NET
API提供了很多完善的写入文件系统相关的类。Stream抽象类和其子类FileStream,还有File类,带有静态函数Open以及很方便的读写函数如BinaryReader和BinaryWriter。C#语言本身提供了using语法可以方便地关闭文件流、文件读写对象实例和文件句柄。这类代码使用便利,安全系数高,容易实现:

基本来说,文件操作可以归纳为以下五个步骤:

打开文件:File.Open

读取字节:stream.Read

写入字节:stream.Write

查询位置: stream.Position 或 stream.Seek

关闭文件:stream.Dispose 或 stream.Close

如果反编译FileStream类,你会发现stream.Position和stream.Seek其实没有什么区别,仅仅是API叫法上的不同。还有,stream.Dispose和stream.Close也基本上没什么区别,都能关掉文件。

得益于streams,readers,writers的便利性,想要测试它们的性能也很方便。进行读写操作只需调用一个函数即可,但这些读写操作函数的性能消耗可大不相同。接下来我针对这些不同的读写方式写了一个测试程序,下面是程序将要做的工作:

写入20MB,每次写入4KB的数据块

写入20MB,每次写入1字节

读取20MB,每次读取4KB的数据块

读取20MB,每次读取1字节

数次查找流的某个位置,次数与数据块的读写次数相同

数次打开某个文件,次数与数据块的读写次数相同

测试脚本如下:

新建Unity工程,在Assets目录下新建TestScript.cs脚本并复制以上代码。然后在默认的空场景中将TestScript附加到Camera游戏对象上,最后编译。注意编译平台使用64位,在非Development模式下编译,画质设置为最快,分辨率设为最低(640x480)。测试环境如下:

2.3 Ghz Intel Core i7-3615QM

Mac OS X 10.11.2

Apple SSD SM256E, HFS+ format

Unity 5.3.0f4, Mac OS X Standalone, x86_64, non-development

640×480, Fastest, Windowed

测试结果如下:

真是有着天壤之别,因此我单独提取出了最快的三个数据,得到了第二张图表。

可以看出,以数据块的方式进行读写操作的效率非常之高。虽然你可能不会一个一个字节地去写,但却有可能以4字节(整数)或类似大小为数据块单位进行写入操作。所以尽可能以较大的数据块为单位进行操作,这将提高38倍的写入效率,205倍的读取效率!

流查找(不论设置Position或调用Seek)不会进行字节读写,但这种操作是有代价的,虽然没有其它类型的操作那样多,它需要相当于读取4KB数据块三分之一的资源。所以最好避免这种查找操作,尽量线性读写文件,这样才能在各层面最大限度地发挥缓存的优势。

最后,打开或关闭文件同样不需要读取任何字节,但需要的时间会很长。事实证明,非常之长!打开和关闭文件需要的时间是以字节块写入整个文件所需时间的6.5倍,是字节块方式读取整个文件所需时间的40倍。考虑到读写操作的重要性,以及打开和关闭文件是操作系统的唯一需求,所以,除非特别需要,不要打开关闭文件。而且操作大文件要比操作多个小文件更好。

以上包括了关于I/O性能的一些建议,如有疑问,欢迎来下方评论区留言。

本文来源于:jacksondunstan.com

原作者:Jackson Dunstan

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×