Kotlin/Native performance gradle plugin (#2713)

This commit is contained in:
LepilkinaElena
2019-10-29 14:23:50 +03:00
committed by GitHub
parent 7160653546
commit b37dc32e03
5 changed files with 234 additions and 0 deletions
@@ -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"
}
}
@@ -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
}
}
@@ -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")
}
}
}
@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlin.gradle.plugin.performance.KotlinPerformancePlugin
@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlin.gradle.plugin.performance.KotlinPerformancePlugin