From 3f9943be7ad9de6a3443befaec1613682dbd0129 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 29 Nov 2012 17:33:03 +0100 Subject: Eliminate allocations in ListBuffer. ++= on a linear sequence can be accomplished without closure allocation. --- .../scala/collection/mutable/ListBuffer.scala | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index b7b487964c..e059f31929 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -11,6 +11,7 @@ package scala.collection package mutable +import scala.annotation.tailrec import generic._ import immutable.{List, Nil, ::} import java.io._ @@ -178,8 +179,23 @@ final class ListBuffer[A] this } - override def ++=(xs: TraversableOnce[A]): this.type = - if (xs.asInstanceOf[AnyRef] eq this) ++= (this take size) else super.++=(xs) + private def ++=(elems: collection.LinearSeq[A]): this.type = { + @tailrec def loop(xs: collection.LinearSeq[A]) { + if (xs.nonEmpty) { + this += xs.head + loop(xs.tail) + } + } + loop(elems) + this + } + + override def ++=(xs: TraversableOnce[A]): this.type = xs match { + case x: AnyRef if x eq this => this ++= (this take size) + case xs: collection.LinearSeq[_] => this ++= xs + case _ => super.++=(xs) + + } override def ++=:(xs: TraversableOnce[A]): this.type = if (xs.asInstanceOf[AnyRef] eq this) ++=: (this take size) else super.++=:(xs) -- cgit v1.2.3 From c53359ecbe135e79d55a6806209a6301bb386ada Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 29 Nov 2012 17:40:38 +0100 Subject: Eliminate allocations in ClassfileParser. --- .../tools/nsc/symtab/classfile/ClassfileParser.scala | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index cb58111b51..04e860f9db 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1225,16 +1225,20 @@ abstract class ClassfileParser { } def skipAttributes() { - val attrCount = in.nextChar - for (i <- 0 until attrCount) { - in.skip(2); in.skip(in.nextInt) + var attrCount: Int = in.nextChar + while (attrCount > 0) { + in skip 2 + in skip in.nextInt + attrCount -= 1 } } def skipMembers() { - val memberCount = in.nextChar - for (i <- 0 until memberCount) { - in.skip(6); skipAttributes() + var memberCount: Int = in.nextChar + while (memberCount > 0) { + in skip 6 + skipAttributes() + memberCount -= 1 } } -- cgit v1.2.3 From 3059e3a0c039645158d2e5533e84d00f508ca824 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 04:39:08 +0100 Subject: Eliminating more allocations in the collections. --- src/library/scala/collection/IndexedSeqOptimized.scala | 10 ++++++++-- src/library/scala/collection/TraversableLike.scala | 17 ++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/library/scala/collection/IndexedSeqOptimized.scala b/src/library/scala/collection/IndexedSeqOptimized.scala index 09c4b14ba0..9721a42e91 100755 --- a/src/library/scala/collection/IndexedSeqOptimized.scala +++ b/src/library/scala/collection/IndexedSeqOptimized.scala @@ -33,11 +33,17 @@ trait IndexedSeqOptimized[+A, +Repr] extends Any with IndexedSeqLike[A, Repr] { while (i < len) { f(this(i)); i += 1 } } + private def prefixLengthImpl(p: A => Boolean, expectTrue: Boolean): Int = { + var i = 0 + while (i < length && p(apply(i)) == expectTrue) i += 1 + i + } + override /*IterableLike*/ - def forall(p: A => Boolean): Boolean = prefixLength(p(_)) == length + def forall(p: A => Boolean): Boolean = prefixLengthImpl(p, expectTrue = true) == length override /*IterableLike*/ - def exists(p: A => Boolean): Boolean = prefixLength(!p(_)) != length + def exists(p: A => Boolean): Boolean = prefixLengthImpl(p, expectTrue = false) != length override /*IterableLike*/ def find(p: A => Boolean): Option[A] = { diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index c1a68b6b16..a55257d128 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -252,18 +252,21 @@ trait TraversableLike[+A, +Repr] extends Any b.result } + private def filterImpl(p: A => Boolean, isFlipped: Boolean): Repr = { + val b = newBuilder + for (x <- this) + if (p(x) != isFlipped) b += x + + b.result + } + /** Selects all elements of this $coll which satisfy a predicate. * * @param p the predicate used to test elements. * @return a new $coll consisting of all elements of this $coll that satisfy the given * predicate `p`. The order of the elements is preserved. */ - def filter(p: A => Boolean): Repr = { - val b = newBuilder - for (x <- this) - if (p(x)) b += x - b.result - } + def filter(p: A => Boolean): Repr = filterImpl(p, isFlipped = false) /** Selects all elements of this $coll which do not satisfy a predicate. * @@ -271,7 +274,7 @@ trait TraversableLike[+A, +Repr] extends Any * @return a new $coll consisting of all elements of this $coll that do not satisfy the given * predicate `p`. The order of the elements is preserved. */ - def filterNot(p: A => Boolean): Repr = filter(!p(_)) + def filterNot(p: A => Boolean): Repr = filterImpl(p, isFlipped = true) def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) -- cgit v1.2.3 From 78269a68d04d57e65ff0403edb6e06440ea74f7d Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 04:39:14 +0100 Subject: Eliminating allocations in Codec. --- src/library/scala/io/Codec.scala | 55 ++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/src/library/scala/io/Codec.scala b/src/library/scala/io/Codec.scala index 5d046e48b0..bda4234460 100644 --- a/src/library/scala/io/Codec.scala +++ b/src/library/scala/io/Codec.scala @@ -43,42 +43,37 @@ class Codec(val charSet: Charset) { override def toString = name // these methods can be chained to configure the variables above - def onMalformedInput(newAction: Action): this.type = { _onMalformedInput = newAction ; this } - def onUnmappableCharacter(newAction: Action): this.type = { _onUnmappableCharacter = newAction ; this } - def decodingReplaceWith(newReplacement: String): this.type = { _decodingReplacement = newReplacement ; this } + def onMalformedInput(newAction: Action): this.type = { _onMalformedInput = newAction ; this } + def onUnmappableCharacter(newAction: Action): this.type = { _onUnmappableCharacter = newAction ; this } + def decodingReplaceWith(newReplacement: String): this.type = { _decodingReplacement = newReplacement ; this } def encodingReplaceWith(newReplacement: Array[Byte]): this.type = { _encodingReplacement = newReplacement ; this } - def onCodingException(handler: Handler): this.type = { _onCodingException = handler ; this } + def onCodingException(handler: Handler): this.type = { _onCodingException = handler ; this } def name = charSet.name - def encoder = - applyFunctions[CharsetEncoder](charSet.newEncoder(), - (_ onMalformedInput _onMalformedInput, _onMalformedInput != null), - (_ onUnmappableCharacter _onUnmappableCharacter, _onUnmappableCharacter != null), - (_ replaceWith _encodingReplacement, _encodingReplacement != null) - ) - - def decoder = - applyFunctions[CharsetDecoder](charSet.newDecoder(), - (_ onMalformedInput _onMalformedInput, _onMalformedInput != null), - (_ onUnmappableCharacter _onUnmappableCharacter, _onUnmappableCharacter != null), - (_ replaceWith _decodingReplacement, _decodingReplacement != null) - ) + def encoder: CharsetEncoder = { + val enc = charSet.newEncoder() + if (_onMalformedInput ne null) enc onMalformedInput _onMalformedInput + if (_onUnmappableCharacter ne null) enc onUnmappableCharacter _onUnmappableCharacter + if (_encodingReplacement ne null) enc replaceWith _encodingReplacement + enc + } + def decoder: CharsetDecoder = { + val dec = charSet.newDecoder() + if (_onMalformedInput ne null) dec onMalformedInput _onMalformedInput + if (_onUnmappableCharacter ne null) dec onUnmappableCharacter _onUnmappableCharacter + if (_decodingReplacement ne null) dec replaceWith _decodingReplacement + dec + } def wrap(body: => Int): Int = try body catch { case e: CharacterCodingException => _onCodingException(e) } - - // call a series of side effecting methods on an object, finally returning the object - private def applyFunctions[T](x: T, fs: Configure[T]*) = - fs.foldLeft(x)((x, pair) => pair match { - case (f, cond) => if (cond) f(x) else x - }) } trait LowPriorityCodecImplicits { self: Codec.type => /** The Codec of Last Resort. */ - implicit def fallbackSystemCodec: Codec = defaultCharsetCodec + implicit lazy val fallbackSystemCodec: Codec = defaultCharsetCodec } object Codec extends LowPriorityCodecImplicits { @@ -90,9 +85,9 @@ object Codec extends LowPriorityCodecImplicits { * the fact that you can influence anything at all via -Dfile.encoding * as an accident, with any anomalies considered "not a bug". */ - def defaultCharsetCodec = apply(Charset.defaultCharset) - def fileEncodingCodec = apply(scala.util.Properties.encodingString) - def default = defaultCharsetCodec + def defaultCharsetCodec = apply(Charset.defaultCharset) + def fileEncodingCodec = apply(scala.util.Properties.encodingString) + def default = defaultCharsetCodec def apply(encoding: String): Codec = new Codec(Charset forName encoding) def apply(charSet: Charset): Codec = new Codec(charSet) @@ -130,7 +125,7 @@ object Codec extends LowPriorityCodecImplicits { bytes } - implicit def string2codec(s: String) = apply(s) - implicit def charset2codec(c: Charset) = apply(c) - implicit def decoder2codec(cd: CharsetDecoder) = apply(cd) + implicit def string2codec(s: String): Codec = apply(s) + implicit def charset2codec(c: Charset): Codec = apply(c) + implicit def decoder2codec(cd: CharsetDecoder): Codec = apply(cd) } -- cgit v1.2.3 From d3099c0d3ef363f4f1815409051da2edfec81f30 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 04:42:29 +0100 Subject: Eliminating allocations in typeDepth. --- src/reflect/scala/reflect/internal/Types.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 282d7e18ac..599bc2e264 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -5021,19 +5021,19 @@ trait Types extends api.Types { self: SymbolTable => /** The maximum depth of type `tp` */ def typeDepth(tp: Type): Int = tp match { case TypeRef(pre, sym, args) => - typeDepth(pre) max typeDepth(args) + 1 + math.max(typeDepth(pre), typeDepth(args) + 1) case RefinedType(parents, decls) => - typeDepth(parents) max typeDepth(decls.toList.map(_.info)) + 1 + math.max(typeDepth(parents), symTypeDepth(decls.toList) + 1) case TypeBounds(lo, hi) => - typeDepth(lo) max typeDepth(hi) + math.max(typeDepth(lo), typeDepth(hi)) case MethodType(paramtypes, result) => typeDepth(result) case NullaryMethodType(result) => typeDepth(result) case PolyType(tparams, result) => - typeDepth(result) max typeDepth(tparams map (_.info)) + 1 + math.max(typeDepth(result), symTypeDepth(tparams) + 1) case ExistentialType(tparams, result) => - typeDepth(result) max typeDepth(tparams map (_.info)) + 1 + math.max(typeDepth(result), symTypeDepth(tparams) + 1) case _ => 1 } @@ -5045,13 +5045,14 @@ trait Types extends api.Types { self: SymbolTable => // for (tp <- tps) d = d max by(tp) //!!!OPT!!! // d def loop(tps: List[Type], acc: Int): Int = tps match { - case tp :: rest => loop(rest, acc max by(tp)) - case _ => acc + case tp :: rest => loop(rest, math.max(acc, by(tp))) + case _ => acc } loop(tps, 0) } - private def typeDepth(tps: List[Type]): Int = maxDepth(tps, typeDepth) + private def symTypeDepth(syms: List[Symbol]): Int = typeDepth(syms map (_.info)) + private def typeDepth(tps: List[Type]): Int = maxDepth(tps, typeDepth) private def baseTypeSeqDepth(tps: List[Type]): Int = maxDepth(tps, _.baseTypeSeqDepth) /** Is intersection of given types populated? That is, -- cgit v1.2.3 From 1697132ec8e0df21c98a1420d186c58e02af69ab Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 05:19:07 +0100 Subject: Eliminate allocations in Growable. --- src/library/scala/collection/generic/Growable.scala | 17 +++++++++++++++-- src/library/scala/collection/mutable/ListBuffer.scala | 15 --------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/library/scala/collection/generic/Growable.scala b/src/library/scala/collection/generic/Growable.scala index cb75212e3d..52a0d32de1 100644 --- a/src/library/scala/collection/generic/Growable.scala +++ b/src/library/scala/collection/generic/Growable.scala @@ -6,10 +6,11 @@ ** |/ ** \* */ - package scala.collection package generic +import scala.annotation.tailrec + /** This trait forms part of collections that can be augmented * using a `+=` operator and that can be cleared of all elements using * a `clear` method. @@ -45,7 +46,19 @@ trait Growable[-A] extends Clearable { * @param xs the TraversableOnce producing the elements to $add. * @return the $coll itself. */ - def ++=(xs: TraversableOnce[A]): this.type = { xs.seq foreach += ; this } + def ++=(xs: TraversableOnce[A]): this.type = { + @tailrec def loop(xs: collection.LinearSeq[A]) { + if (xs.nonEmpty) { + this += xs.head + loop(xs.tail) + } + } + xs.seq match { + case xs: collection.LinearSeq[_] => loop(xs) + case xs => xs foreach += + } + this + } /** Clears the $coll's contents. After this operation, the * $coll is empty. diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index e059f31929..97d469bca2 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -6,12 +6,9 @@ ** |/ ** \* */ - - package scala.collection package mutable -import scala.annotation.tailrec import generic._ import immutable.{List, Nil, ::} import java.io._ @@ -179,20 +176,8 @@ final class ListBuffer[A] this } - private def ++=(elems: collection.LinearSeq[A]): this.type = { - @tailrec def loop(xs: collection.LinearSeq[A]) { - if (xs.nonEmpty) { - this += xs.head - loop(xs.tail) - } - } - loop(elems) - this - } - override def ++=(xs: TraversableOnce[A]): this.type = xs match { case x: AnyRef if x eq this => this ++= (this take size) - case xs: collection.LinearSeq[_] => this ++= xs case _ => super.++=(xs) } -- cgit v1.2.3 From bf253b8983ec3a2807d2137a9e29b732135eb2dc Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 05:25:16 +0100 Subject: Eliminate allocations in TypeMap. --- src/reflect/scala/reflect/api/Trees.scala | 2 +- src/reflect/scala/reflect/internal/Types.scala | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index cfa6315797..94226ae866 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2921,7 +2921,7 @@ trait Trees { self: Universe => def transform(tree: Tree): Tree = itransform(this, tree) /** Transforms a list of trees. */ - def transformTrees(trees: List[Tree]): List[Tree] = trees mapConserve (transform(_)) + def transformTrees(trees: List[Tree]): List[Tree] = trees mapConserve transform /** Transforms a `Template`. */ def transformTemplate(tree: Template): Template = diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 599bc2e264..b706ef8abe 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4082,8 +4082,13 @@ trait Types extends api.Types { self: SymbolTable => /** Called by mapOver to determine whether the original symbols can * be returned, or whether they must be cloned. Overridden in VariantTypeMap. */ - protected def noChangeToSymbols(origSyms: List[Symbol]) = - origSyms forall (sym => sym.info eq this(sym.info)) + protected def noChangeToSymbols(origSyms: List[Symbol]): Boolean = { + @tailrec def loop(syms: List[Symbol]): Boolean = syms match { + case Nil => true + case x :: xs => (x.info eq this(x.info)) && loop(xs) + } + loop(origSyms) + } /** Map this function over given scope */ def mapOver(scope: Scope): Scope = { -- cgit v1.2.3 From 2e3e43b5971ab93b04ab4677fe23a81bb3291470 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 06:01:17 +0100 Subject: Eliminate allocations in CPSAnnotationChecker. --- .../plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index c147dc483d..cf5b1fa2c4 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -221,7 +221,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } else if (retMode && !hasPlusMarker(tree.tpe) && annotsTree.isEmpty && annotsExpected.nonEmpty) { // add a marker annotation that will make tree.tpe behave as pt, subtyping wise // tree will look like having any possible annotation - + // note 1: we are only adding a plus marker if the method's result type is a cps type // (annotsExpected.nonEmpty == cpsParamAnnotation(pt).nonEmpty) // note 2: we are not adding the expected cps annotations, since they will be added @@ -234,7 +234,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { /** Returns an adapted type for a return expression if the method's result type (pt) is a CPS type. * Otherwise, it returns the `default` type (`typedReturn` passes `NothingClass.tpe`). - * + * * A return expression in a method that has a CPS result type is an error unless the return * is in tail position. Therefore, we are making sure that only the types of return expressions * are adapted which will either be removed, or lead to an error. @@ -396,8 +396,10 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { override def addAnnotations(tree: Tree, tpe: Type): Type = { import scala.util.control._ if (!cpsEnabled) { - if (Exception.failAsValue(classOf[MissingRequirementError])(false)(hasCpsParamTypes(tpe))) + val report = try hasCpsParamTypes(tpe) catch { case _: MissingRequirementError => false } + if (report) global.reporter.error(tree.pos, "this code must be compiled with the Scala continuations plugin enabled") + return tpe } -- cgit v1.2.3 From 9a6320b882495e93210b0e11dad02271306d83d2 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 06:08:41 +0100 Subject: Eliminate allocations in BaseTypeSeqs. --- src/reflect/scala/reflect/internal/BaseTypeSeqs.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index eba10e8ffb..18a4a36840 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -193,15 +193,23 @@ trait BaseTypeSeqs { i += 1 } var minTypes: List[Type] = List() + def alreadyInMinTypes(tp: Type): Boolean = { + @annotation.tailrec def loop(tps: List[Type]): Boolean = tps match { + case Nil => false + case x :: xs => (tp =:= x) || loop(xs) + } + loop(minTypes) + } + i = 0 while (i < nparents) { if (nextTypeSymbol(i) == minSym) { nextRawElem(i) match { case RefinedType(variants, decls) => for (tp <- variants) - if (!(minTypes exists (tp =:= _))) minTypes = tp :: minTypes + if (!alreadyInMinTypes(tp)) minTypes ::= tp case tp => - if (!(minTypes exists (tp =:= _))) minTypes = tp :: minTypes + if (!alreadyInMinTypes(tp)) minTypes ::= tp } index(i) = index(i) + 1 } -- cgit v1.2.3 From cdf6feb1aea366c33ac99e3e5f1e235f7ea0ae19 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 06:23:51 +0100 Subject: Eliminate allocations in uncurry and the backend. --- src/compiler/scala/tools/nsc/backend/icode/Members.scala | 9 +++++++-- src/compiler/scala/tools/nsc/transform/UnCurry.scala | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index 12daa32186..248a505b54 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -46,8 +46,13 @@ trait Members { def touched = _touched def touched_=(b: Boolean): Unit = { - if (b) - blocks foreach (_.touched = true) + @annotation.tailrec def loop(xs: List[BasicBlock]) { + xs match { + case Nil => + case x :: xs => x.touched = true ; loop(xs) + } + } + if (b) loop(blocks.toList) _touched = b } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 6e89f6387e..f4e40a216e 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -114,7 +114,8 @@ abstract class UnCurry extends InfoTransform def isByNameRef(tree: Tree) = ( tree.isTerm && !byNameArgs(tree) - && tree.hasSymbolWhich(isByName) + && (tree.symbol ne null) + && (isByName(tree.symbol)) ) /** Uncurry a type of a tree node. -- cgit v1.2.3 From 113405b935db20705b88df4fd3ff24273e4391bc Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 06:30:32 +0100 Subject: Eliminate allocations in Trees. --- src/reflect/scala/reflect/api/Trees.scala | 6 ++++-- src/reflect/scala/reflect/internal/Trees.scala | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index 94226ae866..05458cb311 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2951,8 +2951,10 @@ trait Trees { self: Universe => if (exprOwner != currentOwner && stat.isTerm) atOwner(exprOwner)(transform(stat)) else transform(stat)) filter (EmptyTree != _) /** Transforms `Modifiers`. */ - def transformModifiers(mods: Modifiers): Modifiers = - mods.mapAnnotations(transformTrees) + def transformModifiers(mods: Modifiers): Modifiers = { + if (mods.annotations.isEmpty) mods + else mods mapAnnotations transformTrees + } /** Transforms a tree with a given owner symbol. */ def atOwner[A](owner: Symbol)(trans: => A): A = { diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 9e737528d2..870c1ec5ed 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -926,8 +926,11 @@ trait Trees extends api.Trees { self: SymbolTable => def withPosition(flag: Long, position: Position) = copy() setPositions positions + (flag -> position) - override def mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = - Modifiers(flags, privateWithin, f(annotations)) setPositions positions + override def mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = { + val newAnns = f(annotations) + if (annotations == newAnns) this + else Modifiers(flags, privateWithin, newAnns) setPositions positions + } override def toString = "Modifiers(%s, %s, %s)".format(flagString, annotations mkString ", ", positions) } -- cgit v1.2.3 From 57c40c54d6119c42e256d6f7c4c7681a5257b266 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 11:04:58 +0100 Subject: Eliminate allocations in Specialize. --- src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 173ca1e628..116b6ab58f 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -10,6 +10,7 @@ import scala.tools.nsc.symtab.Flags import scala.collection.{ mutable, immutable } import scala.language.postfixOps import scala.language.existentials +import scala.annotation.tailrec /** Specialize code on types. * @@ -403,11 +404,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case _ => false }) def specializedTypeVars(tpes: List[Type]): immutable.Set[Symbol] = { - if (tpes.isEmpty) immutable.Set.empty else { - val buf = Set.newBuilder[Symbol] - tpes foreach (tp => buf ++= specializedTypeVars(tp)) - buf.result + @tailrec def loop(result: immutable.Set[Symbol], xs: List[Type]): immutable.Set[Symbol] = { + if (xs.isEmpty) result + else loop(result ++ specializedTypeVars(xs.head), xs.tail) } + loop(immutable.Set.empty, tpes) } def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = ( if (definitions.neverHasTypeParameters(sym)) immutable.Set.empty -- cgit v1.2.3 From eb491d2f1a857a25e381eb23275b78ccafd2981a Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 11:05:30 +0100 Subject: Eliminate allocations in Trees and Symbols. --- src/reflect/scala/reflect/api/Trees.scala | 3 ++- src/reflect/scala/reflect/internal/Symbols.scala | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index 05458cb311..0b44d6a237 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2921,7 +2921,8 @@ trait Trees { self: Universe => def transform(tree: Tree): Tree = itransform(this, tree) /** Transforms a list of trees. */ - def transformTrees(trees: List[Tree]): List[Tree] = trees mapConserve transform + def transformTrees(trees: List[Tree]): List[Tree] = + if (trees.isEmpty) Nil else trees mapConserve transform /** Transforms a `Template`. */ def transformTemplate(tree: Template): Template = diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index fd5c3909b8..3d43500ef1 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2048,7 +2048,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Returns all symbols overriden by this symbol. */ final def allOverriddenSymbols: List[Symbol] = ( if ((this eq NoSymbol) || !owner.isClass) Nil - else owner.ancestors map overriddenSymbol filter (_ != NoSymbol) + else { + def loop(xs: List[Symbol]): List[Symbol] = xs match { + case Nil => Nil + case x :: xs => + overriddenSymbol(x) match { + case NoSymbol => loop(xs) + case sym => sym :: loop(xs) + } + } + loop(owner.ancestors) + } ) /** Equivalent to allOverriddenSymbols.nonEmpty, but more efficient. */ -- cgit v1.2.3 From 6a288b632e0e78a96f1298be9b4e8231728183af Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 30 Nov 2012 11:22:00 +0100 Subject: Eliminate allocations in Types. At this commit the statistics when compiling src/library are as follows. These counts are precise, collected by a modified Function1 which counts every instantiation of every implementing class. The net result is 27 million fewer allocations, over a 20% drop. // master (5b5635ee9d), total and top five by count: Total Function1 allocations: 128,805,865 scala.collection.immutable.$colon$colon 26781958 scala.collection.mutable.ListBuffer 15365174 scala.collection.TraversableLike$$anonfun$map$1 9127787 scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1 4636154 scala.collection.mutable.StringBuilder 3531211 // After these commits, total and top five by count: Total Function1 allocations: 101,865,721 scala.collection.immutable.$colon$colon 26993704 scala.collection.mutable.ListBuffer 15319656 scala.collection.TraversableLike$$anonfun$map$1 7585019 scala.reflect.internal.Types$MethodType$$anonfun$paramTypes$1 2447307 scala.reflect.internal.Types$SubstSymMap 2436088 --- src/reflect/scala/reflect/internal/Types.scala | 76 ++++++++++++++------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index b706ef8abe..c82904ae67 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -5023,42 +5023,9 @@ trait Types extends api.Types { self: SymbolTable => else if (bd <= 7) td max (bd - 2) else (td - 1) max (bd - 3) - /** The maximum depth of type `tp` */ - def typeDepth(tp: Type): Int = tp match { - case TypeRef(pre, sym, args) => - math.max(typeDepth(pre), typeDepth(args) + 1) - case RefinedType(parents, decls) => - math.max(typeDepth(parents), symTypeDepth(decls.toList) + 1) - case TypeBounds(lo, hi) => - math.max(typeDepth(lo), typeDepth(hi)) - case MethodType(paramtypes, result) => - typeDepth(result) - case NullaryMethodType(result) => - typeDepth(result) - case PolyType(tparams, result) => - math.max(typeDepth(result), symTypeDepth(tparams) + 1) - case ExistentialType(tparams, result) => - math.max(typeDepth(result), symTypeDepth(tparams) + 1) - case _ => - 1 - } - - private def maxDepth(tps: List[Type], by: Type => Int): Int = { - //OPT replaced with tailrecursive function to save on #closures - // was: - // var d = 0 - // for (tp <- tps) d = d max by(tp) //!!!OPT!!! - // d - def loop(tps: List[Type], acc: Int): Int = tps match { - case tp :: rest => loop(rest, math.max(acc, by(tp))) - case _ => acc - } - loop(tps, 0) - } - private def symTypeDepth(syms: List[Symbol]): Int = typeDepth(syms map (_.info)) - private def typeDepth(tps: List[Type]): Int = maxDepth(tps, typeDepth) - private def baseTypeSeqDepth(tps: List[Type]): Int = maxDepth(tps, _.baseTypeSeqDepth) + private def typeDepth(tps: List[Type]): Int = maxDepth(tps) + private def baseTypeSeqDepth(tps: List[Type]): Int = maxBaseTypeSeqDepth(tps) /** Is intersection of given types populated? That is, * for all types tp1, tp2 in intersection @@ -7006,6 +6973,45 @@ trait Types extends api.Types { self: SymbolTable => private[scala] val typeIsAny = (tp: Type) => tp.typeSymbolDirect eq AnyClass private[scala] val typeIsHigherKinded = (tp: Type) => tp.isHigherKinded + /** The maximum depth of type `tp` */ + def typeDepth(tp: Type): Int = tp match { + case TypeRef(pre, sym, args) => + math.max(typeDepth(pre), typeDepth(args) + 1) + case RefinedType(parents, decls) => + math.max(typeDepth(parents), symTypeDepth(decls.toList) + 1) + case TypeBounds(lo, hi) => + math.max(typeDepth(lo), typeDepth(hi)) + case MethodType(paramtypes, result) => + typeDepth(result) + case NullaryMethodType(result) => + typeDepth(result) + case PolyType(tparams, result) => + math.max(typeDepth(result), symTypeDepth(tparams) + 1) + case ExistentialType(tparams, result) => + math.max(typeDepth(result), symTypeDepth(tparams) + 1) + case _ => + 1 + } + //OPT replaced with tailrecursive function to save on #closures + // was: + // var d = 0 + // for (tp <- tps) d = d max by(tp) //!!!OPT!!! + // d + private[scala] def maxDepth(tps: List[Type]): Int = { + @tailrec def loop(tps: List[Type], acc: Int): Int = tps match { + case tp :: rest => loop(rest, math.max(acc, typeDepth(tp))) + case _ => acc + } + loop(tps, 0) + } + private[scala] def maxBaseTypeSeqDepth(tps: List[Type]): Int = { + @tailrec def loop(tps: List[Type], acc: Int): Int = tps match { + case tp :: rest => loop(rest, math.max(acc, tp.baseTypeSeqDepth)) + case _ => acc + } + loop(tps, 0) + } + @tailrec private def typesContain(tps: List[Type], sym: Symbol): Boolean = tps match { case tp :: rest => (tp contains sym) || typesContain(rest, sym) case _ => false -- cgit v1.2.3