如何在 Scala 中分析方法?
分析 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
您想在不更改要测量时序的代码的情况下执行此操作吗?如果您不介意更改代码,那么您可以执行以下操作:
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:
除了 Jesper 的答案之外,您还可以在 REPL 中自动包装方法调用:
现在 - 让我们将任何内容包装在这个
OK - 我们需要处于电源模式
包装
我不知道为什么
从 2.12 开始打印的内容会更新 5 次。 2:
In addition to Jesper's answer, you can automatically wrap method invocations in the REPL:
Now - let's wrap anything in this
OK - we need to be in power mode
Wrap away
I have no idea why that printed stuff out 5 times
Update as of 2.12.2:
这是我用的:
This what I use:
有 前 Scala wiki 的“工具和库”页面中提到的三个 Scala 基准测试库:
SPerformance - 性能测试框架,旨在自动比较性能测试并在 Simple Build Tool 中工作。
scala-benchmarking-template - 用于创建 Scala 的 SBT 模板项目(微型)基于 Caliper 的基准测试。
指标 - 捕获 JVM 和应用程序级别的指标。所以你知道发生了什么
截至 2024 年,这些库中唯一仍在维护的库是 Metrics 。
There were three benchmarking libraries for Scala mentioned on the "Tools and Libraries" page of the former Scala wiki:
SPerformance - Performance Testing framework aimed at automagically comparing performance tests and working inside Simple Build Tool.
scala-benchmarking-template - SBT template project for creating Scala (micro-)benchmarks based on Caliper.
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.
推荐的 Scala 代码基准测试方法是通过 sbt-jmh
许多主要的 Scala 项目都采用这种方法,例如
基于
的简单包装计时器System.nanoTime
是 不是可靠的基准测试方法:此外,诸如JIT预热、垃圾收集、系统范围事件等注意 等可能会引入不可预测性到测量中:
基于特拉维斯·布朗的回答,这是一个如何为 Scala 设置 JMH 基准的示例
project/plugins.sbt
build.sbt
中启用jmh插件添加到
src/main/scala/bench/VectorAppendVsListPrependAndReverse.scala
结果
似乎表明先添加到
List
然后在最后反转它比继续附加到Vector
快一个数量级>。The recommended approach to benchmarking Scala code is via sbt-jmh
This approach is taken by many of the major Scala projects, for example,
Simple wrapper timer based on
System.nanoTime
is not a reliable method of benchmarking:Furthermore, considerations such as JIT warmup, garbage collection, system-wide events, etc. might introduce unpredictability into measurements:
Based on Travis Brown's answer here is an example of how to setup JMH benchmark for Scala
project/plugins.sbt
build.sbt
Add to
src/main/scala/bench/VectorAppendVsListPreppendAndReverse.scala
The results are
which seems to indicate prepending to a
List
and then reversing it at the end is order of magnitude faster than keep appending to aVector
.testing.Benchmark
可能会有用。testing.Benchmark
might be useful.我使用了一种易于在代码块中移动的技术。关键在于完全相同的行开始和结束计时器 - 所以这实际上是一个简单的复制和粘贴。另一个好处是,您可以将时间对您的意义定义为字符串,所有这些都在同一行中。
示例用法:
代码:
优点:
缺点:
例如,如果您的代码没有到达给定计时器启动的第二次调用。
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:
The code:
Pros:
Cons:
e.g. if your code doesn't get to the second invocation for a given timer start.
ScalaMeter 是一个很好的库,可以在 Scala 中执行基准测试
下面是一个简单的示例
如果您在 Scala 工作表中执行上述代码片段你会得到以毫秒为单位的运行时间
ScalaMeter is a nice library to perform benchmarking in Scala
Below is a simple example
If you execute above code snippet in Scala Worksheet you get the running time in milliseconds
我从 Jesper 获取了解决方案,并在多次运行相同代码时添加了一些聚合
假设您想要对两个函数
counter_new
和counter_old
进行计时,以下是用法:希望这有帮助
I took the solution from Jesper and added some aggregation to it on multiple run of the same code
Suppose you want to time two functions
counter_new
andcounter_old
, the following is the usage:Hopefully this is helpful
我喜欢@wrick答案的简单性,但也想要:
探查器处理循环(为了一致性和便利性)
更准确的计时(使用nanoTime)
每次迭代的时间(不是所有迭代的总时间)
只返回 ns/iteration - 不是一个tuple
这是在这里实现的:
为了获得更高的准确性,一个简单的修改允许 JVM 热点预热循环(不定时)来对小片段进行计时:
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:
For even more accuracy, a simple modification allows a JVM Hotspot warmup loop (not timed) for timing small snippets:
您可以使用
System.currentTimeMillis
:用法:
nanoTime 会显示
ns
,因此很难看到。所以我建议你可以使用currentTimeMillis来代替它。You can use
System.currentTimeMillis
:Usage:
nanoTime will show you
ns
, so it will hard to see. So I suggest that you can use currentTimeMillis instead of it.站在巨人的肩膀上...
一个可靠的第 3 方库会更理想,但如果您需要快速且基于标准库的东西,可以使用以下变体:
。
另外值得注意的是,您可以使用
Duration.toCoarsest
方法转换为可能的最大时间单位,尽管我不确定这对于运行之间的微小时间差异有多友好,例如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:
.
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.添加=>方法名称和;秒
adding on => method with name & seconds