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