This small Scala library can be used to determine application performance bottlenecks, user/application interactions, and application scalability. Each stopwatch gathers summary statistics such as hits, execution times (total, average, minimum, maximum, standard deviation), distribution, simultaneous requests or concurrent threads.
import stopwatch.Stopwatch
// A stopwatch surrounds your code
Stopwatch("someName") {
// code being timed
doSomething()
somethingElse()
}
Sometimes you want to measure the performance of an operation which is not emcompassed by a single code section. You can then use the start
and stop
methods to delimit the scope of the operation.
// get a stopwatch instance
val stopwatch = Stopwatch.start("test")
// and later, elsewhere in your code
// use the same instance to signal completion
stopwatch.stop()
// if there was an error, use error() instead
stopwatch.error()
These start
, stop
and error
methods can also be used directly by JVM languages other than Scala (e.g. Java, Closure, Groovy) to provide more idiomatic wrappers for inlined code measurement.
StopwatchGroup
’s are useful to organize stopwatches, avoid name clashes, and specify behavior for multiple stopwatches at once.
The singleton Stopwatch
object that has been used so far in the examples is actually the default StopwatchGroup
. You can define other stopwatch groups, each having their own distribution range and enabled/disabled state.
By default, groups are disabled and have no defined range.
import stopwatch.Stopwatch
import stopwatch.StopwatchGroup
import stopwatch.TimeUnit._
val incomingRequests = new StopwatchGroup("Incoming Requests")
// configure
incomingRequests.enabled = true
incomingRequests.range = StopwatchRange(0 seconds, 30 seconds, 1000 millis)
// usage for code blocks
incomingRequests(clientId) {
doStuffForClient()
}
There are 3 ways to manage if and how stopwatches are enabled.
1) At the group level
As we have seen above, you can enable/disable stopwatches at the group level,
// the default group
Stopwatch.enabled = true
// or if you created your own group
myStopwatchGroup.enabled = false
By default, groups are disabled. You must explicitly enable them to monitor your application. Also, the group’s enabled
property overrides stopwatches’ enabled
property (see below).
2) “On demand”
If a group is enabled and a new stopwatch name is used, the enableOnDemand
property controls whether the new stopwatch is automatically enabled,
// the default group
Stopwatch.enableOnDemand = true
// or if you created your own group
myStopwatchGroup.enableOnDemand = false
// when enableOnDemand is false,
// you must explicitly enable stopwatches
myStopwatchGroup.enable("someCode")
Otherwise, non-preinitialized stopwatches are disabled by default. If enableOnDemand
is false
, you must explicitly enable stopwatches (see below).
3) Individual stopwatches
In addition to enabling/disabling at the group level, you can also enable and disable individual stopwatches explicitly,
// the default group
Stopwatch.enable("myCode")
// or...
Stopwatch.disable("myCode")
// if you created your own group
myStopwatchGroup.enable("myCode")
// or ...
myStopwatchGroup.disable("myCode")
You can define a time distribution range on stopwatch groups to plot execution time distribution graphs.
import stopwatch.Stopwatch
import stopwatch.StopwatchRange
import stopwatch.TimeUnit._
// somewhere in your code, before using the stopwatch
// distribution: 0s to 15s in 500ms intervals
Stopwatch.range = StopwatchRange(0 seconds, 15 seconds, 500 millis)
You may programmatically reset the values of stopwatches if your application has multiple cycles or if you want to recalibrate after the JVM (“HotSpot compiler”) has had time to optimize your code.
import stopwatch.Stopwatch
Stopwatch.reset("test")
The “low-tech” way to display statistics is to use one of the toString
methods: toShortString
, toMediumString
, toLongString
. This can be useful if you want to log statistics directly to the Console
or through an existing logging frameworks, such as Log4J.
scala> val stats = Stopwatch.snapshot("test")
// summary only
scala> s.toShortString
res3: String = Stopwatch "test" {hit=2, min=1838ms, avg=2148ms, max=2457ms, total=4296ms, stdDev=437ms}
// includes thread info
scala> s.toMediumString
res4: String = Stopwatch "test" {hits=2, throughput=0.814/s, minTime=1838ms, avgTime=2148ms, maxTime=2457ms, totalTime=4296ms, stdDev=437ms, currentThreads=0, avgThreads=1.50, maxThreads=2, first=2009-12-21 11:22:54.708 PST, last=2009-12-21 11:22:57.166 PST}
// includes threads + time distribution
scala> s.toLongString
res5: String = Stopwatch "test" {hits=2, throughput=0.814/s, minTime=1838ms, avgTime=2148ms, maxTime=2457ms, totalTime=4296ms, stdDev=437ms, currentThreads=0, avgThreads=1.50, maxThreads=2, first=2009-12-21 11:22:54.708 PST, last=2009-12-21 11:22:57.166 PST} Distribution {under=0, 0-1000ms: 0, 1000-2000ms: 1, 2000-3000ms: 1, 3000-4000ms: 0, 4000-5000ms: 0, 5000-6000ms: 0, 6000-7000ms: 0, 7000-8000ms: 0, 8000-9000ms: 0, 9000-10000ms: 0, over=0}
The standard toString
method is equivalent to toMediumString
.
You can also use the StopwatchStatistic interface to obtain only the information you need and display it differently.
The optional stopwatch-web.jar
library provides a small embedded web server to monitor your application in a development/certification setting. It does not yet provide any security beyond only opening “localhost” loopback-only port.
You can embed the server by inserting the following code fragment in your application’s startup code:
import stopwatch.web.Server
val server = new stopwatch.web.Server
// register StopwatchGroups you want to monitor
server.groups ::= Stopwatch
server.groups ::= myStopwatchGruop
// configure port number
server.port = 9999
server.start()
You can try a sample of the web server by running,
% scala -cp stopwatch-1.x.jar:stopwatch-web-1.x.jar stopwatch.web.SampleServer
If you point your browser to http://localhost:9999, you should see:
and by clicking on the distribution sparklines, you will see more details about the distribution range:
Stopwatch does not rely on any 3rd-party library dependencies.
Questions? Comments? Join, browse or search the mailing list
If you are a member of the mailing list, you can send messages directly to scala-stopwatch@googlegroups.com
Version 1.0-SNAPSHOT
Download jar files directly from the Maven2 repository.
The official Github repository is at http://github.com/aboisvert/stopwatch.
Refer to the README.md for build instructions.
Stopwatch is is licensed under the terms of the Apache Software License v2.0