注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

网易杭州 QA Team

务实 专注 分享 做有态度的QA

 
 
 
 
 

日志

 
 

[转]when systemcurrenttimemillis is too slow  

来自Linsa刘   2013-04-12 17:29:59|  分类: 性能测试 |举报 |字号 订阅

  下载LOFTER 我的照片书  |
原文: http://dow.ngra.de/2008/10/27/when-systemcurrenttimemillis-is-too-slow/

At the moment I am working on reducing the performance overhead of JavaRebel. Once I got rid of all the obvious bottlenecks and optimizations I fired up the profiler and started searching and destroying ad-hoc hotspots. After some time I got to the point, when the bottleneck was in System.currentTimeMillis().

This deserves some explanation. Since JavaRebel reloads code after the class changes, it needs to poll the actual class file for changes. The way we do it is quite lazy and the class itself causes the resource to be polled. However often there will be a lot of polling involved (more or less any method call on the class will cause a check), so we need to filter those calls. What we did is to check if some amount of time (e.g. 500 ms) has passed since the last check:

if( lastCheck +500 < System.currentTimeMillis() )
     return;
     lastCheck =System.currentTimeMillis();// Resource check code
After some optimization everything else got fast enough that this call started to weigh the performance down. System.currentTimeMillis() causes a system call and often a hardware poll, which is comparatively expensive. I needed some way to eliminate it.

After some thought I decided to cache the call. It may sound crazy to cache the current time, but I only needed the resolution of about half a second, so it was OK for me if the time was lagging behind about that much. What I did is put up a separate thread that would cache the current time and update it every half a sec.

public class TimeCacheThread extendsThread{
     public static volatile long currentTimeMillis =System.currentTimeMillis();
     public TimeCacheThread(){
          setDaemon(true);
     }
     static{
          new TimeCacheThread().start();
     }
     public void run(){
          while(true){      
               currentTimeMillis =System.currentTimeMillis();
               try{
                    Thread.sleep(500);
               }
               catch(InterruptedException e){
                    thrownewRuntimeException(e);
               }
          }
     }
}
Then the check became:

if(lastCheck +500< TimeCacheThread.currentTimeMillis) return;
lastCheck = TimeCacheThread.currentTimeMillis;// Resource check code
This check is amazingly fast, because instead of doing a system call we are just making a memory read.

The original code did not have the field declared volatile with the following explanation:

You may also notice the complete lack of any synchronization (or volatile keyword) on the currentTimeMillis static field. Since the type is primitive we can only deal with stale data here and I cared about performance more than I cared about missing a couple of checks.

There is a follow-up post (What do we really know about non-blocking concurrency in Java?) that explains some more of the reasoning behind initially omitting the volatile and then still putting it in. 

However once I got that far I decided I could do even better. The currentTimeMillis is of type long and we are still doing some arithmetic on it. In fact the only thing we want to know is if a quantum of time has passed since the last check. And we can do that by using a very simple heartbeat counter:

public class HeartBeatThread extendsThread{
     public static volatile int counter =0;
     public HeartBeatThread(){    
          setDaemon(true);
     }
     static{
          new HeartBeatThread().start();
     }
     publicvoid run(){
          while(true){
               try{
                    Thread.sleep(500);
               }
               catch(InterruptedException e){
                    throw new RuntimeException(e);
               }       
               counter++;
          }
     }
}

Now the checking code becomes just:

if(counter == HeartBeatThread.counter)
     return;
counter = HeartBeatThread.counter;

The counter field is now of int type and we only do an equals check on a DWORD, which should translate directly to 3 very fast hardware instructions (two memory reads and one conditional jump). Hopefully if you ever find yourself polling something a lot this post will come of help. As for me, it improved JavaRebel performance by 70% on some benchmarks
  评论这张
 
阅读(826)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016