dotnet程序优化心得(一)
近一段时间测试过几个程序,有一些心得。具体步骤如下:
(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
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

一定要在一次运行中同时测试原方法和优化后方法,这样得到的测试结果才有比较意义。(如果你的方法非常耗用CPU,用带超线程的计算机,或者把测试进程设置成实时进程,这样得到的结果更精确。)
windows下时间精度应该是0.001-0.01ms,运行时间在0.01ms以下的结果不可靠。.net下时间精度应该更低一点。我怀疑只有0.1ms,但没有认真测试过。
不想让别的进程干扰,就把测试进程设置成实时进程。半年前测试一个程序,性能只到了40-60,查看进程发现cpu时间主要是GUI占用了,测试进程只占用了20%。动动鼠标,程序性能会上串下跳的。把进程优先级调到最高,测试进程cpu占用稳定在93-97%。设置为实时进程,cpu占用稳定在96-97%,这样测试的结果才有效。
下面以实际例子具体解释相关技巧。
评论
谢谢。测试了一下,DateTime.Now.Ticks的时间精度是10ms。
static void Main(string[] args)
{
long start,end;
start = DateTime.Now.Ticks;
end = DateTime.Now.Ticks;
while(start==end)
{
end = DateTime.Now.Ticks;
}
Console.WriteLine("DateTime.Now.Ticks时间精度:{0}ms",(end-start)/10000);
}
结果:DateTime.Now.Ticks时间精度:10ms
这里够用了。更精确的测试不够用。
long t;
GC.Collect();
sw.Start();
///....
sw.Stop();
t = sw.ElapsedMilliseconds;
是这样子吗?
不过我找不到这个stopwatch是在Framework哪里定义的,我只看到有别人写的(虽然很简单)
喔,原来是framework2.0中的,试试能不能reflector出来代码。
http://blog.joycode.com/ninputer/archive/2004/07/31/29167.aspx
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public class QueryPerfCounter
{
[DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(
out long lpPerformanceCount);
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);
private long start;
private long stop;
private long frequency;
Decimal multiplier = new Decimal(1.0e9);
public QueryPerfCounter()
{
if (QueryPerformanceFrequency(out frequency) == false)
{
// Frequency not supported
throw new Win32Exception();
}
}
public void Start()
{
QueryPerformanceCounter(out start);
}
public void Stop()
{
QueryPerformanceCounter(out stop);
}
public double Duration(int iterations)
{
return ((((double)(stop - start)* (double) multiplier) / (double) frequency)/iterations);
}
}
static void Main(string[] args)
{
QueryPerfCounter q = new QueryPerfCounter();
q.Start();
q.Stop();
Console.WriteLine("QueryPerfCounter时间精度:{0}ns",q.Duration(1));
}
结果:
QueryPerfCounter时间精度:3911.1116077602ns
Duration单位是纳秒
但是源程序测量误差还是有的,大概是3.9微秒,主要是函数调用的时间。如果测试之前这么来一下:
// Call the object and methods to JIT before the test run
QueryPerfCounter myTimer = new QueryPerfCounter();
myTimer.Start();
myTimer.Stop();
误差大概是1微妙。
我把这个类改了一下,加了时间校准:
public class QueryPerfCounter
{
[DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(
out long lpPerformanceCount);
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);
private long check;
private long start;
private long stop;
private long frequency;
Decimal multiplier = new Decimal(1.0e9);
public QueryPerfCounter()
{
if (QueryPerformanceFrequency(out frequency) == false)
{
// Frequency not supported
throw new Win32Exception();
}
check = 0;
int loops = 10000;
QueryPerformanceCounter(out start);
QueryPerformanceCounter(out stop);
for(int i=0;i<loops;i++)
{
QueryPerformanceCounter(out start);
QueryPerformanceCounter(out stop);
check+=stop-start;
}
check = check/loops;
}
public void Start()
{
QueryPerformanceCounter(out start);
}
public void Stop()
{
QueryPerformanceCounter(out stop);
}
public double Duration(int iterations)
{
return ((((double)(stop - start-check)* (double) multiplier) / (double) frequency)/iterations);
}
}
再运行测试:
static void Main(string[] args)
{
QueryPerfCounter q = new QueryPerfCounter();
q.Start();
q.Stop();
Console.WriteLine("QueryPerfCounter时间精度:{0}ns",q.Duration(1));
}
结果:QueryPerfCounter时间精度:0ns