System.nanoTime()和System.currentTimeMillis()性能问题
之前给模块做性能优化的时候,需要将性能调到毫秒级,使用了System.nanoTime()和System.currentTimeMillis()对代码分片计时分析耗时操作,后发现在串行情况下性能达到毫秒级,但是一旦在并发压测的时候,性能急剧下降,后经多方排查,发现原因出在System.nanoTime()和System.currentTimeMillis()这两个api上,其在并发情况下耗时会急剧上升,当然在整体上看依然很快,但是在高性能场景下就有很显著的影响。特此记录一下。
测试代码:
package cord;
import java.util.concurrent.CountDownLatch;
/**
- Created by cord on 2018/5/7.
*/
public class SystemApiPerfTest {
public static void main(String[] args) throws InterruptedException {
int count = 100;
/**并发*/
long interval = concurrentTest(count, ()->{System.nanoTime();});
System.out.format("[%s] thread concurrent test <nanoTime> cost total time [%s]ns, average time [%s]ns.\n", count, interval, interval/count);
<span class="hljs-comment">/**串行循环*/</span>
interval = serialNanoTime(count);
System.out.format(<span class="hljs-string">"[%s] count serial test <nanoTime> cost total time [%s]ns, average time [%s]ns.\n"</span>, count, interval, interval/count);
System.out.println(<span class="hljs-string">"-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-"</span>);
<span class="hljs-comment">/**并发*/</span>
interval = concurrentTest(count, ()->{System.currentTimeMillis();});
System.out.format(<span class="hljs-string">"[%s] thread concurrent test <currentTimeMillis> cost total time [%s]ns, average time [%s]ns.\n"</span>, count, interval, interval/count);
<span class="hljs-comment">/**串行循环*/</span>
interval = serialCurrentTime(count);
System.out.format(<span class="hljs-string">"[%s] count serial test <currentTimeMillis> cost total time [%s]ns, average time [%s]ns.\n"</span>, count, interval, interval/count);
}
private static long concurrentTest(int threads, final Runnable r) throws InterruptedException {
final CountDownLatch start = new CountDownLatch(1);
final CountDownLatch end = new CountDownLatch(threads);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < threads; i++) {
<span class="hljs-keyword">new</span> Thread(() -> {
<span class="hljs-keyword">try</span> {
start.await();
<span class="hljs-keyword">try</span> {
r.run();
}<span class="hljs-keyword">finally</span> {
end.countDown();
}
} <span class="hljs-keyword">catch</span> (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
<span class="hljs-keyword">long</span> stime = System.nanoTime();
start.countDown();
end.await();
<span class="hljs-keyword">return</span> System.nanoTime() - stime;
}
private static long serialNanoTime(int count){
long stime = System.nanoTime();
for (int i = 0; i < count; i++) {
System.nanoTime();
}
return System.nanoTime() - stime;
}
private static long serialCurrentTime(int count){
long stime = System.nanoTime();
for (int i = 0; i < count; i++) {
System.currentTimeMillis();
}
return System.nanoTime() - stime;
}
}
测试结果如下:
[100] thread concurrent test <nanoTime> cost total time [5085539]ns, average time [50855]ns.
[100] count serial test <nanoTime> cost total time [2871]ns, average time [28]ns.
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
[100] thread concurrent test <currentTimeMillis> cost total time [7678769]ns, average time [76787]ns.
[100] count serial test <currentTimeMillis> cost total time [4103]ns, average time [41]ns.
串行情况下耗时趋于稳定,但是在并行情况下就不一样了。
因为这两个api都是native方法,涉及到系统层级的调用,与平台底层实现有关。
其实在串行情况下这两个api其实性能很好,但是在并发情况下回急剧下降,原因在于计时器在所有进程之间共享,并且其还一直在发生变化,当大量线程尝试同时去访问计时器的时候,就涉及到资源的竞争,于是也就出现并行效率远低于串行效率的现象了。所以在高并发场景下要慎重使用System.nanoTime()和System.currentTimeMillis()这两个API。
附加资料:
linux上使用的计时器一般有两种: TSC, HPET
HPET计时器(HPET Timer):高精度事件计时器,也是外部硬件计时器,固定频率14.31818MHz。
TSC计时器(TSC Timer):时间戳计数计时器,是基于硬件的计时器,但频率可变。以前它就等于处理器频率,在早些年不是问题,但后来处理器不断加入会降低频率的扩展频谱、电源管理等功能,就有问题了,于是后来设计的时候将其改为和处理器频率相独立。
HPET的性能相对TSC的性能要低
(注: 等级越高的时钟越容易被系统使用)
| 等级 | 1 ~ 99 | 100 ~ 199 | 200 ~ 299 | 300 ~ 399 | 400 ~ 499 |
|---|---|---|---|---|---|
| 特点 | 非常差的时钟源,只能作为最后的选择。如 jiffies | 基本可以使用但并非理想的时钟源。如 PIT | 正确可用的时钟源。如 ACPI PM Timer,HPET | 快速并且精确的时钟源。如 TSC | 理想时钟源。如 kvm_clock,xen_clock |
时钟源相关操作:
- 查看当前系统可用时钟源
# cat /sys/devices/system/clocksource/clocksource0/available_clocksource
- 查看当前使用的时钟源
# cat /sys/devices/system/clocksource/clocksource0/current_clocksource
- 修改时钟源
# echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource
http://pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html
http://blog.sina.com.cn/s/blog_71d9aee40101gtuv.html
https://blog.csdn.net/dymloveyxp1314/article/details/10065223
System.nanoTime()和System.currentTimeMillis()性能问题的更多相关文章
- System.nanoTime与System.currentTimeMillis的区别
平时产生随机数时我们经常拿时间做种子,比如用 System.currentTimeMillis的结果,但是在执行一些循环中使用了System.currentTimeMillis,那么每次的结 果将会差 ...
- System.nanoTime与System.currentTimeMillis比较
System.nanoTime与System.currentTimeMillis比较 currentTimeMillis返回的是系统当前时间和1970-01-01之前间隔时间的毫秒数,如果系统时间固 ...
- System.nanoTime与System.currentTimeMillis的理解与区别
System类代表系统,系统级的很多属性和控制方法都放置在该类的内部.该类位于java.lang包. 平时产生随机数时我们经常拿时间做种子,比如用System.currentTimeMillis的结果 ...
- System.nanoTime与System.currentTimeMillis
System.nanoTime提供相对精确的计时,但是不能用他来计算当前日期.(系统计时器的当前值,以毫微秒为单位) System.currentTimeMillis返回的是从1970.1.1 UTC ...
- System.nanoTime与System.currentTimeMillis的区别(转)
原文地址:http://blog.csdn.net/dliyuedong/article/details/8806868 平时产生随机数时我们经常拿时间做种子,比如用System.currentTim ...
- 我的Java开发学习之旅------>System.nanoTime与System.currentTimeMillis的区别
首先来看一道题:下面代码的输出结果是什么? import java.util.HashMap; import java.util.Map; public class HashMapTest { pub ...
- java: new Date().getTime() 与 System.currentTimeMillis() 与 System.nanoTime()
java使用new Date()和System.currentTimeMillis()获取当前时间戳 在开发过程中,通常很多人都习惯使用new Date()来获取当前时间,使用起来也比较方便,同时 ...
- java的System.currentTimeMillis()和System.nanoTime
纳秒 ns(nanosecond):纳秒, 时间单位.一秒的10亿分之一,即等于10的负9次方秒.常用作 内存读写速度的单位,其前面数字越小则表示速度越快. 1纳秒=1000 皮秒 1纳秒 = ...
- System.currentTimeMillis和System.nanoTime()
ns(nanosecond):纳秒, 时间单位.一秒的10亿分之一,即等于10的负9次方秒.常用作 内存读写速度的单位. 1纳秒=0.000001 毫秒 1纳秒=0.00000 0001秒 jav ...
随机推荐
- R语言-方差分析
方差分析指的是不同变量之间互相影响从而导致结果的变化 1.单因素方差分析: 案例:50名患者接受降低胆固醇治疗的药物,其中三种治疗条件使用药物相同(20mg一天一次,10mg一天两次,5mg一天四次) ...
- css的三种表现形式
1.行内样式(内嵌样式):结构的内部,即写在标签内的样式:写在标签的开始部分内部,style属性当中:<标记 style="样式的属性名1:样式的属性值1:属性名2:属性值2:.... ...
- VUE笔记 - 过滤器 Vue.filter 形参默认值 @keyup.f2 自定义按键修饰符
过滤器函数的传参: 第一个参数 A 是固定的,表示要过滤之前的内容. 第二个参数 B,表示要把原本的内容 A 过滤成 B. 写函数内容时, 这里第二处只写个参数. 实际的值要写到管道符调用函数的括号内 ...
- Fragment事务管理源代码分析
转载请标明出处:http://blog.csdn.net/shensky711/article/details/53132952 本文出自: [HansChen的博客] 概述 在Fragment使用中 ...
- JS中的发布订阅模式
一. 你是如何理解发布订阅模式的 JS中的设计模式: 单例模式:处理业务逻辑 构造原型模式:封装类库,组件,框架,插件等 类库:jQuery 只是提供了一些常用的方法,可以应用到任何的项目中,不具备业 ...
- jvisualvm 工具使用
VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe). https:// ...
- ThreadPoolExecutor – Java Thread Pool Example(如何使用Executor框架创建一个线程池)
Java thread pool manages the pool of worker threads, it contains a queue that keeps tasks waiting to ...
- 【ALearning】第四章 Android Layout组件布局(二)
前面我们分别介绍和学习了LinearLayout(线性布局).FrameLayout(单帧布局)和AbsoluteLayout(绝对布局).这次我们要进行RelativeLayout(相对布局)和Ta ...
- css3-13 css3的3D动画如何实现
css3-13 css3的3D动画如何实现 一.总结 一句话总结:这里是transform+setInterval实现.transform属性里面的rotate属性值变成rotateX或rotateY ...
- js cookie介绍和实例(用于自动登录,记住用户名等)
js cookie介绍和实例(用于自动登录,记住用户名等) 一.总结 1.cookie在客户端:因为js是最初是用来在客户端和服务器端进行通信使用的,所以客户端比如js可以操作cookie正常 2.c ...