From 1ee7ffb6fd2de6e7194a4eb89601d98503b50048 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 16 Aug 2012 20:03:14 +0200 Subject: Optimizations to cut down on #closures created Driven by profile data. --- .../scala/tools/nsc/transform/Erasure.scala | 2 +- .../scala/tools/nsc/typechecker/Infer.scala | 8 ++- .../scala/collection/LinearSeqOptimized.scala | 59 +++++++++++++++++++++- .../scala/reflect/internal/AnnotationInfos.scala | 23 +++++++-- src/reflect/scala/reflect/internal/Symbols.scala | 14 +++-- src/reflect/scala/reflect/internal/Trees.scala | 2 +- src/reflect/scala/reflect/internal/Types.scala | 25 +++++---- .../reflect/internal/pickling/UnPickler.scala | 15 +++++- src/reflect/scala/tools/nsc/io/ZipArchive.scala | 27 +++++++--- 9 files changed, 144 insertions(+), 31 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 3cb33ec1f8..d97fbf5daa 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -488,7 +488,7 @@ abstract class Erasure extends AddInterfaces private def isPrimitiveValueMember(sym: Symbol) = sym != NoSymbol && isPrimitiveValueClass(sym.owner) - private def box(tree: Tree, target: => String): Tree = { + @inline private def box(tree: Tree, target: => String): Tree = { val result = box1(tree) log("boxing "+tree+":"+tree.tpe+" to "+target+" = "+result+":"+result.tpe) result diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index fc0f9370b4..1870e1511e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -661,7 +661,13 @@ trait Infer { val restp1 = followApply(restp) if (restp1 eq restp) tp else restp1 case _ => - val appmeth = tp.nonPrivateMember(nme.apply) filter (_.isPublic) + val appmeth = { + //OPT cut down on #closures by special casing non-overloaded case + // was: tp.nonPrivateMember(nme.apply) filter (_.isPublic) + val result = tp.nonPrivateMember(nme.apply) + if ((result eq NoSymbol) || !result.isOverloaded && result.isPublic) result + else result filter (_.isPublic) + } if (appmeth == NoSymbol) tp else OverloadedType(tp, appmeth.alternatives) } diff --git a/src/library/scala/collection/LinearSeqOptimized.scala b/src/library/scala/collection/LinearSeqOptimized.scala index 5e0bd010a6..2a06b9d0bd 100755 --- a/src/library/scala/collection/LinearSeqOptimized.scala +++ b/src/library/scala/collection/LinearSeqOptimized.scala @@ -93,6 +93,16 @@ trait LinearSeqOptimized[+A, +Repr <: LinearSeqOptimized[A, Repr]] extends Linea cnt } + override /*SeqLike*/ + def contains(elem: Any): Boolean = { + var these = this + while (!these.isEmpty) { + if (these.head == elem) return true + these = these.tail + } + false + } + override /*IterableLike*/ def find(p: A => Boolean): Option[A] = { var these = this @@ -113,7 +123,54 @@ trait LinearSeqOptimized[+A, +Repr <: LinearSeqOptimized[A, Repr]] extends Linea } acc } - + + override /*TraversableLike*/ + def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { + val b = bf(repr) + b.sizeHint(this) + var these = this + while (!these.isEmpty) { + b += f(these.head) + these = these.tail + } + b.result + } + + override /*TraversableLike*/ + def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { + val b = bf(repr) + var these = this + while (!these.isEmpty) { + b ++= f(these.head).seq + these = these.tail + } + b.result + } + + override /*TraversableLike*/ + def filter(p: A => Boolean): Repr = { + val b = newBuilder + var these = this + while (!these.isEmpty) { + val x = these.head + if (p(x)) b += x + these = these.tail + } + b.result + } + + override /*TraversableLike*/ + def filterNot(p: A => Boolean): Repr = { + val b = newBuilder + var these = this + while (!these.isEmpty) { + val x = these.head + if (!p(x)) b += x + these = these.tail + } + b.result + } + override /*IterableLike*/ def foldRight[B](z: B)(f: (A, B) => B): B = if (this.isEmpty) z diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 229570dafd..a444c786f7 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -8,6 +8,7 @@ package internal import util._ import pickling.ByteCodecs +import scala.annotation.tailrec /** AnnotationInfo and its helpers */ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => @@ -31,11 +32,27 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => case AnnotationInfo(tp, Literal(Constant(tpe: Type)) :: Nil, _) if tp.typeSymbol == ThrowsClass => tpe.typeSymbol } - /** Test for, get, or remove an annotation */ - def hasAnnotation(cls: Symbol) = annotations exists (_ matches cls) - def getAnnotation(cls: Symbol) = annotations find (_ matches cls) + /** Tests for, get, or remove an annotation */ + def hasAnnotation(cls: Symbol): Boolean = + //OPT inlined from exists to save on #closures; was: annotations exists (_ matches cls) + dropOtherAnnotations(annotations, cls).nonEmpty + + def getAnnotation(cls: Symbol): Option[AnnotationInfo] = + //OPT inlined from exists to save on #closures; was: annotations find (_ matches cls) + dropOtherAnnotations(annotations, cls) match { + case ann :: _ => Some(ann) + case _ => None + } + def removeAnnotation(cls: Symbol): Self = filterAnnotations(ann => !(ann matches cls)) + final def withAnnotation(annot: AnnotationInfo): Self = withAnnotations(List(annot)) + + @tailrec private + def dropOtherAnnotations(anns: List[AnnotationInfo], cls: Symbol): List[AnnotationInfo] = anns match { + case ann :: rest => if (ann matches cls) anns else dropOtherAnnotations(rest, cls) + case Nil => Nil + } } /** Arguments to classfile annotations (which are written to diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 15ca276b1a..f9e4005d16 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1897,10 +1897,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => * @param ofclazz The class containing the symbol's definition * @param site The base type from which member types are computed */ - final def matchingSymbol(ofclazz: Symbol, site: Type): Symbol = - ofclazz.info.nonPrivateDecl(name).filter(sym => - !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) - + final def matchingSymbol(ofclazz: Symbol, site: Type): Symbol = { + //OPT cut down on #closures by special casing non-overloaded case + // was: ofclazz.info.nonPrivateDecl(name) filter (sym => + // !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) + val result = ofclazz.info.nonPrivateDecl(name) + def qualifies(sym: Symbol) = !sym.isTerm || (site.memberType(this) matches site.memberType(sym)) + if ((result eq NoSymbol) || !result.isOverloaded && qualifies(result)) result + else result filter qualifies + } + /** The non-private member of `site` whose type and name match the type of this symbol. */ final def matchingSymbol(site: Type, admit: Long = 0L): Symbol = site.nonPrivateMemberAdmitting(name, admit).filter(sym => diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 8baed325fd..8f4c0de001 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -29,7 +29,7 @@ trait Trees extends api.Trees { self: SymbolTable => def setType(tp: Type): this.type = { rawtpe = tp; this } def defineType(tp: Type): this.type = setType(tp) - def symbol: Symbol = null + def symbol: Symbol = null //!!!OPT!!! symbol is about 3% of hot compile times -- megamorphic dispatch? def symbol_=(sym: Symbol) { throw new UnsupportedOperationException("symbol_= inapplicable for " + this) } def setSymbol(sym: Symbol): this.type = { symbol = sym; this } def hasSymbol = false diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 5697df4924..da92d5eb09 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3991,15 +3991,18 @@ trait Types extends api.Types { self: SymbolTable => override def variance = _variance def variance_=(x: Int) = _variance = x - override protected def noChangeToSymbols(origSyms: List[Symbol]) = { - origSyms forall { sym => - val v = variance - if (sym.isAliasType) variance = 0 - val result = this(sym.info) - variance = v - result eq sym.info + override protected def noChangeToSymbols(origSyms: List[Symbol]) = + //OPT inline from forall to save on #closures + origSyms match { + case sym :: rest => + val v = variance + if (sym.isAliasType) variance = 0 + val result = this(sym.info) + variance = v + (result eq sym.info) && noChangeToSymbols(rest) + case _ => + true } - } override protected def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = map2Conserve(args, tparams) { (arg, tparam) => @@ -5131,14 +5134,14 @@ trait Types extends api.Types { self: SymbolTable => 1 } - private def maxDepth(tps: Seq[Type], by: Type => Int): Int = { + private def maxDepth(tps: List[Type], by: Type => Int): Int = { var d = 0 for (tp <- tps) d = d max by(tp) //!!!OPT!!! d } - private def typeDepth(tps: Seq[Type]): Int = maxDepth(tps, typeDepth) - private def baseTypeSeqDepth(tps: Seq[Type]): Int = maxDepth(tps, _.baseTypeSeqDepth) + 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, * for all types tp1, tp2 in intersection diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index dd515b2201..38f61d3e93 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -769,8 +769,21 @@ abstract class UnPickler /*extends reflect.generic.UnPickler*/ { } /* Read a reference to a pickled item */ + protected def readSymbolRef(): Symbol = {//OPT inlined from: at(readNat(), readSymbol) to save on closure creation + val i = readNat() + var r = entries(i) + if (r eq null) { + val savedIndex = readIndex + readIndex = index(i) + r = readSymbol() + assert(entries(i) eq null, entries(i)) + entries(i) = r + readIndex = savedIndex + } + r.asInstanceOf[Symbol] + } + protected def readNameRef(): Name = at(readNat(), readName) - protected def readSymbolRef(): Symbol = at(readNat(), readSymbol) protected def readTypeRef(): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () protected def readConstantRef(): Constant = at(readNat(), readConstant) protected def readAnnotationRef(): AnnotationInfo = at(readNat(), readAnnotation) diff --git a/src/reflect/scala/tools/nsc/io/ZipArchive.scala b/src/reflect/scala/tools/nsc/io/ZipArchive.scala index 852dba9ec8..d1a91294a5 100644 --- a/src/reflect/scala/tools/nsc/io/ZipArchive.scala +++ b/src/reflect/scala/tools/nsc/io/ZipArchive.scala @@ -96,14 +96,25 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq } } - private def ensureDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry = { - dirs.getOrElseUpdate(path, { - val parent = ensureDir(dirs, dirName(path), null) - val dir = new DirEntry(path) - parent.entries(baseName(path)) = dir - dir - }) - } + private def ensureDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry = + //OPT inlined from getOrElseUpdate; saves ~50K closures on test run. + // was: + // dirs.getOrElseUpdate(path, { + // val parent = ensureDir(dirs, dirName(path), null) + // val dir = new DirEntry(path) + // parent.entries(baseName(path)) = dir + // dir + // }) + dirs get path match { + case Some(v) => v + case None => + val parent = ensureDir(dirs, dirName(path), null) + val dir = new DirEntry(path) + parent.entries(baseName(path)) = dir + dirs(path) = dir + dir + } + protected def getDir(dirs: mutable.Map[String, DirEntry], entry: ZipEntry): DirEntry = { if (entry.isDirectory) ensureDir(dirs, entry.getName, entry) else ensureDir(dirs, dirName(entry.getName), null) -- cgit v1.2.3