如何在 Scala 中分析方法?

发布于 2025-01-03 01:30:45 字数 186 浏览 1 评论 0原文

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

我需要的是一个方法的钩子,我可以用它来启动和停止计时器。

在Java中,我使用切面编程aspectJ来定义要分析的方法并注入字节码来实现相同的目的。

Scala 中是否有一种更自然的方法,我可以定义一堆在函数之前和之后调用的函数,而不会在过程中丢失任何静态类型?

What is a standard way of profiling Scala method calls?

What I need are hooks around a method, using which I can use to start and stop Timers.

In Java I use aspect programming, aspectJ, to define the methods to be profiled and inject bytecode to achieve the same.

Is there a more natural way in Scala, where I can define a bunch of functions to be called before and after a function without losing any static typing in the process?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

丢了幸福的猪 2025-01-10 01:30:45

您想在不更改要测量时序的代码的情况下执行此操作吗?如果您不介意更改代码,那么您可以执行以下操作:

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 }

Do you want to do this without changing the code that you want to measure timings for? If you don't mind changing the code, then you could do something like this:

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 }
束缚m 2025-01-10 01:30:45

除了 Jesper 的答案之外,您还可以在 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

我不知道为什么

从 2.12 开始打印的内容会更新 5 次。 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

In addition to Jesper's answer, you can automatically wrap method invocations in the 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

Now - let's wrap anything in this

scala> :wrap time
wrap: no such command.  Type :help for help.

OK - we need to be in power mode

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

Wrap away

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

I have no idea why that printed stuff out 5 times

Update as of 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
枫林﹌晚霞¤ 2025-01-10 01:30:45

这是我用的:

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)

This what I use:

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)
执笔绘流年 2025-01-10 01:30:45

前 Scala wiki 的“工具和库”页面中提到的三个 Scala 基准测试库

  1. SPerformance - 性能测试框架,旨在自动比较性能测试并在 Simple Build Tool 中工作。

  2. scala-benchmarking-template - 用于创建 Scala 的 SBT 模板项目(微型)基于 Caliper 的基准测试。

  3. 指标 - 捕获 JVM 和应用程序级别的指标。所以你知道发生了什么

截至 2024 年,这些库中唯一仍在维护的库是 Metrics

There were three benchmarking libraries for Scala mentioned on the "Tools and Libraries" page of the former Scala wiki:

  1. SPerformance - Performance Testing framework aimed at automagically comparing performance tests and working inside Simple Build Tool.

  2. scala-benchmarking-template - SBT template project for creating Scala (micro-)benchmarks based on Caliper.

  3. Metrics - Capturing JVM- and application-level metrics. So you know what's going on

As of 2024, the only one of these libraries that is still maintained is Metrics.

沉溺在你眼里的海 2025-01-10 01:30:45

推荐的 Scala 代码基准测试方法是通过 sbt-jmh

“不要相信任何人,把一切都放在板凳上。” - JMH 的 sbt 插件(Java
微基准测试线束)

许多主要的 Scala 项目都采用这种方法,例如

  • Scala 编程语言本身
  • Dotty (Scala 3)
  • cats 用于函数式编程的库
  • Metals 用于 IDE 的语言服务器

基于 的简单包装计时器System.nanoTime不是可靠的基准测试方法

System.nanoTime 现在和 String.intern 一样糟糕:你可以使用它,
但要明智地使用它。延迟、粒度和可扩展性影响
如果完成的话,计时器引入的可能并且将会影响您的测量
没有适当的严格性。这是众多原因之一
System.nanoTime 应通过基准测试从用户那里抽象出来
框架

此外,诸如JIT预热、垃圾收集、系统范围事件等注意 等可能会引入不可预测性到测量中:

需要减轻大量影响,包括预热、死代码
消除、分叉等。幸运的是,JMH 已经处理了许多问题
事物,并且具有 Java 和 Scala 的绑定。

