​ 之前给模块做性能优化的时候,需要将性能调到毫秒级,使用了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 &lt;nanoTime&gt; 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, ()-&gt;{System.currentTimeMillis();});
    System.out.format(<span class="hljs-string">"[%s] thread concurrent test &lt;currentTimeMillis&gt; 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 &lt;currentTimeMillis&gt; 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 &lt; threads; i++) {
    <span class="hljs-keyword">new</span> Thread(() -&gt; {
    <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

http://news.mydrivers.com/1/273/273867_all.htm

System.nanoTime()和System.currentTimeMillis()性能问题的更多相关文章

  1. System.nanoTime与System.currentTimeMillis的区别

    平时产生随机数时我们经常拿时间做种子,比如用 System.currentTimeMillis的结果,但是在执行一些循环中使用了System.currentTimeMillis,那么每次的结 果将会差 ...

  2. System.nanoTime与System.currentTimeMillis比较

    System.nanoTime与System.currentTimeMillis比较 ​currentTimeMillis返回的是系统当前时间和1970-01-01之前间隔时间的毫秒数,如果系统时间固 ...

  3. System.nanoTime与System.currentTimeMillis的理解与区别

    System类代表系统,系统级的很多属性和控制方法都放置在该类的内部.该类位于java.lang包. 平时产生随机数时我们经常拿时间做种子,比如用System.currentTimeMillis的结果 ...

  4. System.nanoTime与System.currentTimeMillis

    System.nanoTime提供相对精确的计时,但是不能用他来计算当前日期.(系统计时器的当前值,以毫微秒为单位) System.currentTimeMillis返回的是从1970.1.1 UTC ...

  5. System.nanoTime与System.currentTimeMillis的区别(转)

    原文地址:http://blog.csdn.net/dliyuedong/article/details/8806868 平时产生随机数时我们经常拿时间做种子,比如用System.currentTim ...

  6. 我的Java开发学习之旅------>System.nanoTime与System.currentTimeMillis的区别

    首先来看一道题:下面代码的输出结果是什么? import java.util.HashMap; import java.util.Map; public class HashMapTest { pub ...

  7. java: new Date().getTime() 与 System.currentTimeMillis() 与 System.nanoTime()

    java使用new Date()和System.currentTimeMillis()获取当前时间戳   在开发过程中,通常很多人都习惯使用new Date()来获取当前时间,使用起来也比较方便,同时 ...

  8. java的System.currentTimeMillis()和System.nanoTime

    纳秒 ns(nanosecond):纳秒, 时间单位.一秒的10亿分之一,即等于10的负9次方秒.常用作 内存读写速度的单位,其前面数字越小则表示速度越快.   1纳秒=1000 皮秒   1纳秒 = ...

  9. System.currentTimeMillis和System.nanoTime()

    ns(nanosecond):纳秒, 时间单位.一秒的10亿分之一,即等于10的负9次方秒.常用作 内存读写速度的单位.  1纳秒=0.000001 毫秒  1纳秒=0.00000 0001秒 jav ...

随机推荐

  1. Impala基础认知与安装

    一.Impala简介 Cloudera Impala对你存储在Apache Hadoop在HDFS,HBase的数据提供直接查询互动的SQL.除了像Hive使用相同的统一存储平台,Impala也使用相 ...

  2. Python 极简教程(七)列表 list

    由于列表过于重要,请认真看完并保证所有代码都敲过一遍. 什么是列表 列表是 Python 中最常用的数据结构,也是一种数据类型,其样式如下: li = [1, 2, 3, 'a', 'b'] 列表是一 ...

  3. Oracle学习总结(9)—— Oracle 常用的基本操作

    创建用户,相当于在sqlServer中创建一个数据库  create user 用户名 identified by 密码  修改用户密码  alter user 用户名 identified by 新 ...

  4. 基于深度学习的目标检测(object detection)—— rcnn、fast-rcnn、faster-rcnn

    模型和方法: 在深度学习求解目标检测问题之前的主流 detection 方法是,DPM(Deformable parts models), 度量与评价: mAP:mean Average Precis ...

  5. hbase单机安装和简单使用

    电脑太卡了,使用不了hadoop和hdfs了,所以今天安装了一个伪分布式,数据存储在本地磁盘,也没有向HDFS中存,也没有使用自己的zookeeper,安装过程中还出了点小问题,总结一下,免得忘了. ...

  6. 关于LWIP断开网线后重连问题(热插拔问题)

    近期在弄STM32+LWIP协议.在网络拔掉网线情况下.无法又一次连接. 网上找了好多方法都没有实现,着实郁闷! 后来无意间看到了临时解决这一问题的方法.尽管不是那么完美,但最算能解决这个问题.分享给 ...

  7. 【习题 5-8 UVA - 230】Borrowers

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 用map+set写个模拟就好. 3个区域 书架.桌子.别人的手上. 其中前两个区域的书都能借出去. [代码] #include &l ...

  8. 【EasyUi】页面设计必学之Layout

    接触EasyUi也快一年了.非常多时候都把重心放在实现功能方面.要显示大量数据了就用DataGrid,要实现分页效果了就想着Tabs,如此等等,再接下来就是考虑CSS.js怎样让这个功能实现的更好. ...

  9. 结合Wireshark捕获分组深入理解TCP/IP协议之以太网帧

    摘要:     本文摘抄并整理了以太网相关理论知识,包括CSMA/CD协议机制及工作.LAN互连,详细分析了Ethernet II帧格式,最后给出Ethernet II帧实例. 一.以太网[1] 1. ...

  10. ARM+linux学习过程(1)虚拟机下ubuntu上网

    总结:(1)通过bridge方式也可以实现ubuntu上网(只要PC物理网卡能上网),可以实现ping通主机和开发板 (2)要想上网简单上网,可以通过nat方式,在vmware中设置为nat方式,选择 ...