加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

使用Scala的REPL进行比较性能基准测试是否合理?

发布时间:2020-12-16 18:12:24 所属栏目:安全 来源:网络整理
导读:Scala的REPL是交互式测试某些代码片段的绝佳场所.最近,我一直在使用REPL进行一些性能比较,以重复执行操作并相对地测量挂钟时间. 这是我最近创建的一个例子,用于帮助回答SO问题[1] [2]: // Figure out the perfomance difference between direct method invo
Scala的REPL是交互式测试某些代码片段的绝佳场所.最近,我一直在使用REPL进行一些性能比较,以重复执行操作并相对地测量挂钟时间.

这是我最近创建的一个例子,用于帮助回答SO问题[1] [2]:

// Figure out the perfomance difference between direct method invocation and reflection-based method.invoke

def invoke1[T,U](obj:Any,method:Method)(param:T):U = method.invoke(obj,Seq(param.asInstanceOf[java.lang.Object]):_*) match { 
    case x: java.lang.Object if x==null => null.asInstanceOf[U]
    case x => x.asInstanceOf[U]
}

def time[T](b: => T):(T,Long) = {
    val t0 = System.nanoTime()
    val res = b
    val t = System.nanoTime() - t0
    (res,t )
}

class Test {
  def op(l:Long): Long = (2 until math.sqrt(l).toInt).filter(x=>l%x==0).sum
}

val t0 = new Test

val method = classOf[Test].getMethods.find(_.getName=="op").get

def timeDiff = {
  val (timeDirectCall,res) = time { (0 to 1000000).map(x=>t0.op(x)) }
  val (timeInvoke,res2) = time { (0 to 1000000).map(x=>{val res:Long=invoke1(t0,method)(x);res}) }
  (timeInvoke-timeDirectCall).toDouble/timeDirectCall.toDouble
}


//scala> timeDiff
//res60: Double = 2.1428745665357445
//scala> timeDiff
//res61: Double = 2.1604176409796683

在另一个案例中,我一直在生成随机数据点的MM,以比较开源项目的并发模型. REPL非常适合在没有代码编译测试周期的情况下使用不同的配置.

我知道常见的基准测试陷阱,例如JIT优化和热身需求.

我的问题是:

>使用时是否需要考虑REPL特定元素
它执行宏观基准比较微观?
>这些测量相对于彼此使用时是否可靠?即他们能回答这个问题:A比B快吗?
>对于相同代码的预先执行的执行是jit的良好预热
编译器?
>还有其他问题需要注意吗?

[1] Scala reflection: How to pass an object’s method as parameter to another method

[2] https://gist.github.com/maasg/6808879

解决方法

这是一个很好的问题.我无法想象为什么有人贬低它.

其中一条评论完全错误的事实表明REPL需要在scala-lang.org的常见问题或教程中占有一席之地.快速搜索后我找不到描述性文件.

答案是肯定的,REPL做到了你所期望的.

Here is an old page关于为什么这个问题很有趣:REPL感觉很动态,但实际上是静态编译的.它“横跨两个世界”,因为关于链接页面的临时评论说明了这一点.

REPL将每一行编译成自己的包装对象.每个这样的对象都从交互式会话的历史中导入符号,这是代码神奇地引用回前一行的方式.所有东西都被编译,因此当它运行时,它可以在JVM上本地运行,可以这么说;没有额外的解释层.这是REPL的杀手级设计功能.

这就是为什么你的问题的答案是肯定的,你的代码以编译代码的速度运行.调用方法不需要重新编译所有历史记录.

Here’s another old link显示其他人对时间和微基准测试有同样的疑问.

目前有an open issue可以自定义REPL如何包装代码行.微博标记是一个有趣的用例,其中代码可以包含在任意框架中以进行基准测试.那将很快到来.

基准框架应该进行热身.由于提交给REPL的每个表达式都是单独编译的(虽然是由相同的编译器),你会注意到第一次可以调用一个方法,而第二次调用一个方法(通过scalac模数内联).

警告:

使用-Yrepl-class-based或注意不要将计算放在包装对象的静态初始化器中.

Here is some sample confusion和here is the same question,隐瞒较少.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读