[IR BE] Add runtime compilation benchmarks for IR JS compiler
This commit is contained in:
@@ -8,18 +8,16 @@ package org.jetbrains.kotlin.library.impl
|
|||||||
import org.jetbrains.kotlin.library.*
|
import org.jetbrains.kotlin.library.*
|
||||||
|
|
||||||
abstract class IrWriterImpl(val irLayout: IrKotlinLibraryLayout) : IrWriter {
|
abstract class IrWriterImpl(val irLayout: IrKotlinLibraryLayout) : IrWriter {
|
||||||
init {
|
|
||||||
irLayout.irDir.mkdirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addDataFlowGraph(dataFlowGraph: ByteArray) {
|
override fun addDataFlowGraph(dataFlowGraph: ByteArray) {
|
||||||
irLayout.dataFlowGraphFile.writeBytes(dataFlowGraph)
|
irLayout.dataFlowGraphFile.writeBytes(dataFlowGraph)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IrMonoliticWriterImpl(irLayout: IrKotlinLibraryLayout) : IrWriterImpl(irLayout) {
|
class IrMonoliticWriterImpl(_irLayout: IrKotlinLibraryLayout) : IrWriterImpl(_irLayout) {
|
||||||
|
|
||||||
override fun addIr(ir: SerializedIrModule) {
|
override fun addIr(ir: SerializedIrModule) {
|
||||||
|
irLayout.irDir.mkdirs()
|
||||||
|
|
||||||
with(ir.files.sortedBy { it.path }) {
|
with(ir.files.sortedBy { it.path }) {
|
||||||
IrArrayWriter(map { it.fileData }).writeIntoFile(irLayout.irFiles.absolutePath)
|
IrArrayWriter(map { it.fileData }).writeIntoFile(irLayout.irFiles.absolutePath)
|
||||||
IrArrayWriter(map { it.declarations }).writeIntoFile(irLayout.irDeclarations.absolutePath)
|
IrArrayWriter(map { it.declarations }).writeIntoFile(irLayout.irDeclarations.absolutePath)
|
||||||
@@ -31,8 +29,10 @@ class IrMonoliticWriterImpl(irLayout: IrKotlinLibraryLayout) : IrWriterImpl(irLa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IrPerFileWriterImpl(irLayout: IrKotlinLibraryLayout) : IrWriterImpl(irLayout) {
|
class IrPerFileWriterImpl(_irLayout: IrKotlinLibraryLayout) : IrWriterImpl(_irLayout) {
|
||||||
override fun addIr(ir: SerializedIrModule) {
|
override fun addIr(ir: SerializedIrModule) {
|
||||||
|
irLayout.irDir.mkdirs()
|
||||||
|
|
||||||
ir.files.forEach {
|
ir.files.forEach {
|
||||||
serializeFile(it)
|
serializeFile(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ dependencies {
|
|||||||
testCompile(project(":js:js.serializer"))
|
testCompile(project(":js:js.serializer"))
|
||||||
testCompile(project(":js:js.dce"))
|
testCompile(project(":js:js.dce"))
|
||||||
testCompile(project(":js:js.engines"))
|
testCompile(project(":js:js.engines"))
|
||||||
|
testCompile(project(":compiler:incremental-compilation-impl"))
|
||||||
testCompile(commonDep("junit:junit"))
|
testCompile(commonDep("junit:junit"))
|
||||||
testCompile(projectTests(":kotlin-build-common"))
|
testCompile(projectTests(":kotlin-build-common"))
|
||||||
testCompile(projectTests(":generators:test-generator"))
|
testCompile(projectTests(":generators:test-generator"))
|
||||||
|
|||||||
@@ -0,0 +1,603 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.benchmarks
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
|
import com.intellij.openapi.vfs.VfsUtilCore
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileManager
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||||
|
import org.jetbrains.kotlin.backend.common.LoggingContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||||
|
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
||||||
|
import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion
|
||||||
|
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
||||||
|
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
|
import org.jetbrains.kotlin.config.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.konan.kotlinLibrary
|
||||||
|
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||||
|
import org.jetbrains.kotlin.incremental.EmptyICReporter
|
||||||
|
import org.jetbrains.kotlin.incremental.IncrementalJsCompilerRunner
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||||
|
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
|
||||||
|
import org.jetbrains.kotlin.incremental.withJsIC
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.*
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrModuleSerializer
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
|
||||||
|
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.library.KotlinLibraryVersioning
|
||||||
|
import org.jetbrains.kotlin.library.KotlinAbiVersion
|
||||||
|
import org.jetbrains.kotlin.library.KotlinLibrary
|
||||||
|
import org.jetbrains.kotlin.library.SerializedIrModule
|
||||||
|
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
|
||||||
|
import org.jetbrains.kotlin.library.impl.KotlinLibraryOnlyIrWriter
|
||||||
|
import org.jetbrains.kotlin.library.impl.createKotlinLibrary
|
||||||
|
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
||||||
|
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
||||||
|
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
|
||||||
|
import org.jetbrains.kotlin.psi2ir.generators.createGeneratorContext
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
|
||||||
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Ignore
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
import org.jetbrains.kotlin.konan.file.File as KonanFile
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
class GenerateIrRuntime {
|
||||||
|
private val lookupTracker: LookupTracker = LookupTracker.DO_NOTHING
|
||||||
|
private val logger = object : LoggingContext {
|
||||||
|
override var inVerbosePhase = false
|
||||||
|
override fun log(message: () -> String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadKlib(klibPath: String, isPacked: Boolean) = createKotlinLibrary(KonanFile("$klibPath${if (isPacked) ".klib" else ""}"))
|
||||||
|
|
||||||
|
private fun buildConfiguration(environment: KotlinCoreEnvironment): CompilerConfiguration {
|
||||||
|
val runtimeConfiguration = environment.configuration.copy()
|
||||||
|
runtimeConfiguration.put(CommonConfigurationKeys.MODULE_NAME, "JS_IR_RUNTIME")
|
||||||
|
runtimeConfiguration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.UMD)
|
||||||
|
|
||||||
|
runtimeConfiguration.languageVersionSettings = LanguageVersionSettingsImpl(
|
||||||
|
LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE,
|
||||||
|
specificFeatures = mapOf(
|
||||||
|
LanguageFeature.AllowContractsForCustomFunctions to LanguageFeature.State.ENABLED,
|
||||||
|
LanguageFeature.MultiPlatformProjects to LanguageFeature.State.ENABLED
|
||||||
|
),
|
||||||
|
analysisFlags = mapOf(
|
||||||
|
AnalysisFlags.useExperimental to listOf("kotlin.contracts.ExperimentalContracts", "kotlin.Experimental", "kotlin.ExperimentalMultiplatform"),
|
||||||
|
AnalysisFlags.allowResultReturnType to true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return runtimeConfiguration
|
||||||
|
}
|
||||||
|
private val CompilerConfiguration.metadataVersion
|
||||||
|
get() = get(CommonConfigurationKeys.METADATA_VERSION) as? KlibMetadataVersion ?: KlibMetadataVersion.INSTANCE
|
||||||
|
|
||||||
|
private val environment =
|
||||||
|
KotlinCoreEnvironment.createForTests(Disposable { }, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
|
||||||
|
private val configuration = buildConfiguration(environment)
|
||||||
|
private val project = environment.project
|
||||||
|
private val phaseConfig = PhaseConfig(jsPhases)
|
||||||
|
|
||||||
|
private val metadataVersion = configuration.metadataVersion
|
||||||
|
private val languageVersionSettings = configuration.languageVersionSettings
|
||||||
|
private val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||||
|
|
||||||
|
fun createPsiFile(fileName: String, isCommon: Boolean): KtFile? {
|
||||||
|
val psiManager = PsiManager.getInstance(environment.project)
|
||||||
|
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
|
||||||
|
|
||||||
|
val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName")
|
||||||
|
|
||||||
|
val psiFile = psiManager.findFile(file)
|
||||||
|
|
||||||
|
return (psiFile as? KtFile)?.apply { isCommonSource = isCommon }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun File.listAllFiles(): List<File> {
|
||||||
|
return if (isDirectory) listFiles().flatMap { it.listAllFiles() }
|
||||||
|
else listOf(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createPsiFileFromDir(path: String, vararg extraDirs: String): List<KtFile> {
|
||||||
|
val dir = File(path)
|
||||||
|
val buildPath = File(dir, "build")
|
||||||
|
val commonPath = File(buildPath, "commonMainSources")
|
||||||
|
val extraPaths = extraDirs.map { File(dir, it) }
|
||||||
|
val jsPaths = listOf(File(buildPath, "jsMainSources")) + extraPaths
|
||||||
|
val commonPsis = commonPath.listAllFiles().mapNotNull { createPsiFile(it.path, true) }
|
||||||
|
val jsPsis = jsPaths.flatMap { d -> d.listAllFiles().mapNotNull { createPsiFile(it.path, false) } }
|
||||||
|
return commonPsis + jsPsis
|
||||||
|
}
|
||||||
|
|
||||||
|
private val fullRuntimeSourceSet = createPsiFileFromDir("libraries/stdlib/js-ir", "builtins", "runtime", "src")
|
||||||
|
private val reducedRuntimeSourceSet = createPsiFileFromDir("libraries/stdlib/js-ir-minimal-for-test", "src")
|
||||||
|
|
||||||
|
private lateinit var workingDir: File
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
workingDir = FileUtil.createTempDirectory("irTest", null, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
workingDir.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runFullPipeline() {
|
||||||
|
runBenchWithWarmup("Full pipeline", 5, 2, MeasureUnits.MICROSECONDS, pre = System::gc) {
|
||||||
|
compile(fullRuntimeSourceSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runWithoutFrontEnd() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Pipeline withput FrontEnd", 40, 10, MeasureUnits.MICROSECONDS, pre = System::gc) {
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
|
||||||
|
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
|
||||||
|
|
||||||
|
val (module, symbolTable, irBuiltIns, linker) = doDeserializeModule(modulePath)
|
||||||
|
|
||||||
|
val jsProgram = doBackEnd(module, symbolTable, irBuiltIns, linker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runPsi2Ir() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Psi2Ir phase", 40, 10, MeasureUnits.MICROSECONDS, pre = System::gc) {
|
||||||
|
doPsi2Ir(files, analysisResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runSerialization() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Ir Serialization", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
|
||||||
|
doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class MeasureUnits(val delimeter: Long, private val suffix: String) {
|
||||||
|
NANOSECONDS(1L, "ns"),
|
||||||
|
MICROSECONDS(1000L, "mcs"),
|
||||||
|
MILLISECONDS(1000L * 1000L, "ms");
|
||||||
|
|
||||||
|
fun convert(nanos: Long): String = "${(nanos / delimeter)}$suffix"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runIrDeserializationMonolitic() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files, false)
|
||||||
|
val moduleRef = loadKlib(modulePath, isPacked = false)
|
||||||
|
val moduleDescriptor = doDeserializeModuleMetadata(moduleRef)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Ir Deserialization Monolithic", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
|
||||||
|
doDeserializeIrModule(moduleDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runIrDeserializationPerFile() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files, true)
|
||||||
|
val moduleRef = loadKlib(modulePath, isPacked = false)
|
||||||
|
val moduleDescriptor = doDeserializeModuleMetadata(moduleRef)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Ir Deserialization Per-File", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
|
||||||
|
doDeserializeIrModule(moduleDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runIrSerialization() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Ir Serialization", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
|
||||||
|
doSerializeIrModule(rawModuleFragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runMonoliticDiskWriting() {
|
||||||
|
val libraryVersion = "JSIR"
|
||||||
|
val compilerVersion = KotlinCompilerVersion.getVersion()
|
||||||
|
val abiVersion = KotlinAbiVersion.CURRENT
|
||||||
|
val metadataVersion = KlibMetadataVersion.INSTANCE.toString()
|
||||||
|
val irVersion = KlibIrVersion.INSTANCE.toString()
|
||||||
|
|
||||||
|
val versions = KotlinLibraryVersioning(libraryVersion, compilerVersion, abiVersion, metadataVersion, irVersion)
|
||||||
|
val file = createTempFile(directory = workingDir)
|
||||||
|
val writer = KotlinLibraryOnlyIrWriter(file.absolutePath, "", versions, BuiltInsPlatform.JS, emptyList(), false)
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
val fileCount = rawModuleFragment.files.size
|
||||||
|
val serializedIr = doSerializeIrModule(rawModuleFragment)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Monolitic Disk Writing of $fileCount files", 10, 30, MeasureUnits.MILLISECONDS, pre = writer::invalidate) {
|
||||||
|
doWriteIrModuleToStorage(serializedIr, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runPerfileDiskWriting() {
|
||||||
|
val libraryVersion = "JSIR"
|
||||||
|
val compilerVersion = KotlinCompilerVersion.getVersion()
|
||||||
|
val abiVersion = KotlinAbiVersion.CURRENT
|
||||||
|
val metadataVersion = KlibMetadataVersion.INSTANCE.toString()
|
||||||
|
val irVersion = KlibIrVersion.INSTANCE.toString()
|
||||||
|
|
||||||
|
val versions = KotlinLibraryVersioning(libraryVersion, compilerVersion, abiVersion, metadataVersion, irVersion)
|
||||||
|
val file = createTempFile(directory = workingDir)
|
||||||
|
val writer = KotlinLibraryOnlyIrWriter(file.absolutePath, "", versions, BuiltInsPlatform.JS, emptyList(), true)
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
val fileCount = rawModuleFragment.files.size
|
||||||
|
val serializedIr = doSerializeIrModule(rawModuleFragment)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Per-file Disk Writing of $fileCount files", 10, 30, MeasureUnits.MILLISECONDS, pre = writer::invalidate) {
|
||||||
|
doWriteIrModuleToStorage(serializedIr, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runIncrementalKlibGeneratation() {
|
||||||
|
|
||||||
|
val klibDirectory = workingDir.resolve("output/klib")
|
||||||
|
|
||||||
|
val filesToCompile = fullRuntimeSourceSet
|
||||||
|
// val filesToCompile = reducedRuntimeSourceSet
|
||||||
|
|
||||||
|
val args = K2JSCompilerArguments().apply {
|
||||||
|
libraries = ""
|
||||||
|
outputFile = klibDirectory.path
|
||||||
|
sourceMap = false
|
||||||
|
irProduceKlibDir = true
|
||||||
|
irOnly = true
|
||||||
|
irModuleName = "kotlin"
|
||||||
|
allowKotlinPackage = true
|
||||||
|
useExperimental = arrayOf("kotlin.contracts.ExperimentalContracts", "kotlin.Experimental", "kotlin.ExperimentalMultiplatform")
|
||||||
|
allowResultReturnType = true
|
||||||
|
multiPlatform = true
|
||||||
|
languageVersion = "1.4"
|
||||||
|
commonSources = filesToCompile.filter { it.isCommonSource == true }.map { it.virtualFilePath }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
val cachesDir = workingDir.resolve("caches")
|
||||||
|
val allFiles = filesToCompile.map { VfsUtilCore.virtualToIoFile(it.virtualFile) }
|
||||||
|
val dirtyFiles = allFiles.filter { it.name.contains("coreRuntime") }
|
||||||
|
|
||||||
|
val cleanBuildStart = System.nanoTime()
|
||||||
|
|
||||||
|
withJsIC {
|
||||||
|
val buildHistoryFile = File(cachesDir, "build-history.bin")
|
||||||
|
val compiler = IncrementalJsCompilerRunner(
|
||||||
|
cachesDir, EmptyICReporter,
|
||||||
|
buildHistoryFile = buildHistoryFile,
|
||||||
|
modulesApiHistory = EmptyModulesApiHistory
|
||||||
|
)
|
||||||
|
compiler.compile(allFiles, args, MessageCollector.NONE, providedChangedFiles = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cleanBuildTime = System.nanoTime() - cleanBuildStart
|
||||||
|
|
||||||
|
println("[Cold] Clean build of ${allFiles.size} takes ${MeasureUnits.MILLISECONDS.convert(cleanBuildTime)}")
|
||||||
|
|
||||||
|
var index = -1
|
||||||
|
val wmpDone = { index = 0 }
|
||||||
|
|
||||||
|
val elist = emptyList<File>()
|
||||||
|
var changedFiles = ChangedFiles.Known(dirtyFiles, elist)
|
||||||
|
|
||||||
|
val update = {
|
||||||
|
changedFiles = if (index < 0) changedFiles else ChangedFiles.Known(listOf(allFiles[index++]), elist)
|
||||||
|
System.gc()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompileTimeResult(val file: String, val time: Long)
|
||||||
|
|
||||||
|
var maxResult = CompileTimeResult("", -1)
|
||||||
|
var minResult = CompileTimeResult("", Long.MAX_VALUE)
|
||||||
|
|
||||||
|
val done = { t: Long ->
|
||||||
|
if (maxResult.time < t) maxResult = CompileTimeResult(changedFiles.modified[0].path, t)
|
||||||
|
if (minResult.time > t) minResult = CompileTimeResult(changedFiles.modified[0].path, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
runBenchWithWarmup(
|
||||||
|
"Incremental recompilation of ${dirtyFiles.count()} files",
|
||||||
|
200,
|
||||||
|
allFiles.size,
|
||||||
|
MeasureUnits.MILLISECONDS,
|
||||||
|
wmpDone,
|
||||||
|
done,
|
||||||
|
update
|
||||||
|
) {
|
||||||
|
withJsIC {
|
||||||
|
val buildHistoryFile = File(cachesDir, "build-history.bin")
|
||||||
|
val compiler = IncrementalJsCompilerRunner(
|
||||||
|
cachesDir, EmptyICReporter,
|
||||||
|
buildHistoryFile = buildHistoryFile,
|
||||||
|
modulesApiHistory = EmptyModulesApiHistory
|
||||||
|
)
|
||||||
|
compiler.compile(allFiles, args, MessageCollector.NONE, changedFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Longest re-compilation takes ${MeasureUnits.MILLISECONDS.convert(maxResult.time)} (${maxResult.file})")
|
||||||
|
println("Fastest re-compilation takes ${MeasureUnits.MILLISECONDS.convert(minResult.time)} (${minResult.file})")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runBenchWithWarmup(name: String, W: Int, N: Int, measurer: MeasureUnits, wmpDone: () -> Unit = {}, bnhDone: (Long) -> Unit = {}, pre: () -> Unit = {}, bench: () -> Unit) {
|
||||||
|
|
||||||
|
println("Run $name benchmark")
|
||||||
|
|
||||||
|
println("Warmup: $W times...")
|
||||||
|
|
||||||
|
repeat(W) {
|
||||||
|
println("W: ${it + 1} out of $W")
|
||||||
|
pre()
|
||||||
|
bench()
|
||||||
|
}
|
||||||
|
|
||||||
|
var total = 0L
|
||||||
|
|
||||||
|
wmpDone()
|
||||||
|
|
||||||
|
println("Run bench: $N times...")
|
||||||
|
|
||||||
|
repeat(N) {
|
||||||
|
print("R: ${it + 1} out of $N ")
|
||||||
|
pre()
|
||||||
|
val start = System.nanoTime()
|
||||||
|
bench()
|
||||||
|
val iter = System.nanoTime() - start
|
||||||
|
println("takes ${measurer.convert(iter)}")
|
||||||
|
bnhDone(iter)
|
||||||
|
total += iter
|
||||||
|
}
|
||||||
|
|
||||||
|
println("$name takes ${measurer.convert(total / N)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runDeserialization() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
|
||||||
|
|
||||||
|
repeat(20) {
|
||||||
|
doDeserializeModule(modulePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun runDeserializationAndBackend() {
|
||||||
|
val files = fullRuntimeSourceSet
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
|
||||||
|
|
||||||
|
runBenchWithWarmup("Deserializtion and Backend", 40, 10, MeasureUnits.MICROSECONDS, pre = System::gc) {
|
||||||
|
val (module, symbolTable, irBuiltIns, linker) = doDeserializeModule(modulePath)
|
||||||
|
doBackEnd(module, symbolTable, irBuiltIns, linker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doFrontEnd(files: List<KtFile>): AnalysisResult {
|
||||||
|
val analysisResult =
|
||||||
|
TopDownAnalyzerFacadeForJS.analyzeFiles(
|
||||||
|
files,
|
||||||
|
project,
|
||||||
|
configuration,
|
||||||
|
emptyList(),
|
||||||
|
friendModuleDescriptors = emptyList(),
|
||||||
|
thisIsBuiltInsModule = true,
|
||||||
|
customBuiltInsModule = null
|
||||||
|
)
|
||||||
|
|
||||||
|
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||||
|
TopDownAnalyzerFacadeForJS.checkForErrors(files, analysisResult.bindingContext)
|
||||||
|
|
||||||
|
return analysisResult
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doPsi2Ir(files: List<KtFile>, analysisResult: AnalysisResult): IrModuleFragment {
|
||||||
|
val psi2IrConfiguration = Psi2IrConfiguration()
|
||||||
|
val mangler = JsManglerDesc
|
||||||
|
val signaturer = IdSignatureDescriptor(mangler)
|
||||||
|
val symbolTable = SymbolTable(signaturer)
|
||||||
|
val generatorExtensions = GeneratorExtensions()
|
||||||
|
val psi2IrContext = createGeneratorContext(
|
||||||
|
psi2IrConfiguration,
|
||||||
|
analysisResult.moduleDescriptor,
|
||||||
|
analysisResult.bindingContext,
|
||||||
|
languageVersionSettings,
|
||||||
|
symbolTable,
|
||||||
|
generatorExtensions
|
||||||
|
)
|
||||||
|
|
||||||
|
val irBuiltIns = psi2IrContext.irBuiltIns
|
||||||
|
val functionFactory = IrFunctionFactory(irBuiltIns, psi2IrContext.symbolTable)
|
||||||
|
irBuiltIns.functionFactory = functionFactory
|
||||||
|
|
||||||
|
val irLinker = JsIrLinker(
|
||||||
|
psi2IrContext.moduleDescriptor,
|
||||||
|
emptyLoggingContext,
|
||||||
|
psi2IrContext.irBuiltIns,
|
||||||
|
psi2IrContext.symbolTable,
|
||||||
|
functionFactory,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
val irProviders = listOf(irLinker)
|
||||||
|
|
||||||
|
val psi2IrTranslator = Psi2IrTranslator(languageVersionSettings, psi2IrContext.configuration, signaturer)
|
||||||
|
return psi2IrTranslator.generateModuleFragment(psi2IrContext, files, irProviders, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doSerializeModule(moduleFragment: IrModuleFragment, bindingContext: BindingContext, files: List<KtFile>, perFile: Boolean = false): String {
|
||||||
|
val tmpKlibDir = createTempDir().also { it.deleteOnExit() }
|
||||||
|
serializeModuleIntoKlib(
|
||||||
|
moduleName,
|
||||||
|
configuration,
|
||||||
|
bindingContext,
|
||||||
|
files,
|
||||||
|
tmpKlibDir.path,
|
||||||
|
emptyList(),
|
||||||
|
moduleFragment,
|
||||||
|
mutableMapOf(),
|
||||||
|
emptyList(),
|
||||||
|
true,
|
||||||
|
perFile
|
||||||
|
)
|
||||||
|
|
||||||
|
return tmpKlibDir.path
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doDeserializeModuleMetadata(moduleRef: KotlinLibrary): ModuleDescriptorImpl {
|
||||||
|
return getModuleDescriptorByLibrary(moduleRef, emptyMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class DeserializedModuleInfo(val module: IrModuleFragment, val symbolTable: SymbolTable, val irBuiltIns: IrBuiltIns, val linker: JsIrLinker)
|
||||||
|
|
||||||
|
|
||||||
|
private fun doSerializeIrModule(module: IrModuleFragment): SerializedIrModule {
|
||||||
|
val serializedIr = JsIrModuleSerializer(logger, module.irBuiltins, mutableMapOf(), true).serializedIrModule(module)
|
||||||
|
return serializedIr
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doWriteIrModuleToStorage(serializedIrModule: SerializedIrModule, writer: KotlinLibraryOnlyIrWriter) {
|
||||||
|
writer.writeIr(serializedIrModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doDeserializeIrModule(moduleDescriptor: ModuleDescriptorImpl): DeserializedModuleInfo {
|
||||||
|
val mangler = JsManglerDesc
|
||||||
|
val signaturer = IdSignatureDescriptor(mangler)
|
||||||
|
val symbolTable = SymbolTable(signaturer)
|
||||||
|
val typeTranslator = TypeTranslator(symbolTable, languageVersionSettings, moduleDescriptor.builtIns).also {
|
||||||
|
it.constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
val irBuiltIns = IrBuiltIns(moduleDescriptor.builtIns, typeTranslator, symbolTable)
|
||||||
|
val functionFactory = IrFunctionFactory(irBuiltIns, symbolTable)
|
||||||
|
irBuiltIns.functionFactory = functionFactory
|
||||||
|
|
||||||
|
val jsLinker = JsIrLinker(moduleDescriptor, logger, irBuiltIns, symbolTable, functionFactory, null)
|
||||||
|
|
||||||
|
val moduleFragment = jsLinker.deserializeFullModule(moduleDescriptor, moduleDescriptor.kotlinLibrary)
|
||||||
|
jsLinker.init(null)
|
||||||
|
// Create stubs
|
||||||
|
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker), languageVersionSettings)
|
||||||
|
.generateUnboundSymbolsAsDependencies()
|
||||||
|
|
||||||
|
jsLinker.postProcess()
|
||||||
|
|
||||||
|
moduleFragment.patchDeclarationParents()
|
||||||
|
|
||||||
|
return DeserializedModuleInfo(moduleFragment, symbolTable, irBuiltIns, jsLinker)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doDeserializeModule(modulePath: String): DeserializedModuleInfo {
|
||||||
|
val moduleRef = loadKlib(modulePath, false)
|
||||||
|
val moduleDescriptor = doDeserializeModuleMetadata(moduleRef)
|
||||||
|
val mangler = JsManglerDesc
|
||||||
|
val signaturer = IdSignatureDescriptor(mangler)
|
||||||
|
val symbolTable = SymbolTable(signaturer)
|
||||||
|
val typeTranslator = TypeTranslator(symbolTable, languageVersionSettings, moduleDescriptor.builtIns).also {
|
||||||
|
it.constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
val irBuiltIns = IrBuiltIns(moduleDescriptor.builtIns, typeTranslator, symbolTable)
|
||||||
|
|
||||||
|
val functionFactory = IrFunctionFactory(irBuiltIns, symbolTable)
|
||||||
|
irBuiltIns.functionFactory = functionFactory
|
||||||
|
|
||||||
|
val jsLinker = JsIrLinker(moduleDescriptor, logger, irBuiltIns, symbolTable, functionFactory, null)
|
||||||
|
|
||||||
|
val moduleFragment = jsLinker.deserializeFullModule(moduleDescriptor, moduleDescriptor.kotlinLibrary)
|
||||||
|
// Create stubs
|
||||||
|
jsLinker.init(null)
|
||||||
|
// Create stubs
|
||||||
|
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker), languageVersionSettings)
|
||||||
|
.generateUnboundSymbolsAsDependencies()
|
||||||
|
|
||||||
|
jsLinker.postProcess()
|
||||||
|
|
||||||
|
moduleFragment.patchDeclarationParents()
|
||||||
|
|
||||||
|
return DeserializedModuleInfo(moduleFragment, symbolTable, irBuiltIns, jsLinker)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun doBackEnd(module: IrModuleFragment, symbolTable: SymbolTable, irBuiltIns: IrBuiltIns, jsLinker: JsIrLinker): CompilerResult {
|
||||||
|
val context = JsIrBackendContext(module.descriptor, irBuiltIns, symbolTable, module, emptySet(), configuration)
|
||||||
|
|
||||||
|
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker), languageVersionSettings).generateUnboundSymbolsAsDependencies()
|
||||||
|
|
||||||
|
jsPhases.invokeToplevel(phaseConfig, context, module)
|
||||||
|
|
||||||
|
val transformer = IrModuleToJsTransformer(context, null, null)
|
||||||
|
|
||||||
|
return transformer.generateModule(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun compile(files: List<KtFile>): String {
|
||||||
|
val analysisResult = doFrontEnd(files)
|
||||||
|
|
||||||
|
val rawModuleFragment = doPsi2Ir(files, analysisResult)
|
||||||
|
|
||||||
|
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
|
||||||
|
|
||||||
|
val (module, symbolTable, irBuiltIns, linker) = doDeserializeModule(modulePath)
|
||||||
|
|
||||||
|
val jsProgram = doBackEnd(module, symbolTable, irBuiltIns, linker)
|
||||||
|
|
||||||
|
return jsProgram.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user