Manual specialization

22
MANUAL SPECIALIZATION Szymon Matejczyk @szymonmatejczyk

Transcript of Manual specialization

Page 1: Manual specialization

MANUAL SPECIALIZATIONSzymon Matejczyk@szymonmatejczyk

Page 2: Manual specialization

ACT 1:WORLD RUINED

Page 3: Manual specialization

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}

Page 4: Manual specialization

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

Page 5: Manual specialization
Page 6: Manual specialization

ACT 1I:DENIAL

Page 7: Manual specialization

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

Page 8: Manual specialization

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."<init>":(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

Page 9: Manual specialization

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."<init>":()V 4: return

Page 10: Manual specialization

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

Page 11: Manual specialization

BOXING: HOW TO AVOID

• don’t use generics!

• use libraries for high performance code

• specialization?!

Page 12: Manual specialization

WHY SPECIALIZATION FAILS?

Page 13: Manual specialization

STEP BACK TO JAVA

• Trove

• fastutil

• HPPC

import scala.collection.JavaConversions._

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

Page 14: Manual specialization

STEP BACK TO JAVA

• Trove

• fastutil

• HPPC

import scala.collection.JavaConversions._

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

Page 15: Manual specialization

ACT 1II:SURVIVE IN BROKEN WORLD

Page 16: Manual specialization

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}

Page 17: Manual specialization

IMPLEMENTATION

class CQueueAny[T, QueueType <: PriorityQueue[T]](protected val underlying: QueueType) extends CQueue[T] { def +=(elem: T): Unit = underlying.enqueue(elem) def deque(): T = underlying.dequeue() override def first(): T = underlying.first() override def isEmpty: Boolean = underlying.isEmpty} class CQueueInt[QueueType <: IntPriorityQueue](protected val underlying: QueueType) extends CQueue[Int] { override def +=(elem: Int): Unit = underlying.enqueue(elem) override def deque(): Int = underlying.dequeueInt() override def first(): Int = underlying.firstInt() override def isEmpty: Boolean = underlying.isEmpty}

Page 18: Manual specialization

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

Page 19: Manual specialization

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}

Page 20: Manual specialization

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

Page 21: Manual specialization

ACT IV:MACROS!!

Page 22: Manual specialization

REKLAMY