1. 使用更好的硬件
2. 选择比较好的编程语言和编译器
vc6 的 strcpy, vc2008 的strcpy 性能区别极大。
RELEASE
1亿次循环测试结果:
strcpy = 13047
strcpy = 4500
check: strcpy, lstrcpy 相关API实现字符串COPY, 性能之比较
3. 设计扩展性很强的应用程序
a. 根据硬件的特性来设计
L1 cache->L2 cache->L3 cache -> 内存 -> 硬盘及其他存储设备
L3 cache 影响相当小
硬件性能比对:
如果L1 数据操作之,需要1s
那么L2 需要 4->7s
内存需要 25->150s
硬盘需要 3个星期
b. 根据程序的要求,选择正确的数据结构和算法
如:结构体之变量按大小排列
4. 对代码进行调整
可能基本开发完成。进行适当的调整。
a. 尽可能不调用代价高的API,如读写文件,socket操作等。
b. 如果一个小程序被调用很多次,尽量提高其性能。
案例说明_蜗牛:
一个IOCP服务器的设计:(传统设计)
线程0
接受网络请求 工作线程1 ... 工作线程32
每个工作线程处理:step1. 解析请求头。step2. 根据请求,逻辑计算. step3. 读写数据库。step4. 本地分发或者转发数据。
线程0 接受网络请求 |
工作线程1 工作线程2 工作线程3 …
工作线程64 |
解析请求头 根据请求,逻辑计算
读写数据库 本地分发或者转发数据 |
新的设计方案:
线程0
接受网络请求
工作线程1->8 解析请求头
工作线程8->16 根据请求,逻辑计算
工作线程16->24 读写数据库
工作线程24->32 本地分发或者转发数据
线程0 接受网络请求 |
工作线程1 …
工作线程8 |
工作线程8 …
工作线程16 |
工作线程16 …
工作线程24 |
每秒操作数对比:
CPU个数: 2 4 6 8
传统方案: 5000 7600 7400 6000
新方案: 9800 17400 24000 27000
能够使用多个CPU并充分利用CPU及其所带的Cache的强大功能。
原因:每一个操作所做的事情都是一样的。各个操作的数据也是相近的。
经过一两次的循环,指令与大部分的数据都从磁盘加载到了Cache中。
总结:
x. Cache使用利正确 严重影响程序整体性能和扩展性。
y. 充分利用指令的局部性与数据之局部性。
5. 对内存的合理利用与控制:
a. 系统基本情况
任何进程都需要占用资源。包括:物理内存+虚拟内存(通常为磁盘)
假如进程需要使用保存在磁盘中的一个虚拟内存页面,影射到内存,然后使用。
这个过程叫做:页面错误或者页失效。
细节:影射到内存的过程是什么?
可能的方式1. 虚拟内存页调入内存(并影射进程内存地址到操作系统内存地址),删除虚拟内存页。
可能的方式2. 把现在的物理内存页放置到磁盘上,然后虚拟内存页调入内存,删除虚拟内存页。
系统如何管理:进程所占内存 ?
最小值: min
最大传: max
当前值: cur (基本位于min->max之间,可能>max)
操作系统会管理进程内存,这是基于操作系统本身所拥用的物理内存数来的。
可能的方式2, 这种情况发生在:物理内存不够使用的情况。
当前值>max 的情况发生在:操作系统物理内存很多的情况。
b. 人为控制
b.1. 使用VirtualLock, VirtualUnLock锁定常用内存。
这样操作系统在任何情况下,都不会存入磁盘。
b.2. 在进程空闲时,使用SetProcessWorkingSetSize 释放掉进程相关资源。
b.3. 编绎时使用:link/WS:AGGRESSIVE 指定系统在任何时候都可以对进程资源进行处理。
案例说明_泰姬陵:
页访问之性能提高:
方式1:
DWORD dwArray[16][4096];
for( int i = 0; i < 4096; i ++ )
{
for( int j = 0; j < 16; j ++ )
{
DWORD dwVal = dwArray[j][i];
}
}
方式2:
DWORD dwArray[16][4096];
for( int i = 0; i < 16; i ++ )
{
for( int j = 0; j < 4096; j ++ )
{
DWORD dwVal = dwArray[i][j];
}
}
方式1消费10个tick count; 方式2消费0个tick count;
方式1程序是前后跳转页访问,容易产生页失效。
有关查表法的说明:
假如我们需要x 以内的数据之相乘为多大。
代码为:int v = x * y;
为了提高性能,我们可能改进为:查表得到。
然而如果表及大,可能产生页失效。因此可能性能更低。