Kotlin/Native performance gradle plugin (#2713)
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.gradle.plugin.performance
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.execution.TaskExecutionListener
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import org.gradle.api.tasks.TaskState
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.addExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.NativePerformanceReport
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import java.io.File
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class TaskTime(val startTime: Long) {
|
||||
var duration: Double? = null
|
||||
}
|
||||
|
||||
class TaskTimerListener(project: Project) : TaskExecutionListener {
|
||||
val tasksTimes = getOrRegisterStorage<String, TaskTime>(project, "org.jetbrains.kotlin.native.taskTimes")
|
||||
fun getTime(taskName: String) = tasksTimes[taskName]?.duration ?: 0.0
|
||||
|
||||
override fun beforeExecute(task: Task) {
|
||||
tasksTimes[task.path] = TaskTime(System.nanoTime())
|
||||
}
|
||||
|
||||
override fun afterExecute(task: Task, taskState: TaskState) {
|
||||
val taskTime = tasksTimes.getValue(task.path)
|
||||
taskTime.duration = (System.nanoTime() - taskTime.startTime) / 1000.0
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun <K, V> getOrRegisterStorage(project: Project, propertyName: String): ConcurrentHashMap<K, V> =
|
||||
project.rootProject.extensions.getByType(ExtraPropertiesExtension::class.java).run {
|
||||
if (!has(propertyName)) {
|
||||
set(propertyName, ConcurrentHashMap<K, V>())
|
||||
}
|
||||
get(propertyName)
|
||||
} as ConcurrentHashMap<K, V>
|
||||
}
|
||||
}
|
||||
|
||||
open class KotlinPerformancePlugin : Plugin<Project> {
|
||||
private fun checkSettings(project: Project, performanceExtension: PerformanceExtension): Boolean {
|
||||
var result = true
|
||||
if (performanceExtension.metrics.isEmpty()) {
|
||||
project.logger.warn("There is no tracked metrics. Please, provide metrics in settings of $EXTENSION_NAME plugin.")
|
||||
result = false
|
||||
}
|
||||
// Find binaries with trackable performance.
|
||||
if (performanceExtension.trackedBinaries.isEmpty()) {
|
||||
project.logger.warn("There is no tracked binaries. Please, provide binaries in settings of $EXTENSION_NAME plugin.")
|
||||
result = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun configureTasks(project: Project, performanceExtension: PerformanceExtension) {
|
||||
// Add time listener.
|
||||
val timeListener = TaskTimerListener(project)
|
||||
project.gradle.addListener(timeListener)
|
||||
performanceExtension.trackedBinaries.forEach { binary ->
|
||||
project.tasks.create(
|
||||
binary.target.disambiguateName(lowerCamelCaseName("perfReport", binary.name)),
|
||||
NativePerformanceReport::class.java
|
||||
) {
|
||||
it.binary = binary
|
||||
it.settings = performanceExtension
|
||||
it.timeListener = timeListener
|
||||
it.group = TASK_GROUP
|
||||
it.description = "Report performance measurement results for binary '${binary.name}' of target '${binary.target.name}'."
|
||||
binary.linkTask.finalizedBy(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun apply(project: Project): Unit = with(project) {
|
||||
pluginManager.withPlugin("kotlin-multiplatform") {
|
||||
val kotlinExtension = project.multiplatformExtension
|
||||
val performanceExtension = PerformanceExtension(this)
|
||||
|
||||
kotlinExtension.addExtension(EXTENSION_NAME, performanceExtension)
|
||||
afterEvaluate {
|
||||
if (checkSettings(project, performanceExtension)) {
|
||||
configureTasks(project, performanceExtension)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTENSION_NAME = "performance"
|
||||
const val TASK_GROUP = "Kotlin/Native Performance"
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.gradle.plugin.performance
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary
|
||||
|
||||
enum class TrackableMetric { COMPILE_TIME, CODE_SIZE }
|
||||
open class PerformanceExtension(private val project: Project) {
|
||||
val version: String
|
||||
get() = project.version.toString()
|
||||
|
||||
var metrics = listOf(TrackableMetric.COMPILE_TIME, TrackableMetric.CODE_SIZE)
|
||||
|
||||
val trackedBinaries = mutableListOf<NativeBinary>()
|
||||
|
||||
val binaryNamesForReport = mutableMapOf<NativeBinary, String>()
|
||||
|
||||
var includeAssociatedTasks = true
|
||||
|
||||
@JvmOverloads
|
||||
fun binary(binary: NativeBinary, nameForReport: String = binary.name) {
|
||||
trackedBinaries.add(binary)
|
||||
binaryNamesForReport[binary] = nameForReport
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.gradle.targets.native.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary
|
||||
import org.jetbrains.kotlin.gradle.plugin.performance.PerformanceExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.performance.TaskTimerListener
|
||||
import org.jetbrains.kotlin.gradle.plugin.performance.TrackableMetric
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* The task generates performance report for Kotlin/Native binary.
|
||||
*/
|
||||
open class NativePerformanceReport : DefaultTask() {
|
||||
@Internal
|
||||
lateinit var binary: NativeBinary
|
||||
|
||||
@Internal
|
||||
lateinit var timeListener: TaskTimerListener
|
||||
|
||||
@Internal
|
||||
val reportDirectory = File(project.buildDir, "perfReports")
|
||||
|
||||
@OutputFile
|
||||
val outputFile = File(reportDirectory, "${name}.txt")
|
||||
|
||||
@Internal
|
||||
lateinit var settings: PerformanceExtension
|
||||
|
||||
private fun getCompilationResults(tasksPaths: Iterable<String>, success: Boolean): String {
|
||||
val status = success && tasksPaths.all { timeListener.tasksTimes.containsKey(it) }
|
||||
val time = tasksPaths.map { timeListener.getTime(it) }.sum()
|
||||
return "${if (status) "PASSED" else "FAILED"}\nCOMPILE_TIME $time"
|
||||
}
|
||||
|
||||
// Get compile task and associated with it other compile tasks.
|
||||
private fun getAllExecutedTasks(compilation: KotlinCompilation<*>): List<Task> {
|
||||
val tasks = mutableListOf(compilation.compileKotlinTask as Task)
|
||||
compilation.associateWith.forEach {
|
||||
tasks += getAllExecutedTasks(it)
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun generate() {
|
||||
val compileTasks = if (settings.includeAssociatedTasks)
|
||||
getAllExecutedTasks(binary.linkTask.compilation)
|
||||
else
|
||||
listOf(binary.linkTask.compilation.compileKotlinTask)
|
||||
val allExecutedTasks = listOf(binary.linkTask) + compileTasks
|
||||
val upToDateTasks = allExecutedTasks.filter { it.state.upToDate }.map { it.name }
|
||||
if (upToDateTasks.isNotEmpty()) {
|
||||
if (outputFile.exists()) {
|
||||
project.delete(outputFile.absolutePath)
|
||||
}
|
||||
project.logger.warn("Next compile tasks which are needed for time measurement are upToDate" +
|
||||
" and weren't executed:\n${upToDateTasks.joinToString("\n", "- ")}")
|
||||
return
|
||||
}
|
||||
val successStatus = allExecutedTasks.all { it.state.failure == null }
|
||||
// Get code size metric.
|
||||
var codeSize: String? = null
|
||||
if (TrackableMetric.CODE_SIZE in settings.metrics) {
|
||||
codeSize = binary.outputFile.let {
|
||||
if (it.exists()) "CODE_SIZE ${it.length()}" else null
|
||||
}
|
||||
}
|
||||
// Get compile time.
|
||||
var compileTime: String? = null
|
||||
if (TrackableMetric.COMPILE_TIME in settings.metrics) {
|
||||
compileTime = getCompilationResults(
|
||||
allExecutedTasks.map { it.path },
|
||||
successStatus
|
||||
)
|
||||
}
|
||||
|
||||
// Create report.
|
||||
if (!reportDirectory.exists()) {
|
||||
project.mkdir(reportDirectory.absolutePath)
|
||||
}
|
||||
val name = settings.binaryNamesForReport[binary]!!
|
||||
outputFile.writeText(name)
|
||||
if (compileTime != null) {
|
||||
outputFile.appendText("\n$compileTime")
|
||||
}
|
||||
if (codeSize != null) {
|
||||
outputFile.appendText("\n$codeSize")
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
implementation-class=org.jetbrains.kotlin.gradle.plugin.performance.KotlinPerformancePlugin
|
||||
+1
@@ -0,0 +1 @@
|
||||
implementation-class=org.jetbrains.kotlin.gradle.plugin.performance.KotlinPerformancePlugin
|
||||
Reference in New Issue
Block a user