Skip to content

Misc microopts (statistics, NoSymbol, backport .info) #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1645,7 +1645,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
}

private val hotCounters =
List(statistics.retainedCount, statistics.retainedByType, statistics.nodeByType)
List(statistics.retainedCount, statistics.retainedByType)
private val parserStats = {
import statistics.treeNodeCount
if (settings.YhotStatisticsEnabled) treeNodeCount :: hotCounters
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5690,7 +5690,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper

def typed(tree: Tree, mode: Mode, pt: Type): Tree = {
lastTreeToTyper = tree
val statsEnabled = StatisticsStatics.areSomeHotStatsEnabled() && statistics.areHotStatsLocallyEnabled
val statsEnabled = StatisticsStatics.areSomeHotStatsEnabled && statistics.areHotStatsLocallyEnabled
val startByType = if (statsEnabled) statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null
if (statsEnabled) statistics.incCounter(visitsByType, tree.getClass)
val shouldPrintTyping = printTypings && !phase.erasedTypes && !noPrintTyping(tree)
Expand Down
176 changes: 87 additions & 89 deletions src/reflect/scala/reflect/internal/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>

protected def nextId() = { ids += 1; ids }

/** Used for deciding in the IDE whether we can interrupt the compiler */
//protected var activeLocks = 0

/** Used for debugging only */
//protected var lockedSyms = scala.collection.immutable.Set[Symbol]()

/** Used to keep track of the recursion depth on locked symbols */
private var _recursionTable = immutable.Map.empty[Symbol, Int]
def recursionTable = _recursionTable
Expand Down Expand Up @@ -587,16 +581,12 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
} else {
_rawflags |= LOCKED
true
// activeLocks += 1
// lockedSyms += this
}
}

// Unlock a symbol
private[scala] def unlock() = {
if ((_rawflags & LOCKED) != 0L) {
// activeLocks -= 1
// lockedSyms -= this
_rawflags &= ~LOCKED
if (settings.Yrecursion.value != 0)
recursionTable -= this
Expand Down Expand Up @@ -840,7 +830,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME)
final def isDelambdafyTarget = isArtifact && isMethod && hasAttachment[DelambdafyTarget.type]
final def isDefinedInPackage = effectiveOwner.isPackageClass
final def needsFlatClasses = phase.flatClasses && (rawowner ne NoSymbol) && !rawowner.isPackageClass && !isMethod
final def needsFlatClasses = phase.flatClasses && !rawowner.isNoSymbol && !rawowner.isPackageClass && !isMethod

// TODO introduce a flag for these?
final def isPatternTypeVariable: Boolean =
Expand Down Expand Up @@ -1096,6 +1086,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
supersym == NoSymbol || supersym.isIncompleteIn(base)
}

