2016-04-07 7 views
3

Ich versuche, die Kosten in der Leistung der Verwendung von typeclasses in Scala zu analysieren, weil ich festgestellt habe, dass wenn sie ausgiebig verwendet werden, die Leistung zu fallen neigen. Nehme sie zum Beispiel ein ByteCodec typeclass:Leistung von typeclasses in Scala

trait ByteCodec[T] { 
    def put(index: Int, byteBuffer: ByteBuffer, t: T): Unit 
    def get(index: Int, byteBuffer: ByteBuffer): T 
} 

Lassen Sie sich dann machen Sie ein Long Beispiel:

object ByteCodec { 
    def apply[T](implicit bc: ByteCodec[T]): ByteCodec[T] = bc 

    implicit val longBC = new ByteCodec[Long] { 
    @inline override def put(index: Int, byteBuffer: ByteBuffer, long: Long): Unit = { 
     val _ = byteBuffer.putLong(index, long) 
    } 

    @inline override def get(index: Int, byteBuffer: ByteBuffer): Long = 
     byteBuffer.getLong(index) 
    } 
} 

Wenn ich 100 Millionen laufen bekommt und Puts, das dauert ~ 1200ms für den typeclass Test und ~ Sonst 800ms. Wo ist der Overhead und kann ich ihn loswerden?

Der Code des Haupt:

object Main extends App { 
    val cycles = 100000000 
    val byteBuffer = ByteBuffer.allocate(java.lang.Long.BYTES) 
    var start = System.currentTimeMillis() 
    var currCycle = cycles 
    while (currCycle > 0) { 
    byteBuffer.putLong(0, 10L) 
    val k = byteBuffer.getLong(0) 
    currCycle -= 1 
    } 
    var end = System.currentTimeMillis() 
    println(s"time elapsed byteBuffer ${ end - start }") 

    val codec = ByteCodec[Long] 
    start = System.currentTimeMillis() 
    currCycle = cycles 
    while (currCycle > 0) { 
    codec.put(0, byteBuffer, 10L) 
    val k = codec.get(0, byteBuffer) 
    currCycle -= 1 
    } 
    end = System.currentTimeMillis() 
    println(s"time elapsed ByteCodec ${ end - start }") 
} 
+2

Nehmen jmh oder sbt-jmh, und analysieren. Hier ist ein Leitfaden für eine ähnliche Frage: http://shipilev.net/blog/2014/java-scala-divided-we-fail/. Der oben erwähnte naive Benchmark fällt mindestens der Eliminierung von toten Codes in "val k = ..." und Aufwärmproblemen bei der Ausführung von Tests mit Back-to-Back-Problemen zum Opfer. –

Antwort

7

Aleksey bereits in einem Kommentar ein Grund erwähnt, warum Ihr Test ungenau ist.

Abgesehen davon, der Hauptgrund, warum Ihre Typklasse langsamer ist, hat nichts mit der Vorgehensweise selbst zu tun: es ist die Boxen/Unboxing von Longs, die es langsamer macht. Sie können Ihre typeclass für Wertklassen mit der @specialized annotation spezialisieren:

trait ByteCodec[@specialized(Long) T] 

Wenn Sie einen Blick auf die Unterschriften von ByteBuffer, Werttypen verwendet werden und keine Boxen/Unboxing beteiligt: ​​

public abstract ByteBuffer putLong(int index, long value); 
public abstract long getLong(int index); 
+1

Natürlich war es @spezial! Ich habe nicht darüber nachgedacht, aber jetzt ist es klar. Ich schätze, das Problem ist dann, dass scalac das 'Long' in einem Fall optimiert und nicht in dem anderen. Jetzt habe ich ähnliche Leistung. – mariop

+0

Der Kommentar bedeutet, dass der gebrochene U-Benchmark keinen Overhead meldet, nicht, dass kein Overhead vorliegt. –

+1

Was ist der Unterschied zwischen den Benchmarks mit Spezialisierung? – pedrofurla