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 :(
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). –
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? –
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