Benchmarks: Move project configuration to a separate plugin

This patch moves benchmark configuration logic described in
performance/gradle/benchmark.gradle to a separate plugin located
in the buildSrc project.
This commit is contained in:
Ilya Matveev
2019-06-11 12:50:44 +07:00
committed by Ilya Matveev
parent 487f6236d2
commit 8600e4bd66
11 changed files with 327 additions and 195 deletions
+11 -1
View File
@@ -30,6 +30,7 @@ buildscript {
apply plugin: 'kotlin'
apply plugin: 'kotlinx-serialization'
apply plugin: 'java-gradle-plugin'
/* don't use repositories: gradle will ignore it anyway, but may confuse gradle build engineer, see outer build.gradle */
@@ -57,4 +58,13 @@ rootProject.dependencies {
compileGroovy {
// Add Kotlin classes to a classpath for the Groovy compiler
classpath += project.files(compileKotlin.destinationDir)
}
}
gradlePlugin {
plugins {
benchmarkPlugin {
id = 'benchmarking'
implementationClass = 'org.jetbrains.kotlin.benchmark.BenchmarkingPlugin'
}
}
}
@@ -26,7 +26,7 @@ import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.file.*
internal class ExecClang(private val project: Project) {
class ExecClang(private val project: Project) {
private val platformManager = project.rootProject.findProperty("platformManager") as PlatformManager
@@ -69,9 +69,6 @@ fun getNativeProgramExtension(): String = when {
else -> error("Unknown host")
}
fun getKotlinNativeExecutable(target: KotlinNativeTarget, buildType: String) =
target.binaries.getExecutable("benchmark", buildType).outputFile.getAbsolutePath()
fun getFileSize(filePath: String): Long? {
val file = File(filePath)
return if (file.exists()) file.length() else null
@@ -0,0 +1,231 @@
package org.jetbrains.kotlin.benchmark
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import org.jetbrains.kotlin.konan.target.HostManager
import javax.inject.Inject
private val NamedDomainObjectContainer<KotlinSourceSet>.commonMain
get() = maybeCreate("commonMain")
private val NamedDomainObjectContainer<KotlinSourceSet>.nativeMain
get() = maybeCreate("nativeMain")
private val NamedDomainObjectContainer<KotlinSourceSet>.jvmMain
get() = maybeCreate("jvmMain")
private val Project.benchmark: BenchmarkExtension
get() = extensions.getByName(BenchmarkingPlugin.BENCHMARK_EXTENSION_NAME) as BenchmarkExtension
private val Project.nativeWarmup: Int
get() = (property("nativeWarmup") as String).toInt()
private val Project.jvmWarmup: Int
get() = (property("jvmWarmup") as String).toInt()
private val Project.attempts: Int
get() = (property("attempts") as String).toInt()
private val Project.nativeBenchResults: String
get() = property("nativeBenchResults") as String
private val Project.jvmBenchResults: String
get() = property("jvmBenchResults") as String
private val Project.compilerArgs: List<String>
get() = (findProperty("compilerArgs") as String?)?.split("\\s").orEmpty()
internal val Project.kotlinVersion: String
get() = property("kotlinVersion") as String
internal val Project.konanVersion: String
get() = property("konanVersion") as String
internal val Project.nativeJson: String
get() = project.property("nativeJson") as String
internal val Project.commonBenchmarkProperties: Map<String, Any>
get() = mapOf(
"cpu" to System.getProperty("os.arch"),
"os" to System.getProperty("os.name"),
"jdkVersion" to System.getProperty("java.version"),
"jdkVendor" to System.getProperty("java.vendor"),
"kotlinVersion" to kotlinVersion
)
open class BenchmarkExtension @Inject constructor(val project: Project) {
var applicationName: String = project.name
var commonSrcDirs: Collection<Any> = emptyList()
var jvmSrcDirs: Collection<Any> = emptyList()
var nativeSrcDirs: Collection<Any> = emptyList()
var linkerOpts: Collection<String> = emptyList()
}
/**
* A plugin configuring a benchmark Kotlin/Native project.
*/
open class BenchmarkingPlugin: Plugin<Project> {
private val mingwPath: String = System.getenv("MINGW64_DIR") ?: "c:/msys64/mingw64"
private fun Project.determinePreset(): KotlinNativeTargetPreset =
defaultHostPreset(this).also { preset ->
logger.quiet("$project has been configured for ${preset.name} platform.")
} as KotlinNativeTargetPreset
private fun Project.configureSourceSets(kotlinVersion: String) {
with(kotlin.sourceSets) {
commonMain.dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion")
}
jvmMain.dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
}
// Add sources specified by a user in the benchmark DSL.
afterEvaluate {
benchmark.let {
commonMain.kotlin.srcDirs(*it.commonSrcDirs.toTypedArray())
nativeMain.kotlin.srcDirs(*it.nativeSrcDirs.toTypedArray())
jvmMain.kotlin.srcDirs(*it.jvmSrcDirs.toTypedArray())
}
}
}
}
private fun Project.configureJVMTarget() {
kotlin.jvm {
compilations.all {
it.compileKotlinTask.kotlinOptions {
jvmTarget = "1.8"
suppressWarnings = true
freeCompilerArgs = project.compilerArgs
}
}
}
}
private fun Project.configureNativeTarget(hostPreset: KotlinNativeTargetPreset) {
kotlin.targetFromPreset(hostPreset, NATIVE_TARGET_NAME) {
compilations.getByName("main").kotlinOptions.freeCompilerArgs = project.compilerArgs + "-opt"
binaries.executable(NATIVE_EXECUTABLE_NAME, listOf(RELEASE)) {
if (HostManager.hostIsMingw) {
linkerOpts.add("-L${mingwPath}/lib")
}
// Specify settings configured by a user in the benchmark extension.
afterEvaluate {
linkerOpts.addAll(benchmark.linkerOpts)
runTask!!.args(
"-w", nativeWarmup,
"-r", attempts,
"-o", buildDir.resolve(nativeBenchResults).absolutePath,
"-p", "${benchmark.applicationName}::"
)
}
}
}
}
private fun Project.configureMPPExtension() {
configureSourceSets(kotlinVersion)
configureJVMTarget()
configureNativeTarget(determinePreset())
}
private fun Project.configureTasks() {
// Native run task.
val nativeTarget = kotlin.targets.getByName(NATIVE_TARGET_NAME) as KotlinNativeTarget
val nativeExecutable = nativeTarget.binaries.getExecutable(NATIVE_EXECUTABLE_NAME, NativeBuildType.RELEASE)
val konanRun = createRunTask(this, "konanRun", nativeExecutable.runTask!!)
// JVM run task.
val jvmRun = tasks.create("jvmRun", RunJvmTask::class.java) { task ->
task.dependsOn("build")
val mainCompilation = kotlin.jvm().compilations.getByName("main")
val runtimeDependencies = configurations.getByName(mainCompilation.runtimeDependencyConfigurationName)
task.classpath(files(mainCompilation.output.allOutputs, runtimeDependencies))
task.main = "MainKt"
// Specify settings configured by a user in the benchmark extension.
afterEvaluate {
task.args(
"-w", jvmWarmup,
"-r", attempts,
"-o", buildDir.resolve(jvmBenchResults),
"-p", "${benchmark.applicationName}::"
)
}
}
// Native report task.
val konanJsonReport = tasks.create("konanJsonReport") {
it.doLast {
val applicationName = benchmark.applicationName
val nativeCompileTime = getNativeCompileTime(applicationName)
val benchContents = buildDir.resolve(nativeBenchResults).readText()
val properties = commonBenchmarkProperties + mapOf(
"type" to "native",
"compilerVersion" to konanVersion,
"flags" to nativeTarget.compilations.main.kotlinOptions.freeCompilerArgs.map { "\"$it\"" },
"benchmarks" to benchContents,
"compileTime" to listOf(nativeCompileTime),
"codeSize" to getCodeSizeBenchmark(applicationName, nativeExecutable.outputFile.absolutePath)
)
val output = createJsonReport(properties)
buildDir.resolve(nativeJson).writeText(output)
}
}
// JVM report task.
val jvmJsonReport = tasks.create("jvmJsonReport") {
it.doLast {
val applicationName = benchmark.applicationName
val jarPath = (tasks.getByName("jvmJar") as Jar).archiveFile.get().asFile
val jvmCompileTime = getJvmCompileTime(applicationName)
val benchContents = buildDir.resolve(jvmBenchResults).readText()
val properties: Map<String, Any> = commonBenchmarkProperties + mapOf(
"type" to "jvm",
// TODO: We had buildKotlinVersion here!!!!
"compilerVersion" to kotlinVersion,
"benchmarks" to benchContents,
"compileTime" to listOf(jvmCompileTime),
"codeSize" to getCodeSizeBenchmark(applicationName, jarPath.absolutePath)
)
val output = createJsonReport(properties)
buildDir.resolve(project.property("jvmJson") as String).writeText(output)
}
jvmRun.finalizedBy(it)
}
}
override fun apply(target: Project) = with(target) {
pluginManager.apply("kotlin-multiplatform")
extensions.create(BENCHMARK_EXTENSION_NAME, BenchmarkExtension::class.java, this)
configureMPPExtension()
addTimeListener(this)
configureTasks()
}
companion object {
const val NATIVE_TARGET_NAME = "native"
const val NATIVE_EXECUTABLE_NAME = "benchmark"
const val BENCHMARK_EXTENSION_NAME = "benchmark"
}
}
-12
View File
@@ -1,12 +0,0 @@
project.ext {
applicationName = 'Cinterop'
commonSrcDirs = ['../../tools/benchmarks/shared/src', 'src/main/kotlin', '../shared/src/main/kotlin', '../../tools/kliopt']
jvmSrcDirs = ['src/main/kotlin-jvm', '../shared/src/main/kotlin-jvm']
nativeSrcDirs = ['src/main/kotlin-native', '../shared/src/main/kotlin-native']
}
apply from: rootProject.file("${rootProject.rootDir}/performance/gradle/benchmark.gradle")
kotlin.targets.native.compilations.main.cinterops {
macros
struct
types
}
+24
View File
@@ -0,0 +1,24 @@
/*
* 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 file.
*/
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
id("benchmarking")
}
benchmark {
applicationName = "Cinterop"
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin", "../../tools/kliopt")
jvmSrcDirs = listOf("src/main/kotlin-jvm", "../shared/src/main/kotlin-jvm")
nativeSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native")
}
val native = kotlin.targets.getByName("native") as KotlinNativeTarget
native.compilations["main"].cinterops {
create("macros")
create("struct")
create("types")
}
-141
View File
@@ -1,141 +0,0 @@
import org.jetbrains.kotlin.MPPTools
import org.jetbrains.kotlin.RunJvmTask
apply plugin: 'kotlin-multiplatform'
repositories {
maven {
url 'https://cache-redirector.jetbrains.com/jcenter'
}
maven {
url kotlinCompilerRepo
}
maven {
url buildKotlinCompilerRepo
}
}
private def determinePreset() {
def preset = MPPTools.defaultHostPreset(project)
println("$project has been configured for ${preset.name} platform.")
preset
}
def getMingwPath() {
def directory = System.getenv("MINGW64_DIR")
if (directory == null)
directory = "c:/msys64/mingw64"
return directory
}
def hostPreset = determinePreset()
kotlin {
sourceSets {
commonMain {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion"
}
project.ext.commonSrcDirs.forEach {
kotlin.srcDir it
}
}
nativeMain {
project.ext.nativeSrcDirs.forEach {
kotlin.srcDir it
}
}
jvmMain {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
}
project.ext.jvmSrcDirs.forEach {
kotlin.srcDir it
}
}
}
jvm() {
compilations.all {
tasks[compileKotlinTaskName].kotlinOptions {
jvmTarget = '1.8'
suppressWarnings = true
freeCompilerArgs = (project.hasProperty('compilerArgs') ? compilerArgs.split() : [])
}
}
}
targetFromPreset(hostPreset, 'native') {
compilations.main.extraOpts = (project.hasProperty('compilerArgs') ? compilerArgs.split() : []) + '-opt'
binaries {
executable('benchmark', [RELEASE]) {
if (project.hasProperty("linkerOpts")) {
linkerOpts = project.ext.linkerOpts
}
if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
linkerOpts += ["-L${getMingwPath()}/lib".toString()]
}
runTask.args("-w", "$nativeWarmup", "-r", "$attempts", "-o", "${buildDir.absolutePath}/${nativeBenchResults}", "-p", "${project.ext.applicationName}::")
}
}
}
}
MPPTools.addTimeListener(project)
MPPTools.createRunTask(project, 'konanRun', kotlin.targets.native.binaries.getExecutable("benchmark", "RELEASE").runTask)
task jvmRun(type: RunJvmTask) {
dependsOn 'build'
def runtimeClasspath = files(
kotlin.targets.jvm.compilations.main.output.allOutputs,
project.configurations.getByName(kotlin.targets.jvm.compilations.main.runtimeDependencyConfigurationName)
)
classpath runtimeClasspath
main = "MainKt"
args "-w", "$jvmWarmup", "-r", "$attempts", "-o", "${buildDir.absolutePath}/${jvmBenchResults}", "-p", "${project.ext.applicationName}::"
}
task konanJsonReport {
doLast {
def nativeExecutable = MPPTools.getKotlinNativeExecutable(kotlin.targets.native, "RELEASE")
def nativeCompileTime = MPPTools.getNativeCompileTime(project.ext.applicationName)
String benchContents = new File("${buildDir.absolutePath}/${nativeBenchResults}").text
def properties = getCommonProperties() + ['type': 'native',
'compilerVersion': "${konanVersion}".toString(),
'flags': kotlin.targets.native.compilations.main.extraOpts.collect{ "\"$it\"" },
'benchmarks': benchContents,
'compileTime': [nativeCompileTime],
'codeSize': MPPTools.getCodeSizeBenchmark(project.ext.applicationName, nativeExecutable) ]
def output = MPPTools.createJsonReport(properties)
new File("${buildDir.absolutePath}/${nativeJson}").write(output)
}
}
task jvmJsonReport {
doLast {
def jarPath = project.getTasks().getByName("jvmJar").archivePath
def jvmCompileTime = MPPTools.getJvmCompileTime(project.ext.applicationName)
String benchContents = new File("${buildDir.absolutePath}/${jvmBenchResults}").text
def properties = getCommonProperties() + ['type': 'jvm',
'compilerVersion': "${buildKotlinVersion}".toString(),
'benchmarks': benchContents,
'compileTime': [jvmCompileTime],
'codeSize': MPPTools.getCodeSizeBenchmark(project.ext.applicationName, "${jarPath}") ]
def output = MPPTools.createJsonReport(properties)
new File("${buildDir.absolutePath}/${jvmJson}").write(output)
}
}
jvmRun.finalizedBy jvmJsonReport
private def getCommonProperties() {
return ['cpu': System.getProperty("os.arch"),
'os': System.getProperty("os.name"), // OperatingSystem.current().getName()
'jdkVersion': System.getProperty("java.version"), // org.gradle.internal.jvm.Jvm.current().javaVersion
'jdkVendor': System.getProperty("java.vendor"),
'kotlinVersion': "${kotlinVersion}".toString()]
}
-29
View File
@@ -1,29 +0,0 @@
import org.jetbrains.kotlin.konan.target.HostManager
project.ext {
applicationName = 'ObjCInterop'
commonSrcDirs = ['../../tools/benchmarks/shared/src', 'src/main/kotlin', '../shared/src/main/kotlin', '../../tools/kliopt']
jvmSrcDirs = ['src/main/kotlin-jvm', '../shared/src/main/kotlin-jvm']
nativeSrcDirs = ['src/main/kotlin-native', '../shared/src/main/kotlin-native']
linkerOpts = ["-L$buildDir".toString(), "-lcomplexnumbers".toString()]
}
task compileLibrary {
doFirst {
mkdir buildDir.absolutePath
execKonanClang(HostManager.host) {
args "$projectDir/src/nativeInterop/cinterop/complexNumbers.m"
args "-lobjc", '-fobjc-arc'
args '-fPIC', '-shared', '-o', "$buildDir/libcomplexnumbers.dylib"
}
}
}
apply from: rootProject.file("${rootProject.rootDir}/performance/gradle/benchmark.gradle")
kotlin.targets.native.compilations.main.cinterops {
classes {
headers "$projectDir/src/nativeInterop/cinterop/complexNumbers.h"
}
}
kotlin.targets.native.binaries.getExecutable("benchmark", "RELEASE").linkTask.dependsOn 'compileLibrary'
+45
View File
@@ -0,0 +1,45 @@
/*
* 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 file.
*/
import org.jetbrains.kotlin.benchmark.BenchmarkingPlugin
import org.jetbrains.kotlin.ExecClang
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.konan.target.HostManager
plugins {
id("benchmarking")
}
benchmark {
applicationName = "ObjCInterop"
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin", "../../tools/kliopt")
jvmSrcDirs = listOf("src/main/kotlin-jvm", "../shared/src/main/kotlin-jvm")
nativeSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native")
linkerOpts = listOf("-L$buildDir", "-lcomplexnumbers")
}
val compileLibary by tasks.creating {
doFirst {
mkdir(buildDir)
project.withConvention(ExecClang::class) {
execKonanClang(HostManager.host) {
args("$projectDir/src/nativeInterop/cinterop/complexNumbers.m")
args("-lobjc", "-fobjc-arc")
args("-fPIC", "-shared", "-o", "$buildDir/libcomplexnumbers.dylib")
}
}
}
}
val native = kotlin.targets.getByName("native") as KotlinNativeTarget
native.apply {
compilations["main"].cinterops {
create("classes") {
headers("$projectDir/src/nativeInterop/cinterop/complexNumbers.h")
}
}
binaries.getExecutable(BenchmarkingPlugin.NATIVE_EXECUTABLE_NAME, "RELEASE").linkTask.dependsOn(compileLibary)
}
-8
View File
@@ -1,8 +0,0 @@
project.ext {
applicationName = 'Ring'
commonSrcDirs = ['../../tools/benchmarks/shared/src', 'src/main/kotlin', '../shared/src/main/kotlin', '../../tools/kliopt']
jvmSrcDirs = ['src/main/kotlin-jvm', '../shared/src/main/kotlin-jvm']
nativeSrcDirs = ['src/main/kotlin-native', '../shared/src/main/kotlin-native']
}
apply from: rootProject.file("${rootProject.rootDir}/performance/gradle/benchmark.gradle")
+15
View File
@@ -0,0 +1,15 @@
/*
* 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 file.
*/
plugins {
id("benchmarking")
}
benchmark {
applicationName = "Ring"
commonSrcDirs = listOf("../../tools/benchmarks/shared/src", "src/main/kotlin", "../shared/src/main/kotlin", "../../tools/kliopt")
jvmSrcDirs = listOf("src/main/kotlin-jvm", "../shared/src/main/kotlin-jvm")
nativeSrcDirs = listOf("src/main/kotlin-native", "../shared/src/main/kotlin-native")
}