2013-04-30 5 views
5

Ich bin auf der Suche nach guten Praktiken zu vermeiden, den gleichen Code immer und immer wieder zu schreiben, um Unboxedness zu erreichen. Sagen, ich habe so etwas wie dies:Boilerplate-freie Scala ArrayBuilder Spezialisierung

def speedyArrayMaker[@specialized(Long) A: ClassTag](...): Array[A] = { 
    val builder = Array.newBuilder[A] 
    // do stuff with builder 
    builder.result 
} 

Diese in unboxed Lagerung führen zugrunde liegenden mein builder, wenn möglich, aber wie ich es verstehe, keine unboxed Methode ruft es, weil ich durch den unspezialisiert ArrayBuilder Zug werde.

In einer monomorphic Welt Long spezialisiert, würde ich val builder = new ArrayBuilder.ofLong() schreiben und Boxen vermeiden überhaupt, aber kurz zu sagen, ArrayBuilder/Builder auf allen primitiven Typen spezialisiert werden, kann ich nicht von einer netten Art und Weise denken zu vermeiden Duplizieren Anstrengung hier. Ein Ansatz, den ich gedacht habe von auch sein mag, in speedyArrayMaker:

val (add, builder): (A => Unit, ArrayBuilder[A]) = implicitly[ClassTag[A]].runtimeClass match { 
    case java.lang.Long.TYPE => 
    val builder = new ArrayBuilder.ofLong() 
    ((x: Long) => builder += x, builder).asInstanceOf 
    case _ => 
    val builder = Array.newBuilder[A] 
    ((x: A) => builder += x, builder) 
} 

Da es nur die += Methode, die wir wirklich interessieren spezialisiert zu bekommen, und dann bekommen wir ein Function1 für add, die auf Long spezialisiert. Überprüfung mit javap, ja ich

90: invokestatic #118; //Method scala/runtime/BoxesRunTime.boxToLong:(J)Ljava/lang/Long; 
93: invokeinterface #127, 2; //InterfaceMethod scala/collection/mutable/Builder.$plus$eq:(Ljava/lang/Object;)Lscala/collection/mutable/Builder; 

für die Array.newBuilder[A] Version (auch im Fachausgang) und:

252: invokeinterface #204, 3; //InterfaceMethod scala/Function1.apply$mcVJ$sp:(J)V 

für die gewundene Version. Ich kann dieses Muster in eine "spezialisierte Builder-Helfer" -Funktion einrahmen, aber es fühlt sich hässlich an, besonders wenn es zur Laufzeit noch auf der Basis von etwas kompiliert, das während der Spezialisierung bekannt ist. Letztendlich würde ich meinen Vorschlag hier auf die Tatsache piggyback, dass Function1 bereits spezialisiert ist, und ich mag es nicht besonders.

Gibt es clevere Tricks, die ich verwenden kann, um dies angenehmer zu machen? Mir ist klar, dass dies ein sehr tiefgründiges Detail ist und selten leistungskritisch sein wird, aber angesichts der Menge an Aufwand/Code-Duplizierung, die in alle Spezialklassen ging, scheint es schade zu sein, einige ihrer Vorteile im Gegenzug zu verschenken polymorph sein.

bearbeiten ich von etwas hässlich dachte aber, dass ich hatte gehofft funktionieren würde:

def builderOf(x: Array[Int]): ArrayBuilder.ofInt = new ArrayBuilder.ofInt() 
def builderOf(x: Array[Long]): ArrayBuilder.ofLong = new ArrayBuilder.ofLong() 
def builderOf[A: ClassTag](x: Array[A]): ArrayBuilder[A] = ArrayBuilder.make[A] 

und dann in meinem Fachfunktion:

val witness: Array[A] = null 
val builder = builderOf(witness) 

aber es scheint die allgemeine builderOf zu nennen sogar in der spezialisierten Version (auch wenn genügend Typinformationen verfügbar sind, um die Array[Long]-Version aufzurufen). Wer weiß, warum das nicht funktioniert? Der Ansatz scheint ziemlich sauber zu sein, verglichen mit dem anderen, den ich vorschlug. Ich glaube, ich hatte auf einen "makro-like" Ansatz zur Spezialisierung gehofft, aber ich denke, es gibt keine Garantie, dass es für alle Instantiierungen korrekt ist, es sei denn, es wird für jede Spezialisierung die gleiche Methode gewählt :(

+0

ich bin nicht sicher, zu sehen, wie alles, was Sie tun können, werden Sie jede Spezialisierung erhalten, da 'ArrayBuidler' nicht spezialisiert (und damit' + = 'wird niemals spezialisiert sein, auch wenn es von einer spezialisierten Methode aufgerufen wird.Sie erhalten nur dann eine Spezialisierung, wenn Sie 'ArrayBuidler' vollständig umgehen (zB indem Sie Ihre eigene spezialisierte Version definieren). –

+0

Tatsächlich ist mir aufgefallen, dass die Spezialisierung "nur" der äußeren Methode (die einen '+ =' nennt) uns bereits eine erhebliche Beschleunigung bringen könnte, indem wir dem Jitter erlauben, monophisches Cache-Inlining durchzuführen. Haben Sie daran gedacht? –

+0

Mein Punkt (das ist mein anderer Account) ist, dass es eine Reihe spezialisierter 'ArrayBuilder'-Unterklassen mit den Namen' ofInt', 'ofDouble' usw. gibt. Sie werden verwendet, wenn Sie nach' Array.newBuilder [irgendjemand]] fragen kann sie auch direkt instanziieren. Wenn Sie 'newBuilder' verwenden, erhalten Sie einen' ArrayBuilder', der nicht spezialisiert ist, aber wenn Sie einen 'neuen ArrayBuilder.ofInt()' instanziieren, erhalten Sie auch ungesetzte Aufrufe an '+ =', und genau das habe ich versucht oben erfassen. Sie können dies testen, indem Sie das 'new ofInt()' mit den spezifischeren und weniger spezifischen Typen kommentieren und sehen, ob Sie einen Box-Aufruf erhalten. – copumpkin

Antwort

4

könnten versuchen, etwas entlang der Linien von (die barbarischen Namen entschuldigen),

import scala.collection.mutable.ArrayBuilder 
import scala.reflect.ClassTag 

trait SpecializedArrayBuilder[@specialized(Long) A] { 
    def +=(a: A) 
    def result: Array[A] 
} 

trait LowPrioritySpecializedArrayBuilder { 
    implicit def defaultBuilder[A: ClassTag] = new SpecializedArrayBuilder[A] { 
    val builder = ArrayBuilder.make[A] 
    def +=(a: A) = builder += a 
    def result = builder.result 
    } 
} 

object SpecializedArrayBuilder extends LowPrioritySpecializedArrayBuilder { 
    implicit val longBuilder = new SpecializedArrayBuilder[Long] { 
    val builder = new ArrayBuilder.ofLong 
    def +=(a: Long) = builder += a 
    def result = builder.result 
    } 
} 

object Specialized { 
    def speedyArrayMaker[@specialized(Long) A](a: A) 
    (implicit builder: SpecializedArrayBuilder[A]): Array[A] = { 
    builder += a 
    builder.result 
    } 

    def main(arg: Array[String]) { 
    val arr = speedyArrayMaker(1L) 
    println(arr) 
    } 
} 
+0

Danke dafür! Ich hatte noch keine Gelegenheit, es zu testen, aber ich denke, implicits werden nach dem spezialisierten Typ gelöst, im Gegensatz zu überladenen Methoden, wie ich in der ursprünglichen Frage versucht habe. Ich denke, dass der spezielle Code, den Sie geschrieben haben, mir eine Builder-Instanz für den gesamten Typ geben wird, aber es ist nicht schwer zu sehen, wie man ihn ändert, um Builder bei Bedarf zu erstellen. –

+0

In gewissem Sinne repliziert dies nur eine Menge der Duplizierung, die bereits in der Standardbibliothek vorhanden ist. Es scheint so, als ob wir, wenn wir vorsichtig beginnen möchten, Spezialisierungsannotationen zu mehr Teilen der Sammlungsbibliothek hinzuzufügen, "ArrayBuilder" und "Builder" wären gute Ausgangspunkte. –

+0

@MyseriousDan Ja, ich stimme zu. –