简体   繁体   中英

Scala TypeTags and performance

There are some answers around for equivalent questions about Java, but is scala reflection (2.11, TypeTags) really slow? there's a long narrative write-up about it at http://docs.scala-lang.org/overviews/reflection/overview.html , where the answer to this question is hard to extract.

I see a lot of advice floating around about avoiding reflection, maybe some of it predating the improvements of 2.11, but if this works well it looks like it can solve the debilitating aspect of the JVM's type erasure, for scala code.

Thanks!

Let's measure it. I've created simple class C that has one method. All what this method do is sleep for 10ms. Let's invoke this method

  • within reflection

  • directly

And see which is faster and how fast it is.

I've created three tests.

Test 1. Invoke via reflection. Execution time include all work that necessary to be done for setup reflection. Create runtimeMirror , reflect class, create declaration for method, and at last step - execute method.

Test 2. Do not take into account this preparation stage, as it can be re-used. We are calculate time of method invoking via reflection only.

Test 3. Invoke method directly.

Results:

Reflection from start : job done in 2561ms got 101 (1,5seconds for setup each execution)

Invoke method reflection: job done in 1093ms got 101 ( < 1ms for setup each execution)

No reflection: job done in 1087ms got 101 ( < 1ms for setup each execution)

Conclusion: Setup phase increase execution time dramatically. But there are no need to perform setup on each execution (this is like class initialization - can be done once). So if you use reflection in right way(with separated init stage) it shows relevant performance and can be used for production.

Source code:

    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")
        }
      }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM