我遇到了这个老问题,并使用scala 2.10.3进行了以下实验。
我重写了Scala版本以使用显式尾递归:
import scala.annotation.tailrec
object ScalaMain {
private val t = 20
private def run() {
var i = 10
while(!isEvenlyDivisible(2, i, t))
i += 2
println(i)
}
@tailrec private def isEvenlyDivisible(i: Int, a: Int, b: Int): Boolean = {
if (i > b) true
else (a % i == 0) && isEvenlyDivisible(i+1, a, b)
}
def main(args: Array[String]) {
val t1 = System.currentTimeMillis()
var i = 0
while (i < 20) {
run()
i += 1
}
val t2 = System.currentTimeMillis()
println("time: " + (t2 - t1))
}
}
并将其与以下Java版本进行了比较。我有意识地使函数成为非静态的,以便与Scala进行公平的比较:
public class JavaMain {
private final int t = 20;
private void run() {
int i = 10;
while (!isEvenlyDivisible(2, i, t))
i += 2;
System.out.println(i);
}
private boolean isEvenlyDivisible(int i, int a, int b) {
if (i > b) return true;
else return (a % i == 0) && isEvenlyDivisible(i+1, a, b);
}
public static void main(String[] args) {
JavaMain o = new JavaMain();
long t1 = System.currentTimeMillis();
for (int i = 0; i < 20; ++i)
o.run();
long t2 = System.currentTimeMillis();
System.out.println("time: " + (t2 - t1));
}
}
这是我计算机上的结果:
> java JavaMain
....
time: 9651
> scala ScalaMain
....
time: 20592
这是(Java HotSpot(TM)64位服务器VM,Java 1.7.0_51)上的scala 2.10.3。
我的问题是,scala版本的隐藏成本是多少?
非常感谢。
好吧,OP的基准测试不是理想的基准测试。需要减轻许多影响,包括预热,消除死代码,分叉等。幸运的是,JMH已经处理了很多事情,并且具有Java和Scala的绑定。请按照JMH页面上的过程获取基准测试项目,然后可以将下面的基准测试移植。
这是样本Java基准测试:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
@Fork(3)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
public class JavaBench {
@Param({"1", "5", "10", "15", "20"})
int t;
private int run() {
int i = 10;
while(!isEvenlyDivisible(2, i, t))
i += 2;
return i;
}
private boolean isEvenlyDivisible(int i, int a, int b) {
if (i > b)
return true;
else
return (a % i == 0) && isEvenlyDivisible(i + 1, a, b);
}
@GenerateMicroBenchmark
public int test() {
return run();
}
}
…这是示例Scala基准测试:
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
@Fork(3)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
class ScalaBench {
@Param(Array("1", "5", "10", "15", "20"))
var t: Int = _
private def run(): Int = {
var i = 10
while(!isEvenlyDivisible(2, i, t))
i += 2
i
}
@tailrec private def isEvenlyDivisible(i: Int, a: Int, b: Int): Boolean = {
if (i > b) true
else (a % i == 0) && isEvenlyDivisible(i + 1, a, b)
}
@GenerateMicroBenchmark
def test(): Int = {
run()
}
}
如果您在JDK 8 GA,Linux x86_64上运行它们,则将获得:
Benchmark (t) Mode Samples Mean Mean error Units
o.s.ScalaBench.test 1 avgt 15 0.005 0.000 us/op
o.s.ScalaBench.test 5 avgt 15 0.489 0.001 us/op
o.s.ScalaBench.test 10 avgt 15 23.672 0.087 us/op
o.s.ScalaBench.test 15 avgt 15 3406.492 9.239 us/op
o.s.ScalaBench.test 20 avgt 15 2483221.694 5973.236 us/op
Benchmark (t) Mode Samples Mean Mean error Units
o.s.JavaBench.test 1 avgt 15 0.002 0.000 us/op
o.s.JavaBench.test 5 avgt 15 0.254 0.007 us/op
o.s.JavaBench.test 10 avgt 15 12.578 0.098 us/op
o.s.JavaBench.test 15 avgt 15 1628.694 11.282 us/op
o.s.JavaBench.test 20 avgt 15 1066113.157 11274.385 us/op
请注意,我们正在t
努力查看效果对于的特定值是否是局部的t
。并非如此,效果是系统的,而Java版本是两倍的速度。
PrintAssembly将对此有所启发。这是Scala基准测试中最热门的代码块:
0x00007fe759199d42: test %r8d,%r8d
0x00007fe759199d45: je 0x00007fe759199d76 ;*irem
; - org.sample.ScalaBench::isEvenlyDivisible@11 (line 52)
; - org.sample.ScalaBench::run@10 (line 45)
0x00007fe759199d47: mov %ecx,%eax
0x00007fe759199d49: cmp $0x80000000,%eax
0x00007fe759199d4e: jne 0x00007fe759199d58
0x00007fe759199d50: xor %edx,%edx
0x00007fe759199d52: cmp $0xffffffffffffffff,%r8d
0x00007fe759199d56: je 0x00007fe759199d5c
0x00007fe759199d58: cltd
0x00007fe759199d59: idiv %r8d
…这与Java中的类似块相同:
0x00007f4a811848cf: movslq %ebp,%r10
0x00007f4a811848d2: mov %ebp,%r9d
0x00007f4a811848d5: sar $0x1f,%r9d
0x00007f4a811848d9: imul $0x55555556,%r10,%r10
0x00007f4a811848e0: sar $0x20,%r10
0x00007f4a811848e4: mov %r10d,%r11d
0x00007f4a811848e7: sub %r9d,%r11d ;*irem
; - org.sample.JavaBench::isEvenlyDivisible@9 (line 63)
; - org.sample.JavaBench::isEvenlyDivisible@19 (line 63)
; - org.sample.JavaBench::run@10 (line 54)
请注意,在Java版本中,编译器如何使用技巧将整数余数计算转换为乘法和右移(请参阅Hacker’s
Delight,第10章,第19节)。当编译器检测到我们针对常数计算余数时,这是可能的,这表明Java版本达到了最佳效果,而Scala版本则没有。您可以深入研究字节码反汇编以弄清scalac中有哪些怪癖,但是此练习的目的是,基准测试大大放大了代码生成中令人惊讶的微小差异。
PS这么多@tailrec
…
更新:对效果的更彻底的解释:http : //shipilev.net/blog/2014/java-scala-divided-we-
fail/
问题内容: 已锁定 。该问题及其答案被锁定,因为该问题是题外话,但具有历史意义。它目前不接受新的答案或互动。 我从事Microsoft SQL Server 已有多年,但直到最近才开始在我的Web应用程序中使用MySQL,而且我渴望获得知识。 为了继续回答“隐藏功能”问题,我想知道MySQL的任何隐藏或方便功能,这些功能有望改善我对这个开源数据库的了解。 问题答案: 既然您悬赏,我将分享我的来之不
问题内容: Python的隐藏功能 问题答案: 链接比较运算符: 如果你以为它在做,它显示为,然后比较,它也是,那么不,那实际上不是什么事情(请参阅最后一个示例。)它实际上是翻译成但键入和每个输入较少该术语仅评估一次。
问题内容: 看完C#的隐藏功能后,我想知道Java的一些隐藏功能是什么? 问题答案: 几个月前,Double Brace Initialization让我惊讶,以前从未听说过它。 ThreadLocals通常并不广为人知,它是一种存储每个线程状态的方法。 由于JDK 1.5 Java除了锁以外,还具有非常好的实现和健壮的并发工具,因此它们位于java.util.concurrent中,并且一个特别
此处将公开Dism++未在UI中呈现的设置,你可以修改Config\Config.ini,让这些设置生效。 启用方式 功能 [Dism++] NotLoadWofadk=1 添加后Dism++将不会在启动时加载Wof驱动,默认情况下Dism++会在Win8以上系统自动加载Wof驱动。如果你的环境比较特殊,可以开启此选项。开启后,WIMBoot以及Compact相关功能可能会受限。 [WUA] Di
问题内容: 使用Angular Material时,我一直看到aria属性。有人可以向我解释,aria前缀是什么意思?但最重要的是,我想了解的是和属性之间的区别。 问题答案: ARIA(可访问的富Internet应用程序)定义了一种使残障人士更容易访问Web内容和Web应用程序的方法。 该属性是HTML5中的新增属性,它告诉 浏览器 不要显示该元素。该属性告诉 屏幕阅读器 是否应该忽略该元素。请查
我正在使用DTO对象从spring rest控制器中的@RequestBody中检索信息,并在json响应中使用相同的DTO对象。我想完全隐藏一些字段不让响应。 我尝试了,它为未映射的属性返回null,但我的问题是: null FieldBonlyOrderToOrderToMapper 将返回一个OrderDto对象,该对象没有名为(otherFiledA)的字段