DSP

测量代码执行时间

2019-07-13 19:47发布

在Android开发当中,往往需要对一些代码进行性能的测试,通过计算代码的执行时间差往往是首选的方法,通常我们会选择System.currentTimeMillis()方法,然后与协调世界时间进行对比,得出执行的时间。但是今天,小编将介绍另外一种方法System.nanoTime(),可以精确测试代码的执行时间。 一、System.nanoTime()简介 此方法返回最精确的可用系统计时器的当前值,以毫秒为单位,只限用于测量已过的时间,与系统或钟表时间的其他任何时间无任何关系。返回值表示从某一固定但任意的时间算起的毫微秒数(或许从以后算起,所以该值可能为负)。此方法提供毫微秒的精度,但不是必要的毫微秒的准确度。它对于值的更改频率没有作出保证。在取值范围大于约 292 年(263 毫微秒)的连续调用的不同点在于:由于数字溢出,将无法准确计算已过的时间。二、使用方法 1.计算开始时间 long startTime = System.nanoTime(); 2.计算执行时间 long consumingTime = System.nanoTime() - startTime; 三、System.currentTimeMillis()与System.nanoTime()区别 System.currentTimeMillis()单位是毫秒(ms),而System.nanoTime()是纳秒(ns),计算更为精确。System.nanoTime()返回的可以是任意时间,甚至可以为负数,但是System.currentTimeMillis()的本质是相当于Date,用于计算相对于1970年1月1日之间的毫秒差。

常规计算方法运行时间

在一些耗时的操作中,我们需要计算方法运行了多少时间,以便于我们分析代码。常规的方法主要是在方法(函数)执行前得到当前时间戳 startTime ,方法执行后再次取当前时间 endTime , 方法运行时间 runTime = endtime - startTime ,见下面代码。 long startTime = System.currentTimeMillis(); //起始时间 doSomething(); long endTime = System.currentTimeMillis(); //结束时间 long runTime = endTime - startTime; Log.i("test", String.format("方法使用时间 %d ms", runTime)); 统计方法少的时候,这个方法还是比较好的,也就是做一些简单预算,但需要统计的方法多了,就要重写一些 long startTime 、 long endTime、System.currentTimeMillis() ,再简单的事情重复多了也会厌烦,就想着在上面的代码简单封装下,达到只调用一个方法,就能统计方法运行时间。

简单封装

先创建一个接口和一个方法。 public interface CallBack { //执行回调操作的方法 void doSometing(); } public static void countTime(CallBack callBack) { long startTime= System.currentTimeMillis(); //起始时间 callBack.doSometing(); ///进行回调操作 long endTime = System.currentTimeMillis(); //结束时间 log.i("test",String.format("方法使用时间 %d ms", endTime - startTime)); //打印使用时间 } 在需要计算时间的地方就可以调用了 , countTime(new CallBack() { @Override public void doSometing() { //耗时的操作方法 do(); } });

SystemClock

三种时钟都是有效的,它们不应该被混淆:
  1. System.currentTimeMillis()是一个标准的“墙”时钟(时间和日期),表示从纪元到现在的毫秒数。该墙时钟能够被用户或电话网络(见setCurrentTimeMillis(long))设置,所以该时间可能会向前或向后不可预知地跳越。该时钟应该仅仅被使用在当现实世界对应的日期和时间非常重要的情况下,比如日历或闹钟应用程序。而间隔时间和经历时间的测算应该使用不同的时钟。如果你使用System.currentTimeMillis(),当时间变化时时可以考虑监听ACTION为ACTION_TIME_TICK、ACTION_TIME_CHANGED和ACTION_TIMEZONE_CHANGED的广播。
  2. uptimeMillis()表示自系统启动时开始计时,以毫秒为单位。返回的是从系统启动到现在这个过程中的处于非休眠期的时间。当系统进入深度睡眠时(CPU关闭,设备变黑,等待外部输入装置)该时钟会停止。但是该时钟不会被时钟调整、闲置或其他节能机制所影响。这是大多数间隔时间的基本点,例如Thread.sleep(millls)、Object.wait(millis)和System.nanoTime()。该时钟被保证是单调的,适用于检测不包含休眠的间隔时间的情况。大多数接受一个时间戳值的方法认为是使用了uptimeMillis()时钟。
  3. elapsedRealtime() 和elapsedRealtimeNanos() 返回系统启动到现在的时间,包含设备深度休眠的时间。该时钟被确保单调,即使CPU在省电模式下,该时间也会继续计时。该时钟可以被使用在测量时间间隔时系统可能睡眠的时间段。
这有一些机制为了控制定时事件的:
  1. 标准的功能Thread.sleep(millis)Object.wait(millis)都总是合适的。这是功能使用uptimeMillis()时钟;如果该设备进入睡眠,剩余的时间将被推迟直到系统唤醒。这些同步功能可能被中断伴随Thread.interrupt()方法,并且你必须处理InterruptedException异常。
  2. SystemClock.sleep(millis)是一种实用工具函数变化类似于Thread.sleep(millis),但是它忽视了InterruptedException异常。使用该函数产生的延迟如果你不使用Thread.interrupt(),因为它会保存线程的中断状态。
  3. 处理程序类可以安排在绝对或相对时间异步回调。处理器类对象也使用uptimeMillis()时钟,并且需要一个eventloop(正常呈现在任何一个GUI应用程序上)。该AlarmManager可以触发一次或重复事件去发生即使在深睡眠或你的应用程序没有运行。事件可能有计划的发生伴随你的currentTimeMillis()(RTC)机会或elapsedRealtime()(ELAPSED_REALTIME),并且引起一个意图广播当它们发生时。
有几种机制可以控制事件发生的时间:
  1. 对标准的方法比如Thread.sleep(millis) 和Object.wait(millis)都是有效的,这些方法使用的是uptimeMillis()时钟,如果设备进入深度休眠,剩余的时间将被推迟直到系统唤醒。这些同步方法可能被Thread.interrupt()中断,你必须处理InterruptedException异常。
  2. SystemClock.sleep(millis)是一个类似于Thread.sleep(millis)的实用方法,但是它忽略InterruptedException异常(其实就是使用Thread.sleep,不过对异常进行了处理)。使用该函数会产生延迟,如果你不使用Thread.interrupt(),因为它会保存线程的中断状态。
  3. Handler类可以在一个相对或者绝对的时间设置异步回调,Handler对象也使用uptimeMillis()时钟,而且需要一个loop(经常出现在GUI程序中)。
  4. AlarmManager可以触发一次或重复事件,即使设备深度休眠或者应用程序没有运行。事件可以选择用currentTimeMillis()(RTC)或者elapsedRealtime()(ELAPSED_REALTIME)来设置时间,当事件发生会触发一个Intent广播。
  5. public方法:
static long currentThreadTimeMillis() 返在当前线程运行的毫秒数。 static long elapsedRealtime() 返回系统启动到现在的毫秒数,包含休眠时间。 static long elapsedRealtimeNanos() 返回系统启动到现在的纳秒数,包含休眠时间。 static boolean setCurrentTimeMillis(long millis) 设置当前挂钟时间,以毫秒为单位。需要调用进程具有相应的权限。 static void sleep(long ms) 等待给定的时间(uptimeMillis)。和Thread.sleep(millis)类似,但是它不会抛出InterruptedException异常。事件被推迟到下一个中断操作。该方法直到指定的时间过去才返回。 static long uptimeMillis() 返回系统启动到现在的毫秒级时间,不包含休眠时间。(系统启动到现在的非休眠期时间)