栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Scala中隐藏的性能成本?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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    Unitso.s.ScalaBench.test     1   avgt        15        0.005        0.000    us/opo.s.ScalaBench.test     5   avgt        15        0.489        0.001    us/opo.s.ScalaBench.test    10   avgt        15       23.672        0.087    us/opo.s.ScalaBench.test    15   avgt        15     3406.492        9.239    us/opo.s.ScalaBench.test    20   avgt        15  2483221.694     5973.236    us/opBenchmark (t)   Mode   Samples         Mean   Mean error    Unitso.s.JavaBench.test     1   avgt        15        0.002        0.000    us/opo.s.JavaBench.test     5   avgt        15        0.254        0.007    us/opo.s.JavaBench.test    10   avgt        15       12.578        0.098    us/opo.s.JavaBench.test    15   avgt        15     1628.694       11.282    us/opo.s.JavaBench.test    20   avgt        15  1066113.157    11274.385    us/op

请注意,我们正在

t
努力查看效果对于的特定值是否是局部的
t
。并非如此,效果是系统的,而Java版本是两倍的速度。

PrintAssembly将对此有所启发。这是Scala基准测试中最热门的代码块:

0x00007fe759199d42: test   %r8d,%r8d0x00007fe759199d45: je     0x00007fe759199d76  ;*irem   ; - org.sample.ScalaBench::isEvenlyDivisible@11 (line 52)   ; - org.sample.ScalaBench::run@10 (line 45)0x00007fe759199d47: mov    %ecx,%eax0x00007fe759199d49: cmp    $0x80000000,%eax0x00007fe759199d4e: jne    0x00007fe759199d580x00007fe759199d50: xor    %edx,%edx0x00007fe759199d52: cmp    $0xffffffffffffffff,%r8d0x00007fe759199d56: je     0x00007fe759199d5c0x00007fe759199d58: cltd   0x00007fe759199d59: idiv   %r8d

…这与Java中的类似块相同:

0x00007f4a811848cf: movslq %ebp,%r100x00007f4a811848d2: mov    %ebp,%r9d0x00007f4a811848d5: sar    $0x1f,%r9d0x00007f4a811848d9: imul   $0x55555556,%r10,%r100x00007f4a811848e0: sar    $0x20,%r100x00007f4a811848e4: mov    %r10d,%r11d0x00007f4a811848e7: 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/



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/497401.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号