1. 简单使用
//定义测量函数
def timer[R](block: => R): R = {
val t0 = System.nanoTime()//System.nanoTime为纳秒
val result = block // call-by-name
val t1 = System.nanoTime()
println("Elapsed time: " + (t1 - t0) + "ns")
result
}
//使用测量函数:
timer{
你想要跑的程序
}
//例:
timer{List.range(1,1000, 1)}
注意!!
在 JVM 中的某些运行时优化技术,可能会去除调用block 代码块的语句,那么得到的运行时间就不是真正的运行时间了。于是为了避免这种情况,可以把代码块的返回值,赋予名为 result 的 Volatile 的字段。修改代码如下:
@volatile var result: Any = _
//定义测量函数
def timer[R](block: => R): R = {
val t0 = System.nanoTime()//System.nanoTime为纳秒
val result = block // call-by-name
val t1 = System.nanoTime()
println("Elapsed time: " + (t1 - t0) + "ns")
result
}
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其他线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
2. 更通用的方法
//如下代码写入StopWatch.scala文件中
import java.util.concurrent.TimeUnit
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
/**
* 用于计算性能的秒表
*
*/
@SerialVersionUID(1L)
class StopWatch(name: String) extends Serializable {
private val stops = ListBuffer.empty[(Long, String)]
/**
* 开始秒表
*/
def start(): StopWatch = {
stops.append((System.currentTimeMillis(), "start"))
this
}
/**
* 一次计时打点
*
* @param description 对本次计时的描述
*/
def click(description: String): StopWatch = {
stops.append((System.currentTimeMillis(), description))
this
}
/**
* 停止表秒
*/
def stop(): StopWatch = {
stops.append((System.currentTimeMillis(), "stop"))
this
}
/**
* 重置秒表
*/
def reset(): StopWatch = {
stops.clear()
this
}
/**
* 输出耗时总结,会校验是否是有开始和结束的有效计时
*
* @return 返回总结结果,一次计时打点是一行
*/
def summary(): String = {
val stopsList = stops.toList
require(stopsList.head._2 == "start" && stopsList.last._2 == "stop", "StopWatch must call start and stop method!")
val summary = ArrayBuffer.empty[String]
summary.append(System.lineSeparator())
summary.append(s"Summary of StopWatch: $name")
val startTime = stopsList.head._1
val stopTime = stopsList.last._1
summary.append(s"Total elapse time: ${StopWatch.formatElapseTime(stopTime - startTime)}")
val restStops = stopsList.filter(click => click._2 != "start")
restStops.zipWithIndex.foreach {
case ((ts: Long, desc: String), index: Int) =>
val startTs = if (index > 0) restStops(index - 1)._1 else stopsList.head._1
summary.append(s"Click ${index + 1} [$desc]: elapse time is ${StopWatch.formatElapseTime(ts - startTs)}")
}
summary.toList.mkString(System.lineSeparator())
}
}
/**
* 伴随对象
*/
object StopWatch {
/**
* 定义 apply 函数
*
* @param name 秒表名称
* @return 返回实例
*/
def apply(name: String): StopWatch = new StopWatch(name)
/**
* 格式化耗时,转换成 x hour(s) y minute(s) z second(s)
*
* @param duration 耗时,单位毫秒
* @return 返回格式化的耗时字符串
*/
def formatElapseTime(duration: Long): String = {
val resultBuffer = ArrayBuffer.empty[String]
val hours = TimeUnit.MILLISECONDS.toHours(duration)
resultBuffer.append(
if (hours > 1) s"$hours hours "
else if (hours > 0) s"$hours hour "
else ""
)
var dur = duration - TimeUnit.HOURS.toMillis(hours)
val minutes = TimeUnit.MILLISECONDS.toMinutes(dur)
resultBuffer.append(
if (minutes > 1) s"$minutes minutes "
else if (minutes > 0) s"$minutes minute "
else if (hours > 0) "0 minute "
else ""
)
dur = dur - TimeUnit.MINUTES.toMillis(minutes)
val seconds = TimeUnit.MILLISECONDS.toSeconds(dur)
resultBuffer.append(
if (seconds > 1) s"$seconds seconds"
else if (seconds > 0) s"$seconds second"
else "0 second"
)
resultBuffer.mkString("")
}
}
//使用:
val stopWatch = new StopWatch("你的程序名").start()
...//跑一些代码
stopWatch.click("中途标识说明")
...//跑一些代码
println(s"耗时:\n${stopWatch.stop().summary()}")
参考:
http://biercoff.com/easily-measuring-code-execution-time-in-scala/
https://www.zybuluo.com/runzhliu/note/1121568