【问题标题】:Scala TypeTags and performanceScala TypeTags 和性能
【发布时间】:2015-12-11 23:17:14
【问题描述】:

有一些关于 Java 的等效问题的答案,但是 scala 反射(2.11,TypeTags)真的很慢吗?在http://docs.scala-lang.org/overviews/reflection/overview.html 上有一篇关于它的长篇叙述性文章,很难提取这个问题的答案。

我看到很多关于避免反射的建议,其中一些建议早于 2.11 的改进,但如果效果良好,它看起来可以解决 JVM 类型擦除的弱点,对于 scala 代码。

谢谢!

【问题讨论】:

  • 通过反射调用方法(在 Java 或 Scala 中)比直接调用方法要慢得多,因为在运行时需要做更多的工作 - 查找方法、解析方法等,如果不是必要,不要使用反射。
  • 您是否正在解决查找对象方法然后调用一个对象的情况?它与类型标签或类型恢复有何关系?
  • 您能否提供一些 TypeTags 使用示例,您关心性能的地方?有些用法很快,有些很慢,讨论每种可能用法的答案似乎很难预料(会太长)。

标签: scala


【解决方案1】:

让我们测量一下。 我创建了具有一种方法的简单class C。这个方法所做的只是休眠 10 毫秒。 让我们调用这个方法

  • 在反思中

  • 直接

然后看看哪个更快,它有多快。

我创建了三个测试。

测试 1。 通过反射调用。执行时间包括设置反射所需完成的所有工作。 创建runtimeMirror,反射类,创建方法声明,最后一步——执行方法。

测试 2。 不要考虑这个准备阶段,因为它可以重复使用。 我们仅通过反射计算方法调用的时间。

测试3。直接调用方法。

结果:

从一开始的反思:在 2561 毫秒内完成的工作得到了 101(每次执行设置需要 1.5 秒)

调用方法反射:在 1093 毫秒内完成的工作得到 101(

没有反射:在 1087 毫秒内完成的工作得到了 101(

结论: 设置阶段显着增加了执行时间。但是无需在每次执行时执行设置(这就像类初始化 - 可以完成一次)。因此,如果您以正确的方式使用反射(具有独立的 init 阶段),它会显示相关的性能并可以用于生产。

源代码:

    class C {
      def x = {
        Thread.sleep(10)
        1
      }
    }


    class XYZTest extends FunSpec {
      def withTime[T](procName: String, f: => T): T = {
        val start = System.currentTimeMillis()
        val r = f
        val end = System.currentTimeMillis()
        print(s"$procName job done in ${end-start}ms")
        r
      }

      describe("SomeTest") {
        it("rebuild each time") {
          val s = withTime("Reflection from start : ", (0 to 100). map {x =>
            val ru = scala.reflect.runtime.universe
            val m = ru.runtimeMirror(getClass.getClassLoader)
            val im = m.reflect(new C)
            val methodX = ru.typeOf[C].declaration(ru.TermName("x")).asMethod
            val mm = im.reflectMethod(methodX)
            mm().asInstanceOf[Int]
          }).sum
          println(s" got $s")
        }
        it("invoke each time") {
          val ru = scala.reflect.runtime.universe
          val m = ru.runtimeMirror(getClass.getClassLoader)
          val im = m.reflect(new C)
          val s = withTime("Invoke method reflection: ", (0 to 100). map {x =>
            val methodX = ru.typeOf[C].declaration(ru.TermName("x")).asMethod
            val mm = im.reflectMethod(methodX)
            mm().asInstanceOf[Int]
          }).sum
          println(s" got $s")
        }
        it("invoke directly") {
          val c = new C()
          val s = withTime("No reflection: ", (0 to 100). map {x =>
            c.x
          }).sum
          println(s" got $s")
        }
      }
    }

【讨论】:

  • 这个答案总体上很有帮助。与 TypeTags 有任何联系吗?
  • typeOf[C] 使用TypeTags。通常,这在很大程度上取决于您实际需要对它们做什么。一些操作会很慢。
  • @Mr.V.我建议阅读有关如何对 JVM 操作进行基准测试的信息。特别是,查看 JMH 或 ScalaMeter。
  • 我同意,我的基准不是很精确。此外,没有预热阶段,因此第一种方法需要更多原因。但总的来说,我决定不修复它,因为它在概念上不会改变结果:反射可以像直接调用一样快。
猜你喜欢
  • 1970-01-01
  • 2012-05-17
  • 2014-06-28
  • 2011-06-21
  • 2018-01-06
  • 2013-06-04
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多