基于特拉维斯·布朗的回答,这是一个如何为 Scala 设置 JMH 基准的示例

  1. 将 jmh 添加到 project/plugins.sbt
    addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7")
    
  2. build.sbt中启用jmh插件
    enablePlugins(JmhPlugin)
    
  3. 添加到src/main/scala/bench/VectorAppendVsListPrependAndReverse.scala

    包台
    
    导入 org.openjdk.jmh.annotations._
    
    @State(范围.基准)
    @BenchmarkMode(Array(Mode.AverageTime))
    类 VectorAppendVsListPrependAndReverse {
      验证值大小 = 1_000_000
      val 输入 = 1 到大小
    
      @Benchmark def vectorAppend: Vector[Int] = 
        input.foldLeft(Vector.empty[Int])({ case (acc, next) => acc.append(next)})
    
      @Benchmark def listPrependAndReverse: 列表[Int] = 
        input.foldLeft(List.empty[Int])({ case (acc, next) => acc.prepending(next)}).reverse
    }
    
  4. 执行基准测试
    sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 bench.VectorAppendVsListPrependAndReverse"
    

结果

Benchmark                                                   Mode  Cnt  Score   Error  Units
VectorAppendVsListPreppendAndReverse.listPrependAndReverse  avgt   20  0.024 ± 0.001   s/op
VectorAppendVsListPreppendAndReverse.vectorAppend           avgt   20  0.130 ± 0.003   s/op

似乎表明先添加到 List 然后在最后反转它比继续附加到 Vector 快一个数量级>。

The recommended approach to benchmarking Scala code is via sbt-jmh

"Trust no one, bench everything." - sbt plugin for JMH (Java
Microbenchmark Harness)

This approach is taken by many of the major Scala projects, for example,

  • Scala programming language itself
  • Dotty (Scala 3)
  • cats library for functional programming
  • Metals language server for IDEs

Simple wrapper timer based on System.nanoTime is not a reliable method of benchmarking:

System.nanoTime is as bad as String.intern now: you can use it,
but use it wisely. The latency, granularity, and scalability effects
introduced by timers may and will affect your measurements if done
without proper rigor. This is one of the many reasons why
System.nanoTime should be abstracted from the users by benchmarking
frameworks

Furthermore, considerations such as JIT warmup, garbage collection, system-wide events, etc. might introduce unpredictability into measurements:

Tons of effects need to be mitigated, including warmup, dead code
elimination, forking, etc. Luckily, JMH already takes care of many
things, and has bindings for both Java and Scala.

Based on Travis Brown's answer here is an example of how to setup JMH benchmark for Scala

  1. Add jmh to project/plugins.sbt
    addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7")
    
  2. Enable jmh plugin in build.sbt
    enablePlugins(JmhPlugin)
    
  3. Add to src/main/scala/bench/VectorAppendVsListPreppendAndReverse.scala

    package bench
    
    import org.openjdk.jmh.annotations._
    
    @State(Scope.Benchmark)
    @BenchmarkMode(Array(Mode.AverageTime))
    class VectorAppendVsListPreppendAndReverse {
      val size = 1_000_000
      val input = 1 to size
    
      @Benchmark def vectorAppend: Vector[Int] = 
        input.foldLeft(Vector.empty[Int])({ case (acc, next) => acc.appended(next)})
    
      @Benchmark def listPrependAndReverse: List[Int] = 
        input.foldLeft(List.empty[Int])({ case (acc, next) => acc.prepended(next)}).reverse
    }
    
  4. Execute benchmark with
    sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 bench.VectorAppendVsListPreppendAndReverse"
    

The results are

Benchmark                                                   Mode  Cnt  Score   Error  Units
VectorAppendVsListPreppendAndReverse.listPrependAndReverse  avgt   20  0.024 ± 0.001   s/op
VectorAppendVsListPreppendAndReverse.vectorAppend           avgt   20  0.130 ± 0.003   s/op

which seems to indicate prepending to a List and then reversing it at the end is order of magnitude faster than keep appending to a Vector.

七月上 2025-01-10 01:30:45

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 might be useful.

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
久夏青 2025-01-10 01:30:45

我使用了一种易于在代码块中移动的技术。关键在于完全相同的行开始和结束计时器 - 所以这实际上是一个简单的复制和粘贴。另一个好处是,您可以将时间对您的意义定义为字符串,所有这些都在同一行中。

示例用法:

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

优点:

  • 无需将代码包装为块或在行内进行操作,
  • 在进行探索时可以轻松地在代码行之间移动计时器的开始和结束

