这个系列的最后一章了,基本上这篇里边拿不准的,或者记得不太清楚的,以及很确定的结论,都在网上或者书里找到了依据。如果大家不同意文章里的论点,拿砖头拍我或者拍原作者都可以。 今天的主题是文件访问,Socket,和其他一些值得注意的内容。文件和Socket比较类似,都是在说IO访问,不过在操作系统级别上的实现有很大不同。IO访问无论从什么角度讲,都是计算机系统里最慢的操作。特别是在游戏制作中,动不动就几百兆的动态或者静态数据,贴图纹理,和各种音响音乐等。要一次性把这些所有的数据读到内存中是不大可能的,所以在游戏进行过程中要不短的从硬盘或者光驱里读文件。 如何能够最小化这个瓶颈, 是值得注意的问题。先从文件说起吧。 ===================文件================== 首先要注意的是,fopen里边要用binary mode打开文件,不要用ASCII mode. 很多人在处理文本类型的文件时候,喜欢用ASCII mode,然后用fgets一行一行的读。实际上ASCII mode无论如何操作,都是非常慢的,而且fgets函数更加的慢 [1]。所以即使是文本文件,也要用binary mode打开,一次读入一大块近来,慢慢处理。 其次需要注意的是,每一次读取文件的时候,硬盘都会对磁头进行重新定位和寻址。 这点会根据操作系统的不同而不同,总的来说windows XP要比 windows2000好点,但是也只是在系统文件方面 [2]。 因此每次读取的内容越多,平均效率就越高。同时操作系统提供磁盘缓存,当你写如磁盘的时候,只要不用fflush和fclose,数据在短时间内还是在内存中的,如果这时候再读出来写如的内容,也非常快。读写文件的时候不要用C++的流。写文件一次最好写不小于4k的数据, 而且最好对文件结构有效的安排,进行连续的访问(不要频繁使用fseek)[3]. 这些都有助于提高文件的访问速度。 最后,如上文中提到的,每次使用fclose和fflush的时候,都会强迫文件从缓存中写如到磁盘里。这个过程极其缓慢而且耗费时间,所以不在必要的时候,不要使用fclose和fflush. 如果一个文件读写完毕,而你又不确定是否短时间内会用到它,那就不要用fclose.你可以专门写一个类,管理这写文件的指针。对于经常会进行操作的文件,比如大地图的texture文件等,fopen一次就ok了,直到游戏结束再fclose
1. 使用异步socket (asynchronous IO). 在网络程序设计中,又2种处理方式,第一种是对每一个连接请求,都使用一个线程或者进程,第2种是使用一个线程同时使用异步IO. 第一种方式虽然程序设计上简单,但是创立进程的时候一般会有一些时间用在建立context上,进程间的转换和mutex等也需要浪费很多CPU资源,总体来说不如异步 IO 有效率 [4]。 2. 如果必须要使用多线程,可以考虑事先就创建好该线程,然后在需要的时候,把socket发过去就行了[4]。 3. 在处理 UDP协议的时候,需要注意的是,UDP和TCP不一样。UDP没有control flow,如果接收端的buffer满掉了,再来的UDP包都会被drop掉。所以在处理UDP协议的时候,一般需要专门一个线程读UDP包,防止过多的数据包丢失。 4. 最后,网络数据包多大合适? 这个很难说。对于UDP来说,小包是不划算的. 我们通常用的Ethernet(也就是LAN),在第2层Data link layer,最大的frame size 是1500 bytes,刨除最20 Bytes(最少)的IP头,8 bytes的UDP头,所有最大的UDP包可以包含 1472个bytes。要是考虑IP包有可能会有附加头信息,一般1400就比较合适。但是如果有些老版本router不地道,对你的UDP包分片的话,就比较惨了。能保证不分片的UDP包大小是513个byte左右 [5],不过毕竟现在这种老的Router很少了,1400字节大小的UDP包还是比较安全的。对于TCP来说,因为是stream protocol,不用考虑包的大小。但是TCP有个缺点,就是如果你一次发送很多很多数据,那么TCP的速度会一会快,一会慢(见[4]中的关于video streaming的介绍)。所以,需要程序调节,匀速发送数据。 ====================其他=================== 1. 能用UINT的地方就用UINT,因为很多是UINT最快,而且UINT的除法要比int快。 |