The language’s scalability is the result of a careful integration of object-oriented and functional language concepts.
But nothing comes for free.
class WrappedArray[A] extends IndexedSeq
class IndexedSeq[A] {
def sum[B >: A](implicit num: Numeric[B]): B =
foldLeft(num.zero)(num.plus)
def foldLeft[B](z: B)(op: (B, A) => B): B =
foldl(0, len, z, op)
private def foldl[B](start: Int, end: Int,
z: B, op: (B, A) => B): B =
if (start == end) z
else foldl(start + 1, end, op(z, this(start)), op)
13x slowdown due to boxing
@specialize
solve this problem?class WrappedArray[@specialized A] extends IndexedSeq
class IndexedSeq[@specialized A] {
def sum[B >: A](implicit num: Numeric[B]): B =
foldLeft(num.zero)(num.plus)
def foldLeft[@specialized B](z: B)(op: (B, A) => B): B =
foldl(0, len, z, op)
private def foldl[@specialized B](start: Int, end: Int,
z: B, op: (B, A) => B): B =
if (start == end) z
else foldl(start + 1, end, op(z, this(start)), op)
def sumLogged[@specialized B >: A, C <: Logger[A]]
(implicit num: Numeric[B], log: C)
trait Function3[-T1, -T2, -T3 +R]
How many specializations are needed?
Specialization: 10000 classes
Miniboxing: 81 classes
Detect program entry points, mark them as reachable
For every reachable method:
Paper: Ali K., Rapoport M., Lhoták O., Dolby J., Tip F. Constructing Call Graphs of Scala Programs, ECOOP 2014
// in IndexedSeq
def sum[B >: A](implicit num: Numeric[B]): B =
foldLeft(num.zero)(num.plus)
def foldLeft[B](z: B)(op: (B, A) => B): B =
foldl(0, len, z, op)
private def foldl[B](start: Int, end: Int,
z: B, op: (B, A) => B): B =
if (start == end) z
else foldl(start + 1, end, op(z, this(start)), op)
// in App.scala
def main(args: Array[String]) = println(args.map(_.toInt).sum)
main
as entry pointmap
is called from main
with tparams [String]sum
is called from main
with tparams [Int]foldLeft
is called from sum
with tparams [Int]foldl
is called from foldLeft
with tparams [Int]Function.apply
is called from foldl
with tparams [Int, Int, Int]original | linker | proguard | |
squeryl | 3000 | 400 | 1500 |
kiama | 15000 | 7200 | 13000 |
dotty | 35000 | 18000 | 32000 |
Class needs to follow the restrictions:
But what if some other program has a class that inherits from class that was marked as a value class?
def sum[B >: A](implicit num: Numeric[B]): B =
foldLeft(num.zero)(num.plus)
def foldLeft[B](z: B)(op: (B, A) => B): B =
foldl(0, len, z, op)
private def foldl[B](start: Int, end: Int,
z: B, op: (B, A) => B): B =
if (start == end) z
else foldl(start + 1, end, op(z, this(start)), op)
Array(1,2,3).sum
я
def sum[B >: A](implicit num: Numeric[B]): B =
foldLeft(num.zero)(num.plus)
def foldLeft[B](z: B)(op: (B, A) => B): B =
foldl(0, len, z, op)
private def foldl[B](start: Int, end: Int,
z: B, op: (B, A) => B): B =
if (start == end) z
else foldl(start + 1, end, op(z, this(start)), op)
Array(1,2,3).sum
Array(1,2,3).foldLeft(IntIsIntegral.zero)(IntIsIntegral.plus)
я
def sum[B >: A](implicit num: Numeric[B]): B =
foldLeft(num.zero)(num.plus)
def foldLeft[B](z: B)(op: (B, A) => B): B =
foldl(0, len, z, op)
private def foldl[B](start: Int, end: Int,
z: B, op: (B, A) => B): B =
if (start == end) z
else foldl(start + 1, end, op(z, this(start)), op)
Array(1,2,3).sum
Array(1,2,3).foldLeft(IntIsIntegral.zero)(IntIsIntegral.plus)
Array(1,2,3).foldl(0, len, IntIsIntegral.zero, IntIsIntegral.plus)
я
def sum[B >: A](implicit num: Numeric[B]): B =
foldLeft(num.zero)(num.plus)
def foldLeft[B](z: B)(op: (B, A) => B): B =
foldl(0, len, z, op)
private def foldl[B](start: Int, end: Int,
z: B, op: (B, A) => B): B =
if (start == end) z
else foldl(start + 1, end, op(z, this(start)), op)
def sum1(implicit num: IntIsIntegral.type): Int =
foldLeft1(num.zero)(num.plus)
def foldLeft1(z: IntIsIntegral.zero.type)
(op: IntIsIntegral.plus.type): Int =
foldl1(0, len, z, op)
original | proguard | linker | |
methods | 1036 | 823 | 196 |
bytecode size | 500KB | 330KB | 180KB |
JVM code cache size(1000 runs) | 1700KB | 1500KB | 1000KB |
objects allocated | 136K | 120K | 39K |
for(i <- 1 to 10) {
(1 to i).map(_ + 1).mkString
}
for(i <- 1 to 10) region {
(1 to i).map(_ + 1).mkString
}
12 times faster with region