如何分析在斯卡拉的方法呢?如何分析在斯卡拉的方法呢?(How to profile methods

2019-05-12 20:56发布

什么是剖析Scala的方法调用的标准方法?

我需要的是钩周围的方法,利用它,我可以用它来启动和停止计时器。

在Java中我使用方面的编程,AspectJ中,定义要成型的方法和注入字节码来实现相同的。

有没有在斯卡拉,更自然的方式,我可以定义一组函数之前,没有在这个过程中丢失任何静态类型的功能之后要叫什么?

Answer 1:

你要做到这一点没有改变,你要测量定时的代码? 如果你不介意改变代码,那么你可以做这样的事情:

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 }


Answer 2:

除了加斯帕的回答,您可以自动换行的方法调用在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


Answer 3:

有斯卡拉3个标杆库 ,你可以利用的。

由于链接的网站上的网址可能会改变,我下面粘贴相关内容。

  1. SPerformance -性能测试框架,旨在自动地比较性能测试和简单生成工具容器内工作。

  2. 斯卡拉-基准模板 - SBT用于创建基于卡尺斯卡拉(微)基准模板项目。

  3. 度量 -捕捉JVM-和应用程序级的指标。 所以,你知道这是怎么回事



Answer 4:

这是我使用:

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)


Answer 5:

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


Answer 6:

我把解决方案从加斯帕并增加了一些聚集,它在同一代码的多个运行

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_newcounter_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

但愿这是有帮助



Answer 7:

我使用的技术,很容易在代码块走动。 症结是完全相同的行开始和结束的计时器 - 所以它确实是一个简单的复制和粘贴。 另一个好处是,你能定义什么时机对你意味着作为一个字符串,所有在同一行。

实例:

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()
  }

优点:

  • 不需要包裹代码作为一个块或行内操纵
  • 可以很容易地被探索时移至间代码行定时器的开始和结束

缺点:

  • 为完全功能码少光泽
  • 显然这个对象泄漏映射条目,如果你不这样做“亲密”的计时器,例如,如果你的代码不获得为给定的计时器开始第二次调用。


Answer 8:

我喜欢@扭伤的回答简单,但也希望:

  • 探查处理循环(用于一致和方便)

  • 更精确的定时(使用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
}


Answer 9:

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


Answer 10:

而站在巨人的肩膀上......

实第三方库会比较理想,但如果你需要一些基础快速STD-库,以下变型提供:

  • 重复
  • 最后的结果赢得了多次重复
  • 总时间和平均时间多次重复
  • 删除需要时间/即时提供商为PARAM

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> 


Answer 11:

你可以使用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。



文章来源: How to profile methods in Scala?