of 22 /22
MANUAL SPECIALIZATION Szymon Matejczyk @szymonmatejczyk

Manual specialization

Embed Size (px)

Text of Manual specialization

  • MANUAL SPECIALIZATIONSzymon [email protected]

  • ACT 1:WORLD RUINED

  • HIPSTER VS. OLDSCHOOL

    def hipsterMin(a: Array[Int]): Int = { a.foldLeft(Int.MaxValue){ _.min(_)}}

    def oldschoolMin(a: Array[Int]): Int = { var min = Int.MaxValue var i = 0 while (i < a.length) { min = if (a(i) < min) a(i) else min i += 1 } min}

  • HIPSTER VS. OLDSCHOOL

    def hipsterMin(a: Array[Int]): Int = { a.foldLeft(Int.MaxValue){ _.min(_)}}

    def oldschoolMin(a: Array[Int]): Int = { var min = Int.MaxValue var i = 0 while (i < a.length) { min = if (a(i) < min) a(i) else min i += 1 } min}

    Running time

    967722

    123424

  • ACT 1I:DENIAL

  • OLDSCHOOLpublic int oldschoolMin(int[]); Code: 0: ldc #72 // int 2147483647 2: istore_2 3: iconst_0 4: istore_3 5: iload_3 6: aload_1 7: arraylength 8: if_icmpge 33 11: aload_1 12: iload_3 13: iaload 14: iload_2 15: if_icmpge 24 18: aload_1 19: iload_3 20: iaload 21: goto 25 24: iload_2 25: istore_2 26: iload_3 27: iconst_1 28: iadd 29: istore_3 30: goto 5 33: iload_2 34: ireturn

  • HIPSTER public int hipsterMin(int[]); Code: 0: getstatic #67 // Field scala/Predef$.MODULE$:Lscala/Predef$; 3: aload_1 4: invokevirtual #71 // Method scala/Predef$.intArrayOps:([I)Lscala/collection/mutable/ArrayOps; 7: ldc #72 // int 2147483647 9: invokestatic #78 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; 12: new #80 // class szymon/Test$$anonfun$hipsterMin$1 15: dup 16: aload_0 17: invokespecial #83 // Method szymon/Test$$anonfun$hipsterMin$1."":(Lszymon/Test;)V 20: invokeinterface #89, 3 // InterfaceMethod scala/collection/mutable/ArrayOps.foldLeft:(Ljava/lang/Object;Lscala/Function2;)Ljava/lang/Object; 25: invokestatic #93 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 28: ireturn

  • HIPSTERpublic final class szymon.Test$$anonfun$hipsterMin$1 extends scala.runtime.AbstractFunction2$mcIII$sp implements scala.Serializable { public static final long serialVersionUID; public final int apply(int, int); Code: 0: aload_0 1: iload_1 2: iload_2 3: invokevirtual #21 // Method apply$mcIII$sp:(II)I 6: ireturn public int apply$mcIII$sp(int, int); Code: 0: getstatic #32 // Field scala/runtime/RichInt$.MODULE$:Lscala/runtime/RichInt$; 3: getstatic #37 // Field scala/Predef$.MODULE$:Lscala/Predef$; 6: iload_1 7: invokevirtual #41 // Method scala/Predef$.intWrapper:(I)I 10: iload_2 11: invokevirtual #44 // Method scala/runtime/RichInt$.min$extension:(II)I 14: ireturn public final java.lang.Object apply(java.lang.Object, java.lang.Object); Code: 0: aload_0 1: aload_1 2: invokestatic #51 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 5: aload_2 6: invokestatic #51 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 9: invokevirtual #53 // Method apply:(II)I 12: invokestatic #57 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; 15: areturn public szymon.Test$$anonfun$hipsterMin$1(szymon.Test); Code: 0: aload_0 1: invokespecial #65 // Method scala/runtime/AbstractFunction2$mcIII$sp."":()V 4: return

  • BOXING public int apply$mcIII$sp(int, int); Code: 0: getstatic #32 // Field scala/runtime/RichInt$.MODULE$:Lscala/runtime/RichInt$; 3: getstatic #37 // Field scala/Predef$.MODULE$:Lscala/Predef$; 6: iload_1 7: invokevirtual #41 // Method scala/Predef$.intWrapper:(I)I 10: iload_2 11: invokevirtual #44 // Method scala/runtime/RichInt$.min$extension:(II)I 14: ireturn public final java.lang.Object apply(java.lang.Object, java.lang.Object); Code: 0: aload_0 1: aload_1 2: invokestatic #51 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 5: aload_2 6: invokestatic #51 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 9: invokevirtual #53 // Method apply:(II)I 12: invokestatic #57 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; 15: areturn

  • BOXING: HOW TO AVOID

    dont use generics!

    use libraries for high performance code

    specialization?!

  • WHY SPECIALIZATION FAILS?

  • STEP BACK TO JAVA

    Trove

    fastutil

    HPPC

    import scala.collection.JavaConversions._

    def newInt2IntOpenHashMap(): mutable.Map[Int, Int] = new Int2IntOpenHashMap().asInstanceOf[jutil.Map[Int, Int]]

  • STEP BACK TO JAVA

    Trove

    fastutil

    HPPC

    import scala.collection.JavaConversions._

    def newInt2IntOpenHashMap(): mutable.Map[Int, Int] = new Int2IntOpenHashMap().asInstanceOf[jutil.Map[Int, Int]]

  • ACT 1II:SURVIVE IN BROKEN WORLD

  • SPECIALIZED QUEUEtrait CQueue[@specialized(Int, Long) T] { /** * @return Next element to be dequeued. */ def first(): T /** * * @return Next element that is removed from the `CQueue`. */ def deque(): T /** * Adds element to the `CQueue`. */ def +=(elem: T) def isEmpty: Boolean}

  • IMPLEMENTATION

    class CQueueAny[T, QueueType

  • FACTORYabstract class CQueueFactory[T] { def fifo(): FIFO[T] def priority(): CQueue[T] def priority(order: Order[T]): CQueue[T] } private[collections] class CQueueFactoryAny[T] extends CQueueFactory[T] { def fifo(): FIFO[T] = new CQueueAny[T, ObjectArrayFIFOQueue[T]]( new ObjectArrayFIFOQueue[T]()) with FIFO[T] { override def enqueueFirst(elem: T): Unit = underlying.enqueueFirst(elem) }} private[collections] class CQueueFactoryInt extends CQueueFactory[Int] { override def fifo() = new CQueueInt[IntArrayFIFOQueue]( new IntArrayFIFOQueue()) with FIFO[Int] { def enqueueFirst(elem: Int): Unit = underlying.enqueueFirst(elem) }}

  • GLUEobject CQueue { import Implicits._ def fifo[@specialized(Int, Long) T](): FIFO[T]= { implicitly[CQueueFactory[T]].fifo() } trait LowerPriorityImplicit { private val factoryAny = new CQueueFactoryAny[AnyRef]() implicit def factoryAny[T]: CQueueFactory[T] = factoryAny.asInstanceOf[CQueueFactory[T]] } trait LowPriorityImplicit extends LowerPriorityImplicit { implicit val factoryInt: CQueueFactory[Int] = new CQueueFactoryInt() } object Implicits extends LowPriorityImplicit}

  • USAGEclass CQueueSpec extends WordSpec with Matchers { def verifyQueue(q: CQueue[Int], dequeSeq: Seq[Int]): Unit = { q.isEmpty should be (true) q += 1 q += 2 q += 1 q += 3 q.isEmpty should be (false) dequeSeq.foreach { e => q.deque() should equal (e) } q.isEmpty should be (true) intercept[NoSuchElementException](q.deque()) } "CQueue" should { "enqueue/deque elements" when { "using fifo" in { val q = CQueue.fifo[Int]() verifyQueue(q, Seq(1, 2, 1, 3)) } } }}

  • ACT IV:MACROS!!

  • REKLAMY