随笔 - 103  文章 - 2 评论 - 921 trackbacks - 21
<2008年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011


转载请保留链接。
国际管理VS中国国情链接 8-21 13:48

与我联系

搜索

 

常用链接

留言簿(13)

我参与的团队

我的标签

随笔分类

随笔档案

文章分类

相册

最新随笔

积分与排名

  • 积分 - 148240
  • 排名 - 249

最新评论

阅读排行榜

评论排行榜

60天内阅读排行

 一.              构造StopwatchPool 

System.Diagnostics命名空间中dot net framework为我们提供了精确计算代码执行时间的类Stopwatch;因为我们可能频繁使用Stopwatch类的实例,所以我们在此做一个简单的对象池,尽量降低非方法执行时间对程序性能的影响。 

StopwatchPool类的静态字段List<StopwatchWithUseState> _watchList中包含了系统中初始化好的StopwatchWithUseState实例;StopwatchWithUseState 实例中是Stopwatch的实例,和一个它是否正在被使用的字段。当需要新的Stopwatch实例使直接调用StopwatchPool的静态方法NewStopwatch()即可,注意在使用完Stopwatch实例之后需要调用ReleaseWatch(Stopwatch)释放掉Stopwatch。具体代码会在文章结尾给出,其类图如下:

二.              统计方法执行时间 

类图如下:

MethodPerfMonitor的关键实现是在进入一个方法时执行EnterMethod()方法,该方法会申请一个Stopwatch实例,并启动实例,记录方法执行次数;在方法执行结束时调用FinishMethod()方法,记录方法执行时间,并释放Stopwatch实例。其关键代码如下

/// <summary>
        
/// 在方法开始执行时调用此方法
        
/// </summary>

        [Conditional(EnablePerfMonitor.CompilerSymbal)]
        
public virtual void EnterMethod()
        
{
            
lock (instanceLock)
            
{
                _execCount
++;
                
if (_startRunTime == DateTime.MinValue)
                
{
                    _startRunTime 
= DateTime.Now;
                }

            }

            _watch 
= StopwatchPool.NewStopwatch();
            _watch.Start();
        }



        
/// <summary>
        
/// 在方法完成之前调用,必须调用,若不调用会导致申请的Stopwatch得不到释放
        
/// 最好在try{}finally{}的finally中释放
        
/// </summary>

        [Conditional(EnablePerfMonitor.CompilerSymbal)]
        
public virtual void FinishMethod()
        
{
            
lock (instanceLock)
            
{
                TotalRunTime 
+= TimeSpan.FromTicks(_watch.ElapsedTicks);
            }

            StopwatchPool.ReleaseWatch(_watch);
        }

大家可以看到在两个方法的前面都使用了Conditional特性,这样可以在编译时方便的启用或者禁用方法执行时间统计。

在这个类中还提供了一个静态方法RegisterMonitor(string methodFullName)这个方法会返回一个MethodPerfMonitor类的实例,若该类的静态字段_methodMonitorTable中有方法全名对应的MethodPerfMonitor实例,则直接返回,否则将新生成的实例保存在该类的一个静态字段中。这样保证一个实例对应一个方法的统计。 

三.              统计缓存方法的执行时间和缓存命中率 

我们需要一个新的类来统计方法的缓存命中情况,CachableMethodPerfMonitor该类从MethodPerfMonitor继承,该类只多了一个功能统计缓存的命中率;在方法执行结束时执行FinishMethod时需要指定这一次执行是否命中了缓存。并添加方法执行时间和缓存命中时的执行时间。其关键实现如下:

/// <summary>
        
/// 在方法结束时调用
        
/// </summary>
        
/// <param name="isHitCache">是否命中缓存</param>

        [Conditional(EnablePerfMonitor.CompilerSymbal)]
        
public void FinishMethod(bool isHitCache)
        
{
            
lock (instanceLock)
            
{
                TimeSpan execTimespan 
= TimeSpan.FromTicks(_watch.ElapsedTicks);
                
if (isHitCache)
                
{
                    CacheRunTime 
+= execTimespan;
                    _hitTimes
++;
                }

                TotalRunTime 
+= execTimespan;
            }


            StopwatchPool.ReleaseWatch(_watch);
        }

四.              如何使用 

请看如下实例代码。有问题欢迎提出。

相关源码下载

posted on 2008-05-06 08:58 玉开 阅读(2063) 评论(23)  编辑 收藏 所属分类: .Net开发经验算法,建模

FeedBack:
#1楼  2008-05-06 09:14 PerfectDesign      
初一看标题我以为是mssql的
关于代码运行时间的统计,我以及以前用过一个日本人写的类库,直接调用的api,忘记名字了
  回复  引用  查看    
#2楼 [楼主] 2008-05-06 09:19 玉开      
@PerfectDesign
那我修改一下标题。
  回复  引用  查看    
#3楼  2008-05-06 09:33 PerfectDesign      
@玉开
只是我自己误会了,
最近一直在sql方面的,思维都僵化了
^_^
  回复  引用  查看    
#4楼  2008-05-06 09:37 xiaosonl      
对象池写的好,学习了
  回复  引用  查看    
文章过于呆板,没有把设计思路和设计目的讲清,也没有讲述为什么要这样设计。
一上来就是"构造StopwatchPool",既没有说清楚为什么要构造,也没有说清楚这样构造的优缺点。感觉楼主是顺着自己的思维在写日记,而不是一篇讲述如何处理某个问题的技术文章。

