How to profile methods in 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?


Solution 1:

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 }

Solution 2:

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

Solution 3:

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)

Solution 4:

There are three benchmarking libraries for Scala that you can avail of.

Since the URLs on the linked site are likely to change, I am pasting the relevant content below.

  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