近一段时间测试过几个程序,有一些心得。具体步骤如下:
(1)写出测试程序
(2)测试原程序,记录运行时间,作为优化的基础。要在Release下测试。
(3)检查算法,是不是最有效的算法。尤其是现在内存便宜,看有没有能够用空间换取时间的方法
(4)用Reflector查看类库,看你使用的方法是不是最有效率的方法
(5)对于运行次数多,性能关键的地方,不要直接调用类库。类库是为通用目的设计的,用Reflector可以发现,针对特殊的问题,内库里面有很多冗余代码
测试程序是很关键的,dotnet下用DateTime.Now.Ticks能够得到精确的时间,单位是10^-7s。
我爱用下面这种结构的测试代码:
1
static void Main(string[] args)
2
{
3
long _start,_end,_loops; //DateTime.Now.Ticks是long int
4
_loops=100000; //重复次数
5
_start=DateTime.Now.Ticks;
6
7
for (int i=0;i<_loops;i++)
8
{
9
原方法;
10
}
11
_end=DateTime.Now.Ticks;
12
13
Console.WriteLine("{0}次调用原方法共花费时间{1}ms",_loops,(_end-_start)/10000);
14
15
_start=DateTime.Now.Ticks;
16
17
for (int i=0;i<_loops;i++)
18
{
19
优化后方法;
20
}
21
_end=DateTime.Now.Ticks;
22
23
Console.WriteLine("{0}次调用优化后方法共花费时间{1}ms",_loops,(_end-_start)/10000);
24
}
一定要在一次运行中同时测试原方法和优化后方法,这样得到的测试结果才有比较意义。(如果你的方法非常耗用CPU,用带超线程的计算机,或者把测试进程设置成实时进程,这样得到的结果更精确。)
windows下时间精度应该是0.001-0.01ms,运行时间在0.01ms以下的结果不可靠。.net下时间精度应该更低一点。我怀疑只有0.1ms,但没有认真测试过。
不想让别的进程干扰,就把测试进程设置成实时进程。半年前测试一个程序,性能只到了40-60,查看进程发现cpu时间主要是GUI占用了,测试进程只占用了20%。动动鼠标,程序性能会上串下跳的。把进程优先级调到最高,测试进程cpu占用稳定在93-97%。设置为实时进程,cpu占用稳定在96-97%,这样测试的结果才有效。
下面以实际例子具体解释相关技巧。
下面以实际例子具体解释相关技巧。
(1)缘起
bfax@smth.org发了一个字符串转换程序,引起了热烈讨论。原程序如下:
1
2
Function B2G()Function B2G(prestr As String) As String
3
Dim i, j As Integer
4
Const GB_Lib = "
" //几千个字符吧,因为字符串长度限制,原程序是由GB_lib1,GB_lib2
GB_lib4四个字符串构成的,为了简化问题,只用一个字符串代替。
5
Const BIG5_Lib = "
" //与GB_Lib中简体字一一对应的繁体字
6
7
For i = 1 To prestr.Length
8
j= Instr(1, BIG5_Lib1, GetChar(prestr, i))
9
If j<>0 Then prestr=prestr.Replace(GetChar(BIG5_Lib1,j),GetChar(GB_Lib1,j))
10
j= Instr(1, BIG5_Lib2, GetChar(prestr, i))
11
If j<>0 Then prestr=prestr.Replace(GetChar(BIG5_Lib2,j),GetChar(GB_Lib2,j))
12
j= Instr(1, BIG5_Lib3, GetChar(prestr, i))
13
If j<>0 Then prestr=prestr.Replace(GetChar(BIG5_Lib3,j),GetChar(GB_Lib3,j))
14
j= Instr(1, BIG5_Lib4, GetChar(prestr, i))
15
If j<>0 Then prestr=prestr.Replace(GetChar(BIG5_Lib4,j),GetChar(GB_Lib4,j))
16
Next
17
Return prestr
18
End Function
(2) 分析问题
写测试程序测试在我的1.5M迅驰本本测试,替换效率为30万字/s,该程序采用Replace,这样对每一个字符都要扫描GB_Lib字符串中的几千个字符,性能自然上不去。需要寻找更好的数据结构和算法,降低每一个字符串的操作时间。
.net类库里有一个很好的东西可以拿来直接用:Hashtable。也就是说,把每一个简体字作为key,每一个繁体字作为value。这样处理每个字符的时候只需要看它在不在Hashtable的key里面,在的话就找出对应的value替换,否则就不做任何操作。这样做的代价是Hashtable初始化的耗时,不过初始化顶多也就一次嘛。程序如下:
1
public class ConvertDemo
2

{
3
private static Hashtable _libTable;
4
5
static ConvertDemo()
6
{
7
InitHashTable();
8
}
9
10
static string GB_lib="
";
11
12
static string BIG5_lib="
";
13
14
private static void InitHashTable()
15
{
16
_libTable = new Hashtable();
17
PushIntoHashtable(_libTable,GB_lib,BIG5_lib);
18
}
19
20
private static void PushIntoHashtable(Hashtable t, string g , string b)
21
{
22
for (int i=0;i<g.Length;i++)
23
{
24
t.Add(g,b);
25
}
26
}
27
28
private static char ConvertChar(char input)
29
{
30
if (_libTable.ContainsKey(input)) return (char)_libTable[input];
31
else return input;
32
}
33
34
public static string ConvertText(string inputString)
35
{
36
StringBuilder sb = new StringBuilder(inputString);
37
for (int i=0;i<inputString.Length;i++)
38
{
39
sb = ConvertChar(inputString);
40
}
41
return sb.ToString();
42
}
43
}
测试性能,结果为300万字/秒。性能提高了10倍。
(3)用relector看Hashtable源代码,消除无用操作,继续优化
还能不能继续优化呢?ConvertChar (char input)执行次数最多,是对性能最有影响的方法。用reflector反编译Hashtable的get_Item(object key)方法:
1
public virtual object get_Item(object key)
2

{
3
uint num1;
4
uint num2;
5
Hashtable.bucket bucket1;
6
if (key == null)
7
{
8
throw new ArgumentNullException("key", Environment.GetResourceString("ArgumentNull_Key"));
9
}
10
Hashtable.bucket[] bucketArray1 = this.buckets;
导入论坛
引用链接
收藏
分享给好友
管理
举报
TAG: