Sto cercando buone pratiche per evitare di riscrivere lo stesso codice più e più volte per ottenere unboxedness. Dire che ho qualcosa di simile:Scala ArraySpecializzazione di Builder
def speedyArrayMaker[@specialized(Long) A: ClassTag](...): Array[A] = {
val builder = Array.newBuilder[A]
// do stuff with builder
builder.result
}
Questo si tradurrà in deposito unboxed sottostante mia builder
quando possibile, ma se ho capito bene, nessun metodo unboxing chiama ad esso perché sto attraversando il non specializzato ArrayBuilder
trait.
In un mondo monomorfa specializzato per Long
, mi piacerebbe scrivere val builder = new ArrayBuilder.ofLong()
ed evitare la boxe a tutti, ma a corto di raccontare ArrayBuilder
/Builder
essere specializzate su tutti i tipi primitivi, non riesco a pensare ad un bel modo per evitare la duplicazione sforzo qui. Un approccio che ho pensato potrebbe essere, entro 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)
}
Dal momento che è solo il metodo +=
abbiamo veramente a cuore per ottenere specializzata, e quindi otteniamo un Function1
per add
che è specializzata in Long
. Verifica con javap, anzi ho
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;
per la versione Array.newBuilder[A]
(anche in uscita specializzato) e:
252: invokeinterface #204, 3; //InterfaceMethod scala/Function1.apply$mcVJ$sp:(J)V
per la versione contorta. Riesco a racchiudere questo schema in una funzione di "helper del builder specializzato", ma è brutto soprattutto quando è ancora in fase di distribuzione in fase di esecuzione in base a qualcosa di noto durante la compilazione durante la specializzazione. In definitiva, direi che la mia proposta qui è quella di portarla sul fatto che lo Function1
è già specializzato, e non mi piace particolarmente.
Ci sono trucchi intelligenti che posso usare per rendere questo più piacevole? Mi rendo conto che si tratta di un dettaglio davvero di basso livello e raramente sarà critico per le prestazioni, ma vista la quantità di sforzo/duplicazione del codice applicata a tutte le classi specializzate ArrayBuilder.of*
, sembra un peccato buttare via alcuni dei loro vantaggi in cambio di essere polimorfico.
Edit ho pensato a qualcosa di brutto, ma che speravo avrebbe funzionato:
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]
e poi dentro la mia funzione specializzata:
val witness: Array[A] = null
val builder = builderOf(witness)
ma sembra chiamare il generico builderOf
anche nella versione specializzata (anche se sono disponibili sufficienti informazioni sul tipo per chiamare la versione Array[Long]
). Qualcuno sa perché questo non funziona? L'approccio sembra abbastanza pulito, rispetto all'altro che stavo proponendo. Immagino che stavo sperando in un approccio più "macro" alla specializzazione, ma suppongo che non ci sia garanzia che sarà corretto per tutte le istanze a meno che non scelga lo stesso metodo per ogni specializzazione :(
non sono sicuro di vedere come tutto ciò che farete può ottenere qualsiasi specializzazione, dato che 'ArrayBuidler' non è specializzato (e quindi' + = 'non sarà mai specializzato anche se chiamato da un metodo specializzato).Otterrai la specializzazione solo se elimini "ArrayBuidler" del tutto (ad esempio definendo la tua versione specialistica). –
In realtà, mi è venuto in mente che specializzare "solo" il metodo esterno (quello che chiama '+ =') potrebbe già comprarci un significativo aumento di velocità consentendo al jitter di eseguire l'allineamento della cache monorphic. E 'questo quello che avevi in mente? –
Il mio punto (questo è il mio altro account) è che ci sono alcune sottoclassi di 'ArrayBuilder' specializzate chiamate' ofInt', 'ofDouble', ecc. Sono usate quando si chiede' Array.newBuilder [someprimitive] 'ma si può anche istanziarli direttamente. Se usi 'newBuilder', ottieni un 'ArrayBuilder' che non è specializzato, ma se installi un' nuovo ArrayBuilder.ofInt() ', riceverai anche chiamate unboxed a' + = ', ed è quello che stavo provando per catturare sopra. Puoi testare questo annotando il 'new ofInt()' con i tipi più specifici e meno specifici e vedi se ricevi una chiamata al pugilato. – copumpkin