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