缺点:

  • 对于完全功能性的代码来说不太闪亮
  • ,显然该对象泄漏了映射条目如果你不“关闭”计时器,
    例如,如果您的代码没有到达给定计时器启动的第二次调用。

I use a technique that's easy to move around in code blocks. The crux is that the same exact line starts and ends the timer - so it is really a simple copy and paste. The other nice thing is that you get to define what the timing means to you as a string, all in that same line.

Example usage:

Timelog("timer name/description")
//code to time
Timelog("timer name/description")

The code:

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

Pros:

  • no need to wrap code as a block or manipulate within lines
  • can easily move the start and end of the timer among code lines when being exploratory

Cons:

  • less shiny for utterly functional code
  • obviously this object leaks map entries if you do not "close" timers,
    e.g. if your code doesn't get to the second invocation for a given timer start.
一曲爱恨情仇 2025-01-10 01:30:45

ScalaMeter 是一个很好的库,可以在 Scala 中执行基准测试

下面是一个简单的示例

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

ScalaMeter is a nice library to perform benchmarking in Scala

Below is a simple example

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

If you execute above code snippet in Scala Worksheet you get the running time in milliseconds

execution_time: org.scalameter.Quantity[Double] = 0.260325 ms
记忆消瘦 2025-01-10 01:30:45

我从 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_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

希望这有帮助

I took the solution from Jesper and added some aggregation to it on multiple run of the same code

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

Suppose you want to time two functions counter_new and counter_old, the following is the usage:

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

Hopefully this is helpful

美煞众生 2025-01-10 01:30:45

我喜欢@wrick答案的简单性,但也想要:

  • 探查器处理循环(为了一致性和便利性)

  • 更准确的计时(使用nanoTime)

  • 每次迭代的时间(不是所有迭代的总时间)

  • 只返回 ns/iteration - 不是一个tuple

这是在这里实现的:

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
}

I like the simplicity of @wrick's answer, but also wanted:

  • the profiler handles looping (for consistency and convenience)

  • more accurate timing (using nanoTime)

  • time per iteration (not total time of all iterations)

  • just return ns/iteration - not a tuple

This is achieved here:

def profile[R] (repeat :Int)(code: => R, t: Long = System.nanoTime) = { 
  (1 to repeat).foreach(i => code)
  (System.nanoTime - t)/repeat
}

For even more accuracy, a simple modification allows a JVM Hotspot warmup loop (not timed) for timing small snippets:

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
}
心欲静而疯不止 2025-01-10 01:30:45

您可以使用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来代替它。

You can use 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
}

Usage:

time{
    //execute somethings here, like methods, or some codes.
}  

nanoTime will show you ns, so it will hard to see. So I suggest that you can use currentTimeMillis instead of it.

风筝有风,海豚有海 2025-01-10 01:30:45

站在巨人的肩膀上...

一个可靠的第 3 方库会更理想,但如果您需要快速且基于标准库的东西,可以使用以下变体:

  • 重复
  • 最后结果获胜重复
  • 多次重复的总时间和平均时间
  • 不再需要时间/即时提供程序作为 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> 

While standing on the shoulders of giants...

A solid 3rd-party library would be more ideal, but if you need something quick and std-library based, following variant provides:

  • Repetitions
  • Last result wins for multiple repetitions
  • Total time and average time for multiple repetitions
  • Removes the need for time/instant provider as a 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
  }
}

Also worth noting you can use the Duration.toCoarsest method to convert to the biggest time unit possible, although I am not sure how friendly this is with minor time difference between runs e.g.

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> 
雨落□心尘 2025-01-10 01:30:45

添加=>方法名称和;秒

profile[R](block: => R,methodName : String): R = {
    val n = System.nanoTime()
    val result = block
    val n1 = System.nanoTime()
    println(s"Elapsed time: ${TimeUnit.MILLISECONDS.convert(n1 - n,TimeUnit.NANOSECONDS)}ms - MethodName: ${methodName}")
    result
  }

adding on => method with name & seconds

profile[R](block: => R,methodName : String): R = {
    val n = System.nanoTime()
    val result = block
    val n1 = System.nanoTime()
    println(s"Elapsed time: ${TimeUnit.MILLISECONDS.convert(n1 - n,TimeUnit.NANOSECONDS)}ms - MethodName: ${methodName}")
    result
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文