Skip to content

Commit 63b7ae6

Browse files
committed
Measure typeclass scaling using new scheme
Summary: - Code size increases by 13% (counting characters) to 15% (counting words) - Compile time increases by 3% (wallclock) to 4% (user) Runtime should be somewhat better for new scheme since there are fewer allocations. (Btw, the -Yshow-no-inlines option in the old measurements should be ignored; it is no linger valid and was not included in the tests)
1 parent 2ba6c69 commit 63b7ae6

File tree

1 file changed

+73
-63
lines changed

1 file changed

+73
-63
lines changed

tests/pos-special/typeclass-scaling.scala

+73-63
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ import scala.annotation.tailrec
33

44
// The following command:
55
//
6-
// sc typeclass-scaling.scala -Xmax-inlines 100 -Xprint:front -color:never -Yshow-no-inline -pagewidth 1000 >& x
6+
// sc typeclass-scaling.scala -Xmax-inlines 100 -Xprint:front -color:never -pagewidth 1000 >& x
77
//
88
// produces an output file with `wc` measures (lines/words/chars):
99
//
10-
// 83434 140554 6384738
10+
// 89327 162884 7220258
1111
//
1212
// The command
1313
//
1414
// time sc typeclass-scaling.scala -Xmax-inlines 100
1515
//
1616
// gives (best of three):
1717
//
18-
// real 0m16.061s
19-
// user 1m3.608s
20-
// sys 0m1.314s
18+
// real 0m16.593s
19+
// user 1m6.337s
20+
// sys 0m1.344s
2121
object datatypes {
2222
import typeclasses._
2323

@@ -215,42 +215,45 @@ object typeclasses {
215215
object Eq {
216216
import scala.compiletime.erasedValue
217217
import compiletime._
218-
import reflect.{Mirror, Generic}
218+
import scala.deriving._
219219

220-
inline def tryEql[T](x: T, y: T) = implicit match {
221-
case eq: Eq[T] => eq.eql(x, y)
220+
inline def tryEql[TT](x: TT, y: TT): Boolean = implicit match {
221+
case eq: Eq[TT] => eq.eql(x, y)
222222
}
223223

224-
inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean =
224+
inline def eqlElems[Elems <: Tuple](n: Int)(x: Any, y: Any): Boolean =
225225
inline erasedValue[Elems] match {
226226
case _: (elem *: elems1) =>
227-
tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) &&
228-
eqlElems[elems1](xs, ys, n + 1)
227+
tryEql[elem](productElement[elem](x, n), productElement[elem](y, n)) &&
228+
eqlElems[elems1](n + 1)(x, y)
229229
case _: Unit =>
230230
true
231231
}
232232

233-
inline def eqlCases[Alts <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean =
233+
inline def eqlProduct[T](m: Mirror.ProductOf[T])(x: Any, y: Any): Boolean =
234+
eqlElems[m.MirroredElemTypes](0)(x, y)
235+
236+
inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean =
234237
inline erasedValue[Alts] match {
235-
case _: (Shape.Case[alt, elems] *: alts1) =>
236-
if (xm.ordinal == n) eqlElems[elems](xm, ym, 0)
237-
else eqlCases[alts1](xm, ym, n + 1)
238-
case _: Unit =>
238+
case _: (alt *: alts1) =>
239+
if (ord == n)
240+
implicit match {
241+
case m: Mirror.ProductOf[`alt`] => eqlElems[m.MirroredElemTypes](0)(x, y)
242+
}
243+
else eqlCases[alts1](n + 1)(x, y, ord)
244+
case _: Unit =>
239245
false
240246
}
241247

242-
inline def derived[T](implicit ev: Generic[T]): Eq[T] = new {
243-
def eql(x: T, y: T): Boolean = {
244-
val xm = ev.reflect(x)
245-
val ym = ev.reflect(y)
246-
inline erasedValue[ev.Shape] match {
247-
case _: Shape.Cases[alts] =>
248-
xm.ordinal == ym.ordinal &&
249-
eqlCases[alts](xm, ym, 0)
250-
case _: Shape.Case[_, elems] =>
251-
eqlElems[elems](xm, ym, 0)
248+
inline def derived[T](implicit ev: Mirror.Of[T]): Eq[T] = new Eq[T] {
249+
def eql(x: T, y: T): Boolean =
250+
inline ev match {
251+
case m: Mirror.SumOf[T] =>
252+
val ord = m.ordinal(x)
253+
ord == m.ordinal(y) && eqlCases[m.MirroredElemTypes](0)(x, y, ord)
254+
case m: Mirror.ProductOf[T] =>
255+
eqlElems[m.MirroredElemTypes](0)(x, y)
252256
}
253-
}
254257
}
255258

256259
implicit object IntEq extends Eq[Int] {
@@ -267,79 +270,86 @@ object typeclasses {
267270
object Pickler {
268271
import scala.compiletime.{erasedValue, constValue}
269272
import compiletime._
270-
import reflect.{Mirror, Generic}
273+
import deriving._
271274

272275
def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1)
273276

274277
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = implicit match {
275278
case pkl: Pickler[T] => pkl.pickle(buf, x)
276279
}
277280

278-
inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit =
281+
inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any): Unit =
279282
inline erasedValue[Elems] match {
280283
case _: (elem *: elems1) =>
281-
tryPickle[elem](buf, elems(n).asInstanceOf[elem])
282-
pickleElems[elems1](buf, elems, n + 1)
284+
tryPickle[elem](buf, productElement[elem](x, n))
285+
pickleElems[elems1](n + 1)(buf, x)
283286
case _: Unit =>
284287
}
285288

286-
inline def pickleCases[Alts <: Tuple](buf: mutable.ListBuffer[Int], xm: Mirror, n: Int): Unit =
289+
inline def pickleCases[Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any, ord: Int): Unit =
287290
inline erasedValue[Alts] match {
288-
case _: (Shape.Case[alt, elems] *: alts1) =>
289-
if (xm.ordinal == n) pickleElems[elems](buf, xm, 0)
290-
else pickleCases[alts1](buf, xm, n + 1)
291+
case _: (alt *: alts1) =>
292+
if (ord == n)
293+
implicit match {
294+
case m: Mirror.ProductOf[`alt`] => pickleElems[m.MirroredElemTypes](0)(buf, x)
295+
}
296+
else pickleCases[alts1](n + 1)(buf, x, ord)
291297
case _: Unit =>
292298
}
293299

294300
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = implicit match {
295301
case pkl: Pickler[T] => pkl.unpickle(buf)
296302
}
297303

298-
inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit =
304+
inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: ArrayProduct): Unit =
299305
inline erasedValue[Elems] match {
300306
case _: (elem *: elems1) =>
301307
elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef]
302-
unpickleElems[elems1](buf, elems, n + 1)
308+
unpickleElems[elems1](n + 1)(buf, elems)
303309
case _: Unit =>
304310
}
305311

306-
inline def unpickleCase[T, Elems <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = {
312+
inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.ProductOf[T]): T = {
307313
inline val size = constValue[Tuple.Size[Elems]]
308314
inline if (size == 0)
309-
gen.reify(gen.common.mirror(ordinal))
315+
m.fromProduct(EmptyProduct)
310316
else {
311-
val elems = new Array[Object](size)
312-
unpickleElems[Elems](buf, elems, 0)
313-
gen.reify(gen.common.mirror(ordinal, elems))
317+
val elems = new ArrayProduct(size)
318+
unpickleElems[Elems](0)(buf, elems)
319+
m.fromProduct(elems)
314320
}
315321
}
316322

317-
inline def unpickleCases[T, Alts <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T =
323+
inline def unpickleCases[T, Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], ord: Int): T =
318324
inline erasedValue[Alts] match {
319-
case _: (Shape.Case[_, elems] *: alts1) =>
320-
if (n == ordinal) unpickleCase[T, elems](gen, buf, ordinal)
321-
else unpickleCases[T, alts1](gen, buf, ordinal, n + 1)
322-
case _ =>
323-
throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal")
325+
case _: (alt *: alts1) =>
326+
if (ord == n)
327+
implicit match {
328+
case m: Mirror.ProductOf[`alt` & T] =>
329+
unpickleCase[`alt` & T, m.MirroredElemTypes](buf, m)
330+
}
331+
else unpickleCases[T, alts1](n + 1)(buf, ord)
332+
case _: Unit =>
333+
throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ord")
324334
}
325335

326-
inline def derived[T](implicit ev: Generic[T]): Pickler[T] = new {
327-
def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = {
328-
val xm = ev.reflect(x)
329-
inline erasedValue[ev.Shape] match {
330-
case _: Shape.Cases[alts] =>
331-
buf += xm.ordinal
332-
pickleCases[alts](buf, xm, 0)
333-
case _: Shape.Case[_, elems] =>
334-
pickleElems[elems](buf, xm, 0)
336+
inline def derived[T](implicit ev: Mirror.Of[T]): Pickler[T] = new {
337+
def pickle(buf: mutable.ListBuffer[Int], x: T): Unit =
338+
inline ev match {
339+
case m: Mirror.SumOf[T] =>
340+
val ord = m.ordinal(x)
341+
buf += ord
342+
pickleCases[m.MirroredElemTypes](0)(buf, x, ord)
343+
case m: Mirror.ProductOf[T] =>
344+
pickleElems[m.MirroredElemTypes](0)(buf, x)
335345
}
336-
}
337346
def unpickle(buf: mutable.ListBuffer[Int]): T =
338-
inline erasedValue[ev.Shape] match {
339-
case _: Shape.Cases[alts] =>
340-
unpickleCases[T, alts](ev, buf, nextInt(buf), 0)
341-
case _: Shape.Case[_, elems] =>
342-
unpickleCase[T, elems](ev, buf, 0)
347+
inline ev match {
348+
case m: Mirror.SumOf[T] =>
349+
val ord = nextInt(buf)
350+
unpickleCases[T, m.MirroredElemTypes](0)(buf, ord)
351+
case m: Mirror.ProductOf[T] =>
352+
unpickleCase[T, m.MirroredElemTypes](buf, m)
343353
}
344354
}
345355

0 commit comments

Comments
 (0)