final def isNoSymbol: Boolean = isInstanceOf[NoSymbol]
def exists: Boolean = !isTopLevel || {
val isSourceLoader = rawInfo match {
case sl: SymLoader => sl.fromSource
Expand Down Expand Up @@ -1517,41 +1508,42 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
/** Get type info associated with symbol at current phase, after
* ensuring that symbol is initialized (i.e. type is completed).
*/
def info: Type = try {
def info: Type = {
var cnt = 0
while (validTo == NoPeriod) {
assert(infos ne null, this.name)
assert(infos.prev eq null, this.name)
val tp = infos.info

if ((_rawflags & LOCKED) != 0L) { // rolled out once for performance
lock {
setInfo(ErrorType)
throw CyclicReference(this, tp)
}
} else {
_rawflags |= LOCKED
// TODO another commented out lines - this should be solved in one way or another
// activeLocks += 1
// lockedSyms += this
}
val current = phase
try {
assertCorrectThread()
phase = phaseOf(infos.validFrom)
tp.complete(this)
} finally {
unlock()
phase = current
}
while (_validTo == NoPeriod) {
completeInfo()
cnt += 1
// allow for two completions:
// one: sourceCompleter to LazyType, two: LazyType to completed type
if (cnt == 3) abort(s"no progress in completing $this: $tp")
def abortNoProgress() = abort(s"no progress in completing $this: ${infos.info}")
if (cnt == 3) abortNoProgress()
}
rawInfo
}
catch {

private def completeInfo(): Unit = try {
assert(infos ne null, this.name)
assert(infos.prev eq null, this.name)
val tp = infos.info

if ((_rawflags & LOCKED) != 0L) { // rolled out once for performance
lock {
setInfo(ErrorType)
throw CyclicReference(this, tp)
}
} else {
_rawflags |= LOCKED
}
val current = phase
try {
assertCorrectThread()
phase = phaseOf(infos.validFrom)
tp.complete(this)
} finally {
unlock()
phase = current
}
} catch {
case ex: CyclicReference =>
devWarning("... hit cycle trying to complete " + this.fullLocationString)
throw ex
Expand Down Expand Up @@ -1607,81 +1599,87 @@ trait Symbols extends api.Symbols { self: SymbolTable =>

/** Return info without checking for initialization or completing */
def rawInfo: Type = {
// OPT: hoisting the outer reference reduces the bytecode size of this method a little which makes it more
// likely to inline into hot callers of .info
val outer = Symbols.this

var infos = this.infos
assert(infos != null)
val curPeriod = currentPeriod
val curPid = phaseId(curPeriod)
outer.assert(infos != null)

if (_validTo != NoPeriod) {
val curPeriod = outer.currentPeriod
val curPid = outer.phaseId(curPeriod)

if (validTo != NoPeriod) {
// skip any infos that concern later phases
while (curPid < phaseId(infos.validFrom) && infos.prev != null)
while (curPid < outer.phaseId(infos.validFrom) && infos.prev != null)
infos = infos.prev

if (validTo < curPeriod) {
assertCorrectThread()
if (_validTo < curPeriod) {
// adapt any infos that come from previous runs
val current = phase
val curPhase = outer.phase
try {
infos = adaptInfos(infos)
if (infos != null && outer.runId(infos.validFrom) != outer.currentRunId) {
// scala/bug#8871 Discard all but the first element of type history. Specialization only works in the resident
// compiler / REPL if re-run its info transformer in this run to correctly populate its
// per-run caches, e.g. typeEnv
infos = adaptInfo(infos.oldest)
}

//assert(runId(validTo) == currentRunId, name)
//assert(runId(infos.validFrom) == currentRunId, name)

if (validTo < curPeriod) {
var itr = infoTransformers.nextFrom(phaseId(validTo))
infoTransformers = itr; // caching optimization
while (itr.pid != NoPhase.id && itr.pid < current.id) {
phase = phaseWithId(itr.pid)
val info1 = itr.transform(this, infos.info)
if (info1 ne infos.info) {
infos = TypeHistory(currentPeriod + 1, info1, infos)
this.infos = infos
}
_validTo = currentPeriod + 1 // to enable reads from same symbol during info-transform
itr = itr.next
}
_validTo = if (itr.pid == NoPhase.id) curPeriod
else period(currentRunId, itr.pid)
if (_validTo < curPeriod) {
infos = transformInfos(infos, curPhase, curPeriod)
}
} finally {
phase = current
outer.phase = curPhase
}
}
}
infos.info
}

private def transformInfos(infos0: TypeHistory, curPhase: Phase, curPeriod: Period): TypeHistory = {
assertCorrectThread()
var infos = infos0
var itr = infoTransformers.nextFrom(phaseId(_validTo))
infoTransformers = itr; // caching optimization
while (itr.pid != NoPhase.id && itr.pid < curPhase.id) {
phase = phaseWithId(itr.pid)
val info1 = itr.transform(this, infos.info)
if (info1 ne infos.info) {
infos = TypeHistory(currentPeriod + 1, info1, infos)
this.infos = infos
}
_validTo = currentPeriod + 1 // to enable reads from same symbol during info-transform
itr = itr.next
}
_validTo = if (itr.pid == NoPhase.id) curPeriod
else period(currentRunId, itr.pid)
infos
}

// adapt to new run in fsc.
private def adaptInfos(infos: TypeHistory): TypeHistory = {
private def adaptInfo(oldest: TypeHistory): TypeHistory = {
assert(isCompilerUniverse)
if (infos == null || runId(infos.validFrom) == currentRunId) {
infos
} else if (infos ne infos.oldest) {
// scala/bug#8871 Discard all but the first element of type history. Specialization only works in the resident
// compiler / REPL if re-run its info transformer in this run to correctly populate its
// per-run caches, e.g. typeEnv
adaptInfos(infos.oldest)
assert(oldest.prev == null)
val pid = phaseId(oldest.validFrom)

_validTo = period(currentRunId, pid)
phase = phaseWithId(pid)

val info1 = adaptToNewRunMap(oldest.info)
if (info1 eq oldest.info) {
oldest.validFrom = validTo
this.infos = oldest
oldest
} else {
val prev1 = adaptInfos(infos.prev)
if (prev1 ne infos.prev) prev1
else {
val pid = phaseId(infos.validFrom)

_validTo = period(currentRunId, pid)
phase = phaseWithId(pid)

val info1 = adaptToNewRunMap(infos.info)
if (info1 eq infos.info) {
infos.validFrom = validTo
infos
} else {
this.infos = TypeHistory(validTo, info1, prev1)
this.infos
}
}
this.infos = TypeHistory(validTo, info1, null)
this.infos
}
}


/** Raises a `MissingRequirementError` if this symbol is a `StubSymbol` */
def failIfStub() {}

Expand Down
4 changes: 0 additions & 4 deletions src/reflect/scala/reflect/internal/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ trait Trees extends api.Trees {
val id = nodeCount // TODO: add to attachment?
nodeCount += 1

if (StatisticsStatics.areSomeHotStatsEnabled())
statistics.incCounter(statistics.nodeByType, getClass)

final override def pos: Position = rawatt.pos

private[this] var rawtpe: Type = _
Expand Down Expand Up @@ -1955,7 +1952,6 @@ trait TreesStats {
self: Statistics =>
val symbolTable: SymbolTable
val treeNodeCount = newView("#created tree nodes")(symbolTable.nodeCount)
val nodeByType = newByClass("#created tree nodes by type")(newCounter(""))
val retainedCount = newCounter("#retained tree nodes")
val retainedByType = newByClass("#retained tree nodes by type")(newCounter(""))
}
3 changes: 0 additions & 3 deletions src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -729,9 +729,6 @@ private[internal] trait TypeMaps {

/** A base class to compute all substitutions */
abstract class SubstMap[T](from: List[Symbol], to: List[T]) extends TypeMap {
// OPT this check was 2-3% of some profiles, demoted to -Xdev
if (isDeveloper) assert(sameLength(from, to), "Unsound substitution from "+ from +" to "+ to)

private[this] var fromHasTermSymbol = false
private[this] var fromMin = Int.MaxValue
private[this] var fromMax = Int.MinValue
Expand Down
44 changes: 2 additions & 42 deletions src/reflect/scala/reflect/internal/util/StatisticsStatics.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,13 @@
import scala.reflect.internal.util.AlmostFinalValue;
import java.lang.invoke.MethodHandle;

/**
* Represents all the simulated statics for Statistics.
*
* Its implementation delegates to {@link scala.reflect.internal.util.AlmostFinalValue},
* which helps performance (see docs to find out why).
*/
public final class StatisticsStatics {
private static final AlmostFinalValue COLD_STATS = new AlmostFinalValue() {
@Override
protected boolean initialValue() {
return false;
}
};

private static final AlmostFinalValue HOT_STATS = new AlmostFinalValue() {
@Override
protected boolean initialValue() {
return false;
}
};

private static final MethodHandle COLD_STATS_GETTER = COLD_STATS.createGetter();
private static final MethodHandle HOT_STATS_GETTER = HOT_STATS.createGetter();

public static boolean areSomeColdStatsEnabled() throws Throwable {
return (boolean) COLD_STATS_GETTER.invokeExact();
}

public static boolean areSomeHotStatsEnabled() throws Throwable {
return (boolean) HOT_STATS_GETTER.invokeExact();
}
public static final boolean areSomeColdStatsEnabled = false;
public static final boolean areSomeHotStatsEnabled = false;

public static void enableColdStats() throws Throwable {
if (!areSomeColdStatsEnabled())
COLD_STATS.setValue(true);
}

public static void disableColdStats() {
COLD_STATS.setValue(false);
}

public static void enableHotStats() throws Throwable {
if (!areSomeHotStatsEnabled())
HOT_STATS.setValue(true);
}

public static void disableHotStats() {
HOT_STATS.setValue(false);
}
}