diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/performanceMeasurements.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/performanceMeasurements.kt index 069635229c2..75a0e51b5be 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/performanceMeasurements.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/performanceMeasurements.kt @@ -20,13 +20,13 @@ class CompilerInitializationMeasurement(private val milliseconds: Long) : Perfor } -class CodeAnalysisMeasurement(private val files: Int, val lines: Int, private val milliseconds: Long, private val description: String?) : +class CodeAnalysisMeasurement(val files: Int, val lines: Int, val milliseconds: Long, private val description: String?) : PerformanceMeasurement { - private val speed: Double = lines.toDouble() * 1000 / milliseconds + val lps: Double = lines.toDouble() * 1000 / milliseconds override fun render(): String = - "ANALYZE: $files files ($lines lines) ${description ?: ""}in $milliseconds ms - ${"%.3f".format(speed)} loc/s" + "ANALYZE: $files files ($lines lines) ${description ?: ""}in $milliseconds ms - ${"%.3f".format(lps)} loc/s" } @@ -40,8 +40,8 @@ class CodeGenerationMeasurement(private val files: Int, val lines: Int, private } -class GarbageCollectionMeasurement(private val garbageCollectionKind: String, private val milliseconds: Long) : PerformanceMeasurement { - override fun render(): String = "GC time for $garbageCollectionKind is $milliseconds ms" +class GarbageCollectionMeasurement(val garbageCollectionKind: String, val milliseconds: Long, val count: Long) : PerformanceMeasurement { + override fun render(): String = "GC time for $garbageCollectionKind is $milliseconds ms, $count collections" } diff --git a/compiler/fir/modularized-tests/tests/org/jetbrains/kotlin/fir/FullPipelineModularizedTest.kt b/compiler/fir/modularized-tests/tests/org/jetbrains/kotlin/fir/FullPipelineModularizedTest.kt new file mode 100644 index 00000000000..abf52d3eb17 --- /dev/null +++ b/compiler/fir/modularized-tests/tests/org/jetbrains/kotlin/fir/FullPipelineModularizedTest.kt @@ -0,0 +1,188 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.fir + +import com.intellij.util.io.delete +import org.jetbrains.kotlin.cli.common.* +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler +import org.jetbrains.kotlin.config.Services +import org.jetbrains.kotlin.fir.TableTimeUnit.MS +import org.jetbrains.kotlin.fir.TableTimeUnit.S +import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.util.PerformanceCounter +import java.io.FileOutputStream +import java.io.PrintStream +import java.nio.file.Files + +class FullPipelineModularizedTest : AbstractModularizedTest() { + override fun beforePass() { + totalPassResult = CumulativeTime() + totalModules = 0 + okModules = 0 + } + + private data class CumulativeTime( + val gcInfo: Map, + val analysis: Long, + val translation: Long, + val generation: Long, + val files: Int, + val lines: Int + ) { + constructor() : this(emptyMap(), 0, 0, 0, 0, 0) + + operator fun plus(other: CumulativeTime): CumulativeTime { + return CumulativeTime( + (gcInfo.values + other.gcInfo.values).groupingBy { it.name }.reduce { key, accumulator, element -> + GCInfo(key, accumulator.gcTime + element.gcTime, accumulator.collections + element.collections) + }, + analysis + other.analysis, + translation + other.translation, + generation + other.generation, + files + other.files, + lines + other.lines + ) + } + + fun totalTime() = analysis + translation + generation + } + + private lateinit var totalPassResult: CumulativeTime + private var totalModules = 0 + private var okModules = 0 + + override fun afterPass(pass: Int) { + createReport() + } + + private fun createReport() { + formatReport(System.out) + + PrintStream( + FileOutputStream( + reportDir().resolve("report-$reportDateStr.log"), + true + ) + ).use { stream -> + formatReport(stream) + stream.println() + stream.println() + } + } + + private fun formatReport(stream: PrintStream) { + stream.println("TOTAL MODULES: $totalModules") + stream.println("OK MODULES: $okModules") + val total = totalPassResult + var totalGcTimeMs = 0L + var totalGcCount = 0L + printTable(stream) { + row("Name", "Time", "Count") + separator() + fun gcRow(name: String, timeMs: Long, count: Long) { + row { + cell(name, align = LEFT) + timeCell(timeMs, inputUnit = MS) + cell(count.toString()) + } + } + for (measurement in total.gcInfo.values) { + totalGcTimeMs += measurement.gcTime + totalGcCount += measurement.collections + gcRow(measurement.name, measurement.gcTime, measurement.collections) + } + separator() + gcRow("Total", totalGcTimeMs, totalGcCount) + + } + + printTable(stream) { + row("Phase", "Time", "Files", "L/S") + separator() + + fun phase(name: String, timeMs: Long, files: Int, lines: Int) { + row { + cell(name, align = LEFT) + timeCell(timeMs, inputUnit = MS) + cell(files.toString()) + linePerSecondCell(lines, timeMs, timeUnit = MS) + } + } + phase("Analysis", total.analysis, total.files, total.lines) + phase("Translation", total.translation, total.files, total.lines) + phase("Generation", total.generation, total.files, total.lines) + + separator() + phase("Total", total.totalTime(), total.files, total.lines) + } + } + + override fun processModule(moduleData: ModuleData): ProcessorAction { + val compiler = K2JVMCompiler() + val args = compiler.createArguments() + args.reportPerf = true + args.jvmTarget = "1.8" + args.useFir = true + args.classpath = moduleData.classpath.joinToString(separator = ":") { it.absolutePath } + args.javaSourceRoots = moduleData.javaSourceRoots.map { it.absolutePath }.toTypedArray() + args.allowKotlinPackage = true + args.freeArgs = moduleData.sources.map { it.absolutePath } + val tmp = Files.createTempDirectory("compile-output") + args.destination = tmp.toAbsolutePath().toFile().toString() + val manager = CompilerPerformanceManager() + val services = Services.Builder().register(CommonCompilerPerformanceManager::class.java, manager).build() + val result = try { + compiler.exec(PrintingMessageCollector(System.out, MessageRenderer.GRADLE_STYLE, false), services, args) + } catch (e: Exception) { + e.printStackTrace() + ExitCode.INTERNAL_ERROR + } + val resultTime = manager.reportCumulativeTime() + + PerformanceCounter.resetAllCounters() + + tmp.delete(recursively = true) + totalModules++ + + return when (result) { + ExitCode.OK -> { + totalPassResult += resultTime + okModules++ + ProcessorAction.NEXT + } + else -> ProcessorAction.NEXT + } + } + + + private inner class CompilerPerformanceManager : CommonCompilerPerformanceManager("Modularized test performance manager") { + fun reportCumulativeTime(): CumulativeTime { + val gcInfo = measurements.filterIsInstance() + .associate { it.garbageCollectionKind to GCInfo(it.garbageCollectionKind, it.milliseconds, it.count) } + + val analysisMeasurement = measurements.filterIsInstance().firstOrNull() + val irMeasurements = measurements.filterIsInstance() + + return CumulativeTime( + gcInfo, + analysisMeasurement?.milliseconds ?: 0, + irMeasurements.firstOrNull { it.kind == IRMeasurement.Kind.Translation }?.milliseconds ?: 0, + irMeasurements.firstOrNull { it.kind == IRMeasurement.Kind.Generation }?.milliseconds ?: 0, + analysisMeasurement?.files ?: 0, + analysisMeasurement?.lines ?: 0 + ) + } + } + + fun testTotalKotlin() { + for (i in 0 until PASSES) { + println("Pass $i") + runTestOnce(i) + } + } +} \ No newline at end of file diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt index 16a40c0b33a..014bf5b123a 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/FirResolveBench.kt @@ -452,15 +452,23 @@ private fun RTableContext.printMeasureAsTable(measure: FirResolveBench.Measure, timeCell(measure.gcTime, inputUnit = TableTimeUnit.MS) cell(measure.gcCollections.toString()) - run { - val linePerSec = statistics.totalLines / TableTimeUnit.S.convert(time, TableTimeUnit.NS) - val df = DecimalFormat().apply { - maximumFractionDigits = 1 - isGroupingUsed = true - } - cell(df.format(linePerSec)) - } + linePerSecondCell(statistics.totalLines, time, timeUnit = TableTimeUnit.NS) } } + + +fun RTableContext.RTableRowContext.linePerSecondCell(linePerSec: Double) { + val df = DecimalFormat().apply { + maximumFractionDigits = 1 + isGroupingUsed = true + } + cell(df.format(linePerSec)) +} +fun RTableContext.RTableRowContext.linePerSecondCell(lines: Int, time: Long, timeUnit: TableTimeUnit = TableTimeUnit.NS) { + val linePerSec = lines / TableTimeUnit.S.convert(time, from = timeUnit) + linePerSecondCell(linePerSec) +} + + class FirRuntimeException(override val message: String, override val cause: Throwable) : RuntimeException(message, cause) \ No newline at end of file diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/TableRendering.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/TableRendering.kt index ae74bab52df..8d7f9ed6d61 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/TableRendering.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/TableRendering.kt @@ -120,7 +120,7 @@ class RTableContext { } -internal enum class TableTimeUnit(val postfixText: String, val nsMultiplier: Double, val fractionDigits: Int) { +enum class TableTimeUnit(val postfixText: String, val nsMultiplier: Double, val fractionDigits: Int) { NS("ns", 1.0, 0), MICS("mcs", 1e-3, 3), MS("ms", 1e-6, 6), @@ -131,7 +131,16 @@ internal enum class TableTimeUnit(val postfixText: String, val nsMultiplier: Dou } } -internal inline fun RTableContext.RTableRowContext.timeCell( +inline class TableTimeUnitConversion(val value: Double) { + infix fun to(dest: TableTimeUnit): Double { + return value * dest.nsMultiplier + } +} + +infix fun Long.from(from: TableTimeUnit) = TableTimeUnitConversion(this / from.nsMultiplier) + + +inline fun RTableContext.RTableRowContext.timeCell( time: Long, outputUnit: TableTimeUnit = TableTimeUnit.MS, inputUnit: TableTimeUnit = TableTimeUnit.NS, @@ -144,6 +153,6 @@ internal inline fun RTableContext.RTableRowContext.timeCell( cell("${df.format(outputUnit.convert(time, inputUnit))} ${outputUnit.postfixText}") } -internal inline fun printTable(out: Appendable = System.out, body: RTableContext.() -> Unit) { +inline fun printTable(out: Appendable = System.out, body: RTableContext.() -> Unit) { RTableContext().apply(body).printout(out) } \ No newline at end of file