C语言基本功教程系列 文件, Socket 和 其它

本文探讨了游戏开发中文件和Socket IO的优化策略,包括使用binary mode、减少fclose调用、利用磁盘缓存及异步IO等技术,旨在提高游戏运行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文件和Socket比较类似,都是在说IO访问,不过在操作系统级别上的实现有很大不同。 IO访问无论从什么角度讲,都是计算机系统里最慢的操作。特别是在游戏制作中,动不动就几百兆的动态或者静态数据,贴图纹理,和各种音响音乐等。要一次性把这些所有的数据读到内存中是不大可能的,所以在游戏进行过程中要不短的从硬盘或者光驱里读文件。 如何能够最小化这个瓶颈, 是值得注意的问题。先从文件说起吧。

===================文件==================
C语言里,正常的文件操作一般是3步。
fp = fopen(XXXX,XXX);
读写操作....
fclose(fp);

首先要注意的是,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


===================SOCKET==================
SOCKET虽然也是IO访问,要比文件快多了。但是在recv的时候,还是一次读的越多越好。这样效率更高。下面说一些SOCKET编程的技巧。

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的介绍)。所以,需要程序调节,匀速发送数据。

====================其他===================
其他一些程序设计上的东西,很多可以参见 [6]

1. 能用UINT的地方就用UINT,因为很多是UINT最快,而且UINT的除法要比int快。
2. 尽量避免类型转换,如果最后要转成float,开始的时候就用float比较好。
3. 不要用double
4. 能用乘法就不要用除法。 比如   3/2 可以换成  3 * 0.5
5. struct的大小尽量是2的倍数,如果不是就调整下,加pad。因为可以在level1 cache里放整数个。
6. 全局变量少用,如果要用,加上static
7. 局部变量也是越少越少。这样register的效率更高
8. 能用switch的地方,就不要用if,因为switch是直接生成跳转表,速度快很多
9. 互相关联的代码之间不要空行,功能不同的代码之间最好空上1行区分开
10. 用const static 代替 #define 定义常量
11. 统一你的代码风格,始终使用同样的命名规则

===================最后==================
生成高效率代码永远是从使用更好更合适的算法开始,但是编译的时候不要忘记打开你的优化开关。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值