Merge pull request #8364 from eed3si9n/wip/cache-analysis

[2.x] perf: Cache Zinc Analysis across compilations
This commit is contained in:
eugene yokota 2025-11-12 22:27:02 -05:00 committed by GitHub
commit 7a96851176
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 37 additions and 3 deletions

View File

@ -9,7 +9,10 @@
package sbt
package internal
import com.github.benmanes.caffeine.cache.{ Cache as CCache, Caffeine, Weigher }
import java.io.File
import java.nio.file.{ Files, NoSuchFileException }
import java.nio.file.attribute.BasicFileAttributes
import Keys.{ organization, thisProject, autoGeneratedProject, publish, publishLocal, skip }
import Def.Setting
// import sbt.ProjectExtra.apply
@ -18,7 +21,7 @@ import sbt.internal.util.{ Attributed, StringAttributeMap }
import sbt.internal.inc.{ FileAnalysisStore, ReflectUtilities }
import sbt.util.CacheImplicits.given
import xsbti.{ FileConverter, VirtualFileRef }
import xsbti.compile.CompileAnalysis
import xsbti.compile.{ AnalysisContents, CompileAnalysis }
trait BuildDef {
def projectDefinitions(@deprecated("unused", "") baseDirectory: File): Seq[Project] = projects
@ -87,6 +90,26 @@ private[sbt] object BuildDef:
): Seq[xsbti.compile.CompileAnalysis] =
in.flatMap(a => extractAnalysis(a.metadata, converter))
private[sbt] final val localAnalysisCacheByteSize = 100 * 1024L * 1024L
private val weigher: Weigher[String, (Option[AnalysisContents], Long, Long)] = {
case (_, (_, _, sizeBytes)) => sizeBytes.toInt
}
private val inMemoryAnalysisCache: CCache[String, (Option[AnalysisContents], Long, Long)] =
Caffeine
.newBuilder()
.maximumWeight(localAnalysisCacheByteSize)
.weigher(weigher)
.build()
private def getOrElseUpdate(ref: VirtualFileRef, lastModified: Long, sizeBytes: Long)(
value: => Option[AnalysisContents]
): Option[AnalysisContents] =
Option(inMemoryAnalysisCache.getIfPresent(ref.id())) match
case Some((v, mod, i)) if lastModified == mod && sizeBytes == i => v
case _ =>
val v = value
inMemoryAnalysisCache.put(ref.id(), (v, lastModified, sizeBytes))
v
private[sbt] def extractAnalysis(
metadata: StringAttributeMap,
converter: FileConverter
@ -94,10 +117,21 @@ private[sbt] object BuildDef:
import sbt.OptionSyntax.*
def asBinary(file: File) = FileAnalysisStore.binary(file).get.asScala
def asText(file: File) = FileAnalysisStore.text(file).get.asScala
def fallback(file: File) = asBinary(file).orElse(asText(file))
def getContents(ref: VirtualFileRef): Option[AnalysisContents] =
val path = converter.toPath(ref)
val file = path.toFile()
try
val attrs = Files.readAttributes(path, classOf[BasicFileAttributes])
if attrs.isDirectory then fallback(file)
else
val lastModified = attrs.lastModifiedTime().toMillis()
val sizeBytes = attrs.size()
getOrElseUpdate(ref, lastModified, sizeBytes)(fallback(file))
catch case _: NoSuchFileException => fallback(file)
for
ref <- metadata.get(Keys.analysis)
file = converter.toPath(VirtualFileRef.of(ref)).toFile
content <- asBinary(file).orElse(asText(file))
content <- getContents(VirtualFileRef.of(ref))
yield content.getAnalysis
end BuildDef