什麼是剖析Scala方法調用的標準方法?如何在Scala中剖析方法?
我需要的是一個方法的鉤子,我可以用它來啓動和停止定時器。
在Java中,我使用方面編程aspectJ來定義要分析的方法並注入字節碼以實現相同。
在Scala中有沒有更自然的方法,在那裏我可以定義一堆函數在函數前後被調用,而不會在程序中丟失任何靜態類型?
什麼是剖析Scala方法調用的標準方法?如何在Scala中剖析方法?
我需要的是一個方法的鉤子,我可以用它來啓動和停止定時器。
在Java中,我使用方面編程aspectJ來定義要分析的方法並注入字節碼以實現相同。
在Scala中有沒有更自然的方法,在那裏我可以定義一堆函數在函數前後被調用,而不會在程序中丟失任何靜態類型?
是否要在不更改要測量計時的代碼的情況下執行此操作?如果你不介意改變代碼,那麼你可以做這樣的事情:
def time[R](block: => R): R = {
val t0 = System.nanoTime()
val result = block // call-by-name
val t1 = System.nanoTime()
println("Elapsed time: " + (t1 - t0) + "ns")
result
}
// Now wrap your method calls, for example change this...
val result = 1 to 1000 sum
// ... into this
val result = time { 1 to 1000 sum }
除了加斯帕的回答,您可以自動換行的方法調用在REPL:
scala> def time[R](block: => R): R = {
| val t0 = System.nanoTime()
| val result = block
| println("Elapsed time: " + (System.nanoTime - t0) + "ns")
| result
| }
time: [R](block: => R)R
現在 - 讓我們換東西在這個
scala> :wrap time
wrap: no such command. Type :help for help.
OK - 我們需要在電源模式
scala> :power
** Power User mode enabled - BEEP BOOP SPIZ **
** :phase has been set to 'typer'. **
** scala.tools.nsc._ has been imported **
** global._ and definitions._ also imported **
** Try :help, vals.<tab>, power.<tab> **
裹走
scala> :wrap time
Set wrapper to 'time'
scala> BigDecimal("1.456")
Elapsed time: 950874ns
Elapsed time: 870589ns
Elapsed time: 902654ns
Elapsed time: 898372ns
Elapsed time: 1690250ns
res0: scala.math.BigDecimal = 1.456
我不知道爲什麼會打印的東西了5倍
更新爲2.12.2:
scala> :pa
// Entering paste mode (ctrl-D to finish)
package wrappers { object wrap { def apply[A](a: => A): A = { println("running...") ; a } }}
// Exiting paste mode, now interpreting.
scala> $intp.setExecutionWrapper("wrappers.wrap")
scala> 42
running...
res2: Int = 42
爲了免除任何人現在想知道的麻煩,':wrap'功能[已被刪除](https://groups.google.com/forum/#!msg/scala-user/cE-z6fxkq0U/ EszYr_19qgMJ): - \ – ches 2016-05-04 16:28:08
@ches參閱'$ intp.setExecutionWrapper'的更新。 – 2017-06-01 16:55:06
testing.Benchmark
可能是有用的。
scala> def testMethod {Thread.sleep(100)}
testMethod: Unit
scala> object Test extends testing.Benchmark {
| def run = testMethod
| }
defined module Test
scala> Test.main(Array("5"))
$line16.$read$$iw$$iw$Test$ 100 100 100 100 100
請注意,testing.Benchmark是@deprecated(「此類將被刪除。」,「2.10.0」)。 – Tvaroh 2013-08-23 21:37:38
有three benchmarking libraries for Scala,你可以利用。
由於鏈接網站上的網址可能會發生變化,因此我正在粘貼以下相關內容。
SPerformance - 性能測試框架,旨在自動地比較性能測試和簡單生成工具容器內工作。
scala-benchmarking-template - 用於基於Caliper創建Scala(微)基準的SBT模板項目。
Metrics - 捕獲JVM和應用程序級度量。所以,你知道這是怎麼回事
這是我使用:
import System.nanoTime
def profile[R](code: => R, t: Long = nanoTime) = (code, nanoTime - t)
// usage:
val (result, time) = profile {
/* block of code to be profiled*/
}
val (result2, time2) = profile methodToBeProfiled(foo)
我使用的技術,很容易在代碼塊左右移動。關鍵是相同的確切線開始和結束計時器 - 所以它是一個簡單的複製和粘貼。另一件好事是,你可以定義什麼時間對你來說意味着一個字符串,都在同一行。
實例:
Timelog("timer name/description")
//code to time
Timelog("timer name/description")
的代碼:
object Timelog {
val timers = scala.collection.mutable.Map.empty[String, Long]
//
// Usage: call once to start the timer, and once to stop it, using the same timer name parameter
//
def timer(timerName:String) = {
if (timers contains timerName) {
val output = s"$timerName took ${(System.nanoTime() - timers(timerName))/1000/1000} milliseconds"
println(output) // or log, or send off to some performance db for analytics
}
else timers(timerName) = System.nanoTime()
}
優點:
缺點:
這很好,但不應該是:'Timelog.timer(「timer name/description」)'? – schoon 2017-09-11 14:48:48
我喜歡@扭傷的回答簡單,但也想:
探查處理循環(一致性和便利)
更準確的計時(使用nanoTime)
每次迭代的時間(不是所有迭代的總時間)
只返回NS /迭代 - 不是一個元組
這是在這裏實現:
def profile[R] (repeat :Int)(code: => R, t: Long = System.nanoTime) = {
(1 to repeat).foreach(i => code)
(System.nanoTime - t)/repeat
}
爲了更準確,簡單的修改允許JVM熱點預熱循環(不限時)進行計時小摘要:
def profile[R] (repeat :Int)(code: => R) = {
(1 to 10000).foreach(i => code) // warmup
val start = System.nanoTime
(1 to repeat).foreach(i => code)
(System.nanoTime - start)/repeat
}
而站在巨人的肩膀上......
實第三方庫會比較理想,但如果你需要根據東西快速和std庫,以下變型提供:
。
import scala.concurrent.duration._
import scala.language.{postfixOps, implicitConversions}
package object profile {
def profile[R](code: => R): R = profileR(1)(code)
def profileR[R](repeat: Int)(code: => R): R = {
require(repeat > 0, "Profile: at least 1 repetition required")
val start = Deadline.now
val result = (1 until repeat).foldLeft(code) { (_: R, _: Int) => code }
val end = Deadline.now
val elapsed = ((end - start)/repeat)
if (repeat > 1) {
println(s"Elapsed time: $elapsed averaged over $repeat repetitions; Total elapsed time")
val totalElapsed = (end - start)
println(s"Total elapsed time: $totalElapsed")
}
else println(s"Elapsed time: $elapsed")
result
}
}
另外值得注意的您可以使用Duration.toCoarsest
方法轉換到最大的時間單位可能的,雖然我不知道這是多麼友好與運行例如之間微小的時間差
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.concurrent.duration._
import scala.concurrent.duration._
scala> import scala.language.{postfixOps, implicitConversions}
import scala.language.{postfixOps, implicitConversions}
scala> 1000.millis
res0: scala.concurrent.duration.FiniteDuration = 1000 milliseconds
scala> 1000.millis.toCoarsest
res1: scala.concurrent.duration.Duration = 1 second
scala> 1001.millis.toCoarsest
res2: scala.concurrent.duration.Duration = 1001 milliseconds
scala>
您可以使用System.currentTimeMillis
:
def time[R](block: => R): R = {
val t0 = System.currentTimeMillis()
val result = block // call-by-name
val t1 = System.currentTimeMillis()
println("Elapsed time: " + (t1 - t0) + "ms")
result
}
用法:
time{
//execute somethings here, like methods, or some codes.
}
nanoTime會告訴你ns
,所以它會很難看。所以我建議你可以使用currentTimeMillis來代替它。
難以辨認的納秒很難在兩者之間選擇。除了分辨率之外還有一些重要的差異首先,currentTimeMillis可以在OS週期性執行的時鐘調整期間改變甚至倒退。另一個是nanoTime可能不是線程安全的:http://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime – Chris 2016-10-12 00:07:58
ScalaMeter是在斯卡拉執行基準一個很好的圖書館
下面是一個簡單的例子
import org.scalameter._
def sumSegment(i: Long, j: Long): Long = (i to j) sum
val (a, b) = (1, 1000000000)
val execution_time = measure { sumSegment(a, b) }
如果執行上述Scala的工作表的代碼片段你以毫秒爲單位的運行時間
execution_time: org.scalameter.Quantity[Double] = 0.260325 ms
我從Jesper那裏得到了解決方案,並在多次運行相同的代碼時向它添加了一些聚合
def time[R](block: => R) = {
def print_result(s: String, ns: Long) = {
val formatter = java.text.NumberFormat.getIntegerInstance
println("%-16s".format(s) + formatter.format(ns) + " ns")
}
var t0 = System.nanoTime()
var result = block // call-by-name
var t1 = System.nanoTime()
print_result("First Run", (t1 - t0))
var lst = for (i <- 1 to 10) yield {
t0 = System.nanoTime()
result = block // call-by-name
t1 = System.nanoTime()
print_result("Run #" + i, (t1 - t0))
(t1 - t0).toLong
}
print_result("Max", lst.max)
print_result("Min", lst.min)
print_result("Avg", (lst.sum/lst.length))
}
假設你想不想來一次兩個功能counter_new
和counter_old
,下面是用法:
scala> time {counter_new(lst)}
First Run 2,963,261,456 ns
Run #1 1,486,928,576 ns
Run #2 1,321,499,030 ns
Run #3 1,461,277,950 ns
Run #4 1,299,298,316 ns
Run #5 1,459,163,587 ns
Run #6 1,318,305,378 ns
Run #7 1,473,063,405 ns
Run #8 1,482,330,042 ns
Run #9 1,318,320,459 ns
Run #10 1,453,722,468 ns
Max 1,486,928,576 ns
Min 1,299,298,316 ns
Avg 1,407,390,921 ns
scala> time {counter_old(lst)}
First Run 444,795,051 ns
Run #1 1,455,528,106 ns
Run #2 586,305,699 ns
Run #3 2,085,802,554 ns
Run #4 579,028,408 ns
Run #5 582,701,806 ns
Run #6 403,933,518 ns
Run #7 562,429,973 ns
Run #8 572,927,876 ns
Run #9 570,280,691 ns
Run #10 580,869,246 ns
Max 2,085,802,554 ns
Min 403,933,518 ns
Avg 797,980,787 ns
希望這是有幫助的
如果AspectJ的使用Scala很好地發揮,用AspectJ。爲什麼重新發明輪子?以上使用自定義流量控制的答案無法達到AOP的基本要求,因爲使用它們需要修改代碼。這些也可能是有趣的:http://java.dzone.com/articles/real-world-scala-managing-cros http://blog.fakod.eu/2010/07/26/cross-cutting-concerns- in-scala/ – 2012-02-19 14:56:25
- https://chariotsolutions.com/blog/post/microbenchmarking-scala-with-jmh/ - https://github.com/wadejensen/sbt-jmh-example - https:// github .com/ktoso/sbt-jmh – 2017-12-29 03:49:55