2013-04-30 56 views
5

Poszukuję dobrych praktyk, aby uniknąć przepisywania tego samego kodu w kółko, aby uzyskać unboxedness. Że mam coś takiego:Specjalizacja Scala ArrayBuilder bez specjalnej tablicy

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

spowoduje unboxed przechowywania bazowego mój builder gdy to możliwe, ale jak rozumiem, nie sposób unboxed wzywa do niego, bo idę przez niewykwalifikowanego ArrayBuilder cechy.

W jednokształtnym świecie specjalizuje się Long, będę pisać val builder = new ArrayBuilder.ofLong() i uniknąć boks w ogóle, ale krótkie opowiadania ArrayBuilder/Builder być specjalizuje się we wszystkich typów pierwotnych, nie mogę myśleć o miły sposób, aby uniknąć duplikowania wysiłek tutaj. Jedno podejście myślałem o może być, w ciągu 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) 
} 

Ponieważ jest to tylko metoda += naprawdę dbają, aby uzyskać specjalistyczną, a następnie otrzymujemy Function1 dla add który specjalizuje się Long. Sprawdzanie z javap, rzeczywiście mam

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; 

dla wersji Array.newBuilder[A] (nawet w produkcji specjalistycznego) oraz:

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

dla wersji zawiłe. Mogę zapakować ten wzorzec w funkcję "wyspecjalizowanego pomocnika budowniczego", ale wydaje się on brzydki, zwłaszcza gdy nadal jest wysyłany w czasie wykonywania w oparciu o coś znanego podczas kompilacji podczas specjalizacji. Ostatecznie powiedziałbym, że moja propozycja polega na tym, że Function1 jest już wyspecjalizowany i nie podoba mi się to szczególnie.

Czy można użyć sprytnych sztuczek, aby uczynić to bardziej przyjemnym? Zdaję sobie sprawę, że jest to bardzo niski poziom szczegółowości i rzadko jest krytyczny pod względem wydajności, ale biorąc pod uwagę wysiłek/duplikację kodu, który trafił do wszystkich wyspecjalizowanych klas ArrayBuilder.of*, wydaje się szkoda wyrzucić niektóre z ich zalet w zamian za jest polimorficzny.

Edit myślałem o czymś brzydkim, ale że ja ufałem będzie działać:

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] 

a następnie wewnątrz mojego wyspecjalizowanej funkcji:

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

ale wydaje się, aby wywołać rodzajowe builderOf nawet w wersji specjalistycznej (nawet jeśli dostępna jest wystarczająca informacja o typie, aby wywołać wersję Array[Long]). Ktoś wie, dlaczego to nie działa? Podejście wydaje się dość czyste, w porównaniu do drugiego, które proponowałem. Sądzę, że miałem nadzieję na bardziej "makropolekcyjne" podejście do specjalizacji, ale myślę, że nie ma gwarancji, że będzie to poprawne dla wszystkich wystąpień, chyba że wybierze tę samą metodę dla każdej specjalizacji: (

+0

nie jestem pewien, w jaki sposób cokolwiek zrobisz może Ci żadnej specjalizacji, biorąc pod uwagę, że 'ArrayBuidler' nie specjalizuje się (a więc' + = 'nigdy nie będzie wyspecjalizowane nawet jeśli zostanie wywołane z wyspecjalizowanej metody).Otrzymasz specjalizację tylko wtedy, gdy całkowicie obejdziesz 'ArrayBuidler' (na przykład definiując swoją własną wyspecjalizowaną wersję). –

+0

Właściwie to zdarzyło mi się, że specjalizacja "tylko" zewnętrznej metody (ta, która nazywa się '+ =') może już kupić nam znaczące przyspieszenie, umożliwiając jitterowi wykonywanie monorficznego wstawiania bufora podręcznego. Czy to właśnie miałeś na myśli? –

+0

Mój punkt (jest to moje drugie konto) jest to, że istnieje kilka wyspecjalizowanych podklas "ArrayBuilder'" o nazwie 'ofInt',' ofDouble', itp. Są one używane, gdy pytasz o 'Array.newBuilder [someprimitive]', ale ty można również utworzyć ich bezpośrednio. Jeśli użyjesz 'newBuilder', otrzymasz' ArrayBuilder', który nie jest wyspecjalizowany, ale jeśli utworzysz 'nowy ArrayBuilder.ofInt()', otrzymasz również unboxed wywołania '+ =' i to właśnie próbowałem przechwycić powyżej. Możesz to przetestować, dodając adnotację do 'new ofInt()' z bardziej szczegółowymi i mniej specyficznymi typami i zobacz, czy otrzymujesz połączenie bokserskie. – copumpkin

Odpowiedz

4

Ty może próbować coś wzdłuż linii (usprawiedliwienia barbarzyńskie nazwy),

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

Dzięki za to! Nie miałem jeszcze okazji go przetestować, ale domyślam się, że implicity są rozwiązywane według wyspecjalizowanego typu, w przeciwieństwie do przeciążonych metod, jak próbowałem w pierwotnym pytaniu. Myślę, że napisany przez ciebie kod da mi jedną instancję dla całego typu, ale nie jest trudno zobaczyć, jak go zmodyfikować, aby budować na żądanie. –

+0

W pewnym sensie jest to tylko powielenie dużej części duplikacji, która znajduje się już w standardowej bibliotece. Wygląda na to, że gdybyśmy chcieli ostrożnie dodawać adnotacje specjalizacyjne do większej części biblioteki zbiorów, dobrym początkiem byłyby "ArrayBuilder" i "Builder". –

+0

@MyseriousDan Tak, zgadzam się. –