第二段"这个类MethodPerfMonitor....",如果没看错的话,类[MethodPerfMonitor]是第一次在这篇文章中出现,此处如果使用"类[MethodPerfMonitor]是整个统计系统的核心,这个类提供2个函数 EnterMethod()和FinishMethod(),只要把它们放置于用户函数的初始化之前和退出代码之前。EnterMethod顾名思义就是用于函数初始化之前,另一个则通常放置在return语句之前。...."这样会更通顺,对于读者来说看起来也更明白。

希望作者能在语文上有所改进,毕竟写文章不是写代码,光有技术是不够的。
  回复  引用    
刚才写漏了一段:
原文"只要把它们放置于用户函数的初始化之前和退出代码之前“

应修改为:
"只要把它们放置于用户函数的初始化之前和退出代码之前就能统计该函数的执行时间"
  回复  引用    
#7楼  2008-05-06 10:01 xioxu [未注册用户]
我曾经也有这个需求,需要统计一些类中的方法的执行时间。
你这个方法要求得修改被统计方法,而这个貌似不是很容易办到(有时候方法特别多)。
我当时查资料,看到好像有种方法是指定编译的时候给那些方法插入统计函数,可惜我当时也没能实现。
  回复  引用    
#8楼 [楼主] 2008-05-06 10:14 玉开      
@john.maisen
感谢你的批评,我把类图放到了前面,这样可以让大家对类的概况有一个初步的了解。

另外我在文章中有这么一段话:“因为我们可能频繁使用Stopwatch类的实例,所以我们在此做一个简单的对象池,尽量降低非方法执行时间对程序性能的影响。”是为了说明为什么构造StopwatchPool的。是没有说清楚,还是你没有看到?

你的第二个回复中的那句话,在我的文章中没有找到。
  回复  引用  查看    
#9楼 [楼主] 2008-05-06 10:16 玉开      
@john.maisen
我会在以后的文章中尽量提高自己的语文水平。希望能看到阁下的大作。
  回复  引用  查看    
#10楼  2008-05-06 14:54 sunrisex [未注册用户]
只要把它们放置于用户函数的初始化之前和退出代码之前就能统计该函数的执行时间
... 退出代码之前 ... 这个比较难办到...
  回复  引用    
#11楼 [楼主] 2008-05-06 15:00 玉开      
@sunrisex
其实很容易办到的。
例如 原代码
private void DoSomething(){
//do something.
if(x)return ;
//do another thing
}
加上检测方法
private void DoSomething(){
monitor.EnterMethod();
try{
//do something.
if(x)return ;
//do another thing
}finally{
monitor.FinishMethod();
}
}
  回复  引用  查看    
#12楼  2008-05-06 15:27 cxfcxf8 [未注册用户]
[DllImport("kernel32.dll")]
private static extern bool QueryPerformanceFrequency(ref Int64 lpFrequency);

[DllImport("kernel32.dll")]
private static extern bool QueryPerformanceCounter(ref Int64 lpPerformanceCount);
用的这个计时api
  回复  引用    
#13楼 [楼主] 2008-05-06 16:05 玉开      
@cxfcxf8
使用反射看Stopwatch的实现,你会发现它内部使用了这两个api。
  回复  引用  查看    
#14楼  2008-05-06 17:54 镜涛      
study
  回复  引用  查看    
#15楼  2008-05-06 23:30 rex xiang      
StopwatchPool的适用价值不高, 简单的问题复杂化了.

也没必要创建多个Stopwatch实例, 只需要在开始计时的时候调一下System.Diagnostics.Stopwatch.GetTimestamp(), 保存返回值方法的返回值(startTickes), 然后在结束计时的时候再次调用此方法(endTickes).

最终的经过的时间即是TimeSpan(endTickes - startTickes).

哪里需要创建什么Stopwatch对象啊, 遍历Stopwatch列表啊之类的. 一切都可以简单的在MethodPerfMonitor中实现.
  回复  引用  查看    
#16楼 [楼主] 2008-05-07 09:02 玉开      
@rex xiang
如果只统计一个方法的性能,确实是没有必要的;若同时统计多个方法的性能则是完全有必要的。
  回复  引用  查看    
#17楼  2008-05-07 10:28 sunrisex [未注册用户]
@玉开

这个做法不错... 我些做一个一程序,在所有代码中的任何方法加入了代码,之前很难实现在代码推出时加入代码,只在进入时加了代码...
  回复  引用    
#18楼 [楼主] 2008-05-07 11:12 玉开      
@sunrisex
如果程序性能要求不高的话,你还可以用AOP截获,在方法执行开始时执行一些代码,在结束时执行一些代码。
  回复  引用  查看    
#19楼  2008-05-07 12:10 sunrisex [未注册用户]
@玉开

主要用于找出执行时间最多的地方,然后想办法优化...
  回复  引用    
#20楼  2008-05-07 14:09 rex xiang      
引用--------------------------------------------------
玉开: @rex xiang
如果只统计一个方法的性能,确实是没有必要的;若同时统计多个方法的性能则是完全有必要的。
--------------------------------------------------------
请仔细看我之前说的话, 不论你有多少统计方法, 都无需用所谓的StopwatchPool.
Stopwatch.GetTimestamp()是一个静态方法, 内部简单的包装了QueryPerformanceCounter.

即使多个统计方法也只需要维护一个存储着各个统计方法的[开始时间, 结束时间]的列表就好了. 恕我直言, 我真的看不出StopwatchPool的好处, 感觉它的存在很牵强.
  回复  引用  查看    
#21楼 [楼主] 2008-05-07 15:30 玉开      
to: rex xiang
仔细看了一下,确实没有必要。

多谢了。
  回复  引用  查看    
#22楼  2008-05-08 19:23 airwolf2026      
不错,好文章.学习了.
  回复  引用  查看    

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-05-06 10:13 编辑过


相关链接: