diff --git a/build-tools/build.gradle.kts b/build-tools/build.gradle.kts index 2f9b3ffff2b..813f958da21 100644 --- a/build-tools/build.gradle.kts +++ b/build-tools/build.gradle.kts @@ -63,12 +63,16 @@ gradlePlugin { plugins { create("benchmarkPlugin") { id = "benchmarking" - implementationClass = "org.jetbrains.kotlin.benchmark.BenchmarkingPlugin" + implementationClass = "org.jetbrains.kotlin.benchmark.KotlinNativeBenchmarkingPlugin" } create("compileBenchmarking") { id = "compile-benchmarking" implementationClass = "org.jetbrains.kotlin.benchmark.CompileBenchmarkingPlugin" } + create("swiftBenchmarking") { + id = "swift-benchmarking" + implementationClass = "org.jetbrains.kotlin.benchmark.SwiftBenchmarkingPlugin" + } } } diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/MPPTools.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/MPPTools.kt index 0fccf73f774..a5a9c6ccfbb 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/MPPTools.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/MPPTools.kt @@ -159,21 +159,11 @@ fun sendUploadRequest(url: String, fileName: String, username: String? = null, p fun createRunTask( subproject: Project, name: String, - linkTask: KotlinNativeLink, + linkTask: Task, + executable: String, outputFileName: String ): Task { - return subproject.tasks.create(name, RunKotlinNativeTask::class.java, linkTask, outputFileName) -} - -@JvmOverloads -fun createBenchmarksRunTask( - subproject: Project, - name: String, - configureClosure: Closure? = null -): Task { - val task = subproject.tasks.create(name, RunBenchmarksExecutableTask::class.java) - task.configure(configureClosure ?: task.emptyConfigureClosure()) - return task + return subproject.tasks.create(name, RunKotlinNativeTask::class.java, linkTask, executable, outputFileName) } fun getJvmCompileTime(programName: String): BenchmarkResult = diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/RunBenchmarksExecutableTask.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/RunBenchmarksExecutableTask.kt deleted file mode 100644 index 621d6fb6614..00000000000 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/RunBenchmarksExecutableTask.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. 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 - -import groovy.lang.Closure -import org.gradle.api.DefaultTask -import org.gradle.api.Task -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.options.Option -import org.gradle.api.tasks.Input -import javax.inject.Inject -import java.io.File - -open class RunBenchmarksExecutableTask @Inject constructor() : DefaultTask() { - var workingDir: Any = project.projectDir - var outputFileName: String? = null - var executable: String? = null - @Input - @Option(option = "filter", description = "Benchmarks to run (comma-separated)") - var filter: String = "" - @Input - @Option(option = "filterRegex", description = "Benchmarks to run, described by regular expressions (comma-separated)") - var filterRegex: String = "" - @Input - @Option(option = "verbose", description = "Verbose mode of running benchmarks") - var verbose: Boolean = false - - private var curArgs: List = emptyList() - private val curEnvironment: MutableMap = mutableMapOf() - - fun args(vararg args: Any) { - curArgs = args.map { it.toString() } - } - - fun environment(map: Map) { - curEnvironment += map - } - - override fun configure(configureClosure: Closure): Task { - val task = super.configure(configureClosure) - return task - } - - fun depends(taskName: String) { - this.dependsOn += taskName - } - - private fun executeTask(output: java.io.OutputStream? = null) { - val filterArgs = filter.splitCommaSeparatedOption("-f") - val filterRegexArgs = filterRegex.splitCommaSeparatedOption("-fr") - project.exec { - it.executable = executable - it.args = curArgs + filterArgs + filterRegexArgs - it.environment = curEnvironment - it.workingDir(workingDir) - if (verbose) { - it.args("-v") - } - if (output != null) - it.standardOutput = output - } - } - - @TaskAction - fun run() { - if (outputFileName != null) - File(outputFileName).outputStream().use { output -> executeTask(output) } - else - executeTask() - } - - internal fun emptyConfigureClosure() = object : Closure(this) { - override fun call(): RunBenchmarksExecutableTask { - return this@RunBenchmarksExecutableTask - } - } -} \ No newline at end of file diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/RunKotlinNativeTask.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/RunKotlinNativeTask.kt index 20ef315f751..980cb6cc72a 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/RunKotlinNativeTask.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/RunKotlinNativeTask.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin import org.gradle.api.DefaultTask +import org.gradle.api.Task import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.options.Option @@ -14,7 +15,8 @@ import java.io.ByteArrayOutputStream import java.io.File import javax.inject.Inject -open class RunKotlinNativeTask @Inject constructor(private val linkTask: KotlinNativeLink, +open class RunKotlinNativeTask @Inject constructor(private val linkTask: Task, + private val executable: String, private val outputFileName: String ) : DefaultTask() { @Input @@ -48,7 +50,7 @@ open class RunKotlinNativeTask @Inject constructor(private val linkTask: KotlinN fun run() { var output = ByteArrayOutputStream() project.exec { - it.executable = linkTask.binary.outputFile.absolutePath + it.executable = executable it.args("list") it.standardOutput = output } @@ -63,7 +65,7 @@ open class RunKotlinNativeTask @Inject constructor(private val linkTask: KotlinN val results = benchmarksToRun.map { benchmark -> output = ByteArrayOutputStream() project.exec { - it.executable = linkTask.binary.outputFile.absolutePath + it.executable = executable it.args(argumentsList) it.args("-f", benchmark) if (verbose) { diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt index b1cd2abb39f..4bbf7547f42 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/BenchmarkingPlugin.kt @@ -4,45 +4,33 @@ import groovy.lang.Closure import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.artifacts.Dependency -import org.gradle.jvm.tasks.Jar import org.gradle.util.ConfigureUtil 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 +import kotlin.reflect.KClass -private val NamedDomainObjectContainer.commonMain +internal val NamedDomainObjectContainer.commonMain get() = maybeCreate("commonMain") -private val NamedDomainObjectContainer.nativeMain +internal val NamedDomainObjectContainer.nativeMain get() = maybeCreate("nativeMain") -private val NamedDomainObjectContainer.jvmMain - get() = maybeCreate("jvmMain") - -private val Project.benchmark: BenchmarkExtension - get() = extensions.getByName(BenchmarkingPlugin.BENCHMARK_EXTENSION_NAME) as BenchmarkExtension - -private val Project.nativeWarmup: Int +internal 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 +internal val Project.attempts: Int get() = (property("attempts") as String).toInt() -private val Project.nativeBenchResults: String +internal val Project.nativeBenchResults: String get() = property("nativeBenchResults") as String -private val Project.jvmBenchResults: String - get() = property("jvmBenchResults") as String - -private val Project.compilerArgs: List +internal val Project.compilerArgs: List get() = (findProperty("compilerArgs") as String?)?.split("\\s").orEmpty() internal val Project.kotlinVersion: String @@ -75,10 +63,8 @@ internal val Project.commonBenchmarkProperties: Map open class BenchmarkExtension @Inject constructor(val project: Project) { var applicationName: String = project.name var commonSrcDirs: Collection = emptyList() - var jvmSrcDirs: Collection = emptyList() var nativeSrcDirs: Collection = emptyList() - var mingwSrcDirs: Collection = emptyList() - var posixSrcDirs: Collection = emptyList() + var compileTasks: List = emptyList() var linkerOpts: Collection = emptyList() val dependencies: BenchmarkDependencies = BenchmarkDependencies() @@ -91,7 +77,7 @@ open class BenchmarkExtension @Inject constructor(val project: Project) { } inner class BenchmarkDependencies { - private val sourceSets: NamedDomainObjectContainer + public val sourceSets: NamedDomainObjectContainer get() = project.kotlin.sourceSets fun project(path: String): Dependency = project.dependencies.project(mapOf("path" to path)) @@ -103,10 +89,6 @@ open class BenchmarkExtension @Inject constructor(val project: Project) { implementation(notation) } - fun jvm(notation: Any) = sourceSets.jvmMain.dependencies { - implementation(notation) - } - fun native(notation: Any) = sourceSets.nativeMain.dependencies { implementation(notation) } @@ -116,25 +98,30 @@ open class BenchmarkExtension @Inject constructor(val project: Project) { /** * A plugin configuring a benchmark Kotlin/Native project. */ -open class BenchmarkingPlugin: Plugin { +abstract class BenchmarkingPlugin: Plugin { + protected abstract val Project.nativeExecutable: String + protected abstract val Project.nativeLinkTask: Task + protected abstract val Project.benchmark: BenchmarkExtension + protected abstract val benchmarkExtensionName: String + protected abstract val benchmarkExtensionClass: KClass<*> - private val mingwPath: String = System.getenv("MINGW64_DIR") ?: "c:/msys64/mingw64" + protected val mingwPath: String = System.getenv("MINGW64_DIR") ?: "c:/msys64/mingw64" - private fun Project.determinePreset(): KotlinNativeTargetPreset = + protected open 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) { + protected abstract fun NamedDomainObjectContainer.configureSources(project: Project) + + protected open fun NamedDomainObjectContainer.additionalConfigurations(project: Project) {} + + protected open fun Project.configureSourceSets(kotlinVersion: String) { with(kotlin.sourceSets) { commonMain.dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinStdlibVersion") } - jvmMain.dependencies { - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinStdlibVersion") - } - project.configurations.getByName(nativeMain.implementationConfigurationName).apply { // Exclude dependencies already included into K/N distribution (aka endorsed libraries). exclude(mapOf("module" to "kotlinx.cli")) @@ -144,69 +131,49 @@ open class BenchmarkingPlugin: Plugin { it.setUrl(kotlinStdlibRepo) } + additionalConfigurations(this@configureSourceSets) + // Add sources specified by a user in the benchmark DSL. afterEvaluate { - benchmark.let { - commonMain.kotlin.srcDirs(*it.commonSrcDirs.toTypedArray()) - if (HostManager.hostIsMingw) { - nativeMain.kotlin.srcDirs(*(it.nativeSrcDirs + it.mingwSrcDirs).toTypedArray()) - } else { - nativeMain.kotlin.srcDirs(*(it.nativeSrcDirs + it.posixSrcDirs).toTypedArray()) - } - jvmMain.kotlin.srcDirs(*it.jvmSrcDirs.toTypedArray()) - } + configureSources(project) } } } - private fun Project.configureJVMTarget() { - kotlin.jvm { - compilations.all { - it.compileKotlinTask.kotlinOptions { - jvmTarget = "1.8" - suppressWarnings = true - freeCompilerArgs = project.compilerArgs - } + protected open fun KotlinNativeTarget.configureNativeOutput(project: Project) { + binaries.executable(NATIVE_EXECUTABLE_NAME, listOf(RELEASE)) { + if (HostManager.hostIsMingw) { + linkerOpts.add("-L${mingwPath}/lib") + } + + runTask!!.apply { + group = "" + enabled = false + } + + // Specify settings configured by a user in the benchmark extension. + project.afterEvaluate { + linkerOpts.addAll(project.benchmark.linkerOpts) } } } - private fun Project.configureNativeTarget(hostPreset: KotlinNativeTargetPreset) { + protected open fun Project.configureNativeTarget(hostPreset: KotlinNativeTargetPreset) { kotlin.targetFromPreset(hostPreset, NATIVE_TARGET_NAME) { compilations.getByName("main").kotlinOptions.freeCompilerArgs = project.compilerArgs compilations.getByName("main").enableEndorsedLibs = true - binaries.executable(NATIVE_EXECUTABLE_NAME, listOf(RELEASE)) { - if (HostManager.hostIsMingw) { - linkerOpts.add("-L${mingwPath}/lib") - } - - runTask!!.apply { - group = "" - enabled = false - } - - // Specify settings configured by a user in the benchmark extension. - afterEvaluate { - linkerOpts.addAll(benchmark.linkerOpts) - } - } + configureNativeOutput(this@configureNativeTarget) } } - - private fun Project.configureMPPExtension() { - configureSourceSets(kotlinVersion) - configureJVMTarget() - configureNativeTarget(determinePreset()) + protected open fun configureMPPExtension(project: Project) { + project.configureSourceSets(project.kotlinVersion) + project.configureNativeTarget(project.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.linkTask, - buildDir.resolve(nativeBenchResults).absolutePath).apply { + protected open fun Project.configureNativeTask(nativeTarget: KotlinNativeTarget): Task { + val konanRun = createRunTask(this, "konanRun", nativeLinkTask, + nativeExecutable, buildDir.resolve(nativeBenchResults).absolutePath).apply { group = BENCHMARKING_GROUP description = "Runs the benchmark for Kotlin/Native." } @@ -217,80 +184,61 @@ open class BenchmarkingPlugin: Plugin { "-p", "${benchmark.applicationName}::" ) } + return konanRun + } - // 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" + protected abstract fun Project.configureJvmTask(): Task - task.group = BENCHMARKING_GROUP - task.description = "Runs the benchmark for Kotlin/JVM." + protected open fun Project.getCompilerFlags(nativeTarget: KotlinNativeTarget) = + nativeTarget.compilations.main.kotlinOptions.freeCompilerArgs.map { "\"$it\"" } - // 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") { + protected open fun Project.collectCodeSize(applicationName: String) = + getCodeSizeBenchmark(applicationName, nativeExecutable) + protected open fun Project.configureKonanJsonTask(nativeTarget: KotlinNativeTarget): Task { + return tasks.create("konanJsonReport") { it.group = BENCHMARKING_GROUP it.description = "Builds the benchmarking report for Kotlin/Native." it.doLast { val applicationName = benchmark.applicationName - val nativeCompileTime = getNativeCompileTime(applicationName) val benchContents = buildDir.resolve(nativeBenchResults).readText() + val nativeCompileTime = if (benchmark.compileTasks.isEmpty()) getNativeCompileTime(applicationName) + else getNativeCompileTime(applicationName, benchmark.compileTasks) 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) + "type" to "native", + "compilerVersion" to konanVersion, + "flags" to getCompilerFlags(nativeTarget), + "benchmarks" to benchContents, + "compileTime" to listOf(nativeCompileTime), + "codeSize" to collectCodeSize(applicationName) ) val output = createJsonReport(properties) buildDir.resolve(nativeJson).writeText(output) } } + } + + protected abstract fun Project.configureJvmJsonTask(jvmRun: Task): Task + + protected open fun Project.configureExtraTasks() {} + + private fun Project.configureTasks() { + val nativeTarget = kotlin.targets.getByName(NATIVE_TARGET_NAME) as KotlinNativeTarget + configureExtraTasks() + // Native run task. + configureNativeTask(nativeTarget) + + // JVM run task. + val jvmRun = configureJvmTask() + + // Native report task. + configureKonanJsonTask(nativeTarget) // JVM report task. - val jvmJsonReport = tasks.create("jvmJsonReport") { - - it.group = BENCHMARKING_GROUP - it.description = "Builds the benchmarking report for Kotlin/JVM." - - 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 = commonBenchmarkProperties + mapOf( - "type" to "jvm", - "compilerVersion" to kotlinVersion, - "benchmarks" to benchContents, - "compileTime" to listOf(jvmCompileTime), - "codeSize" to getCodeSizeBenchmark(applicationName, jarPath.absolutePath) - ) - - val output = createJsonReport(properties) - buildDir.resolve(jvmJson).writeText(output) - } - - jvmRun.finalizedBy(it) - } + configureJvmJsonTask(jvmRun) } override fun apply(target: Project) = with(target) { @@ -298,18 +246,16 @@ open class BenchmarkingPlugin: Plugin { // Use Kotlin compiler version specified by the project property. dependencies.add("kotlinCompilerClasspath", "org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlinVersion") - - extensions.create(BENCHMARK_EXTENSION_NAME, BenchmarkExtension::class.java, this) - configureMPPExtension() addTimeListener(this) + + extensions.create(benchmarkExtensionName, benchmarkExtensionClass.java, this) + configureMPPExtension(this) configureTasks() } companion object { const val NATIVE_TARGET_NAME = "native" const val NATIVE_EXECUTABLE_NAME = "benchmark" - const val BENCHMARK_EXTENSION_NAME = "benchmark" - const val BENCHMARKING_GROUP = "benchmarking" } } diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/KotlinNativeBenchmarkingPlugin.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/KotlinNativeBenchmarkingPlugin.kt new file mode 100644 index 00000000000..18876c80b74 --- /dev/null +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/KotlinNativeBenchmarkingPlugin.kt @@ -0,0 +1,145 @@ +package org.jetbrains.kotlin.benchmark + +import org.gradle.jvm.tasks.Jar +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Project +import org.gradle.api.Task +import org.jetbrains.kotlin.* +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.Executable +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import org.jetbrains.kotlin.konan.target.HostManager +import javax.inject.Inject +import kotlin.reflect.KClass + +private val NamedDomainObjectContainer.jvmMain + get() = maybeCreate("jvmMain") + +private val Project.jvmWarmup: Int + get() = (property("jvmWarmup") as String).toInt() + +private val Project.jvmBenchResults: String + get() = property("jvmBenchResults") as String + +open class KotlinNativeBenchmarkExtension @Inject constructor(project: Project) : BenchmarkExtension(project) { + var jvmSrcDirs: Collection = emptyList() + var mingwSrcDirs: Collection = emptyList() + var posixSrcDirs: Collection = emptyList() + + fun BenchmarkExtension.BenchmarkDependencies.jvm(notation: Any) = sourceSets.jvmMain.dependencies { + implementation(notation) + } +} + +/** + * A plugin configuring a benchmark Kotlin/Native project. + */ +open class KotlinNativeBenchmarkingPlugin: BenchmarkingPlugin() { + override fun Project.configureJvmJsonTask(jvmRun: Task): Task { + return tasks.create("jvmJsonReport") { + it.group = BENCHMARKING_GROUP + it.description = "Builds the benchmarking report for Kotlin/JVM." + + 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 = commonBenchmarkProperties + mapOf( + "type" to "jvm", + "compilerVersion" to kotlinVersion, + "benchmarks" to benchContents, + "compileTime" to listOf(jvmCompileTime), + "codeSize" to getCodeSizeBenchmark(applicationName, jarPath.absolutePath) + ) + + val output = createJsonReport(properties) + buildDir.resolve(jvmJson).writeText(output) + } + + jvmRun.finalizedBy(it) + } + } + + override fun Project.configureJvmTask(): Task { + return 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" + + task.group = BENCHMARKING_GROUP + task.description = "Runs the benchmark for Kotlin/JVM." + + // Specify settings configured by a user in the benchmark extension. + afterEvaluate { + task.args( + "-w", jvmWarmup, + "-r", attempts, + "-o", buildDir.resolve(jvmBenchResults), + "-p", "${benchmark.applicationName}::" + ) + } + } + } + + override val benchmarkExtensionClass: KClass<*> + get() = KotlinNativeBenchmarkExtension::class + + override val Project.benchmark: KotlinNativeBenchmarkExtension + get() = extensions.getByName(benchmarkExtensionName) as KotlinNativeBenchmarkExtension + + override val benchmarkExtensionName: String = "benchmark" + + private val Project.nativeBinary: Executable + get() = (kotlin.targets.getByName(NATIVE_TARGET_NAME) as KotlinNativeTarget) + .binaries.getExecutable(NATIVE_EXECUTABLE_NAME, NativeBuildType.RELEASE) + + override val Project.nativeExecutable: String + get() = nativeBinary.outputFile.absolutePath + + override val Project.nativeLinkTask: Task + get() = nativeBinary.linkTask + + override fun configureMPPExtension(project: Project) { + super.configureMPPExtension(project) + project.configureJVMTarget() + } + + override fun NamedDomainObjectContainer.configureSources(project: Project) { + project.benchmark.let { + commonMain.kotlin.srcDirs(*it.commonSrcDirs.toTypedArray()) + if (HostManager.hostIsMingw) { + nativeMain.kotlin.srcDirs(*(it.nativeSrcDirs + it.mingwSrcDirs).toTypedArray()) + } else { + nativeMain.kotlin.srcDirs(*(it.nativeSrcDirs + it.posixSrcDirs).toTypedArray()) + } + jvmMain.kotlin.srcDirs(*it.jvmSrcDirs.toTypedArray()) + } + } + + override fun NamedDomainObjectContainer.additionalConfigurations(project: Project) { + jvmMain.dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${project.kotlinStdlibVersion}") + } + } + + private fun Project.configureJVMTarget() { + kotlin.jvm { + compilations.all { + it.compileKotlinTask.kotlinOptions { + jvmTarget = "1.8" + suppressWarnings = true + freeCompilerArgs = project.compilerArgs + } + } + } + } + + companion object { + const val BENCHMARK_EXTENSION_NAME = "benchmark" + } +} diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/SwiftBenchmarkingPlugin.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/SwiftBenchmarkingPlugin.kt new file mode 100644 index 00000000000..8329639cd97 --- /dev/null +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/benchmark/SwiftBenchmarkingPlugin.kt @@ -0,0 +1,103 @@ +package org.jetbrains.kotlin.benchmark + +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Project +import org.gradle.api.Task +import org.jetbrains.kotlin.* +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.Framework +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 java.io.File +import javax.inject.Inject +import java.nio.file.Paths +import kotlin.reflect.KClass + +enum class CodeSizeEntity { FRAMEWORK, EXECUTABLE } + +open class SwiftBenchmarkExtension @Inject constructor(project: Project) : BenchmarkExtension(project) { + var swiftSources: List = emptyList() + var useCodeSize: CodeSizeEntity = CodeSizeEntity.FRAMEWORK // use as code size metric framework size or executable +} + +/** + * A plugin configuring a benchmark Kotlin/Native project. + */ +open class SwiftBenchmarkingPlugin : BenchmarkingPlugin() { + override fun Project.configureJvmJsonTask(jvmRun: Task): Task { + return tasks.create("jvmJsonReport") { + logger.info("JVM run is unsupported") + jvmRun.finalizedBy(it) + } + } + + override fun Project.configureJvmTask(): Task { + return tasks.create("jvmRun") { task -> + task.doLast { + logger.info("JVM run is unsupported") + } + } + } + + override val benchmarkExtensionClass: KClass<*> + get() = SwiftBenchmarkExtension::class + + override val Project.benchmark: SwiftBenchmarkExtension + get() = extensions.getByName(benchmarkExtensionName) as SwiftBenchmarkExtension + + override val benchmarkExtensionName: String = "swiftBenchmark" + + override val Project.nativeExecutable: String + get() = Paths.get(buildDir.absolutePath, benchmark.applicationName).toString() + + override val Project.nativeLinkTask: Task + get() = tasks.getByName("buildSwift") + + private lateinit var framework: Framework + val nativeFrameworkName = "benchmark" + + override fun NamedDomainObjectContainer.configureSources(project: Project) { + project.benchmark.let { + commonMain.kotlin.srcDirs(*it.commonSrcDirs.toTypedArray()) + nativeMain.kotlin.srcDirs(*(it.nativeSrcDirs).toTypedArray()) + } + } + + override fun Project.determinePreset(): KotlinNativeTargetPreset = kotlin.presets.macosX64 as KotlinNativeTargetPreset + + override fun KotlinNativeTarget.configureNativeOutput(project: Project) { + binaries.framework(nativeFrameworkName, listOf(RELEASE)) { + // Specify settings configured by a user in the benchmark extension. + project.afterEvaluate { + linkerOpts.addAll(project.benchmark.linkerOpts) + } + } + } + + override fun Project.configureExtraTasks() { + val nativeTarget = kotlin.targets.getByName(NATIVE_TARGET_NAME) as KotlinNativeTarget + // Build executable from swift code. + framework = nativeTarget.binaries.getFramework(nativeFrameworkName, NativeBuildType.RELEASE) + val buildSwift = tasks.create("buildSwift") { task -> + task.dependsOn(framework.linkTaskName) + task.doLast { + val frameworkParentDirPath = framework.outputDirectory.absolutePath + val options = listOf("-Xlinker", "-rpath", "-Xlinker", frameworkParentDirPath, "-F", frameworkParentDirPath) + compileSwift(project, nativeTarget.konanTarget, benchmark.swiftSources, options, + Paths.get(buildDir.absolutePath, benchmark.applicationName), false) + } + } + } + + override fun Project.collectCodeSize(applicationName: String) = + getCodeSizeBenchmark(applicationName, + if (benchmark.useCodeSize == CodeSizeEntity.FRAMEWORK) + File("${framework.outputFile.absolutePath}/$nativeFrameworkName").canonicalPath + else + nativeExecutable + ) + + override fun Project.getCompilerFlags(nativeTarget: KotlinNativeTarget) = + listOf() +} diff --git a/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt b/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt index 54a78a37a7d..d57b88c067d 100644 --- a/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt +++ b/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt @@ -56,7 +56,6 @@ abstract class Launcher { } else { printStderr("$message") } - } } } diff --git a/performance/swiftinterop/build.gradle b/performance/swiftinterop/build.gradle deleted file mode 100644 index a7a7f030093..00000000000 --- a/performance/swiftinterop/build.gradle +++ /dev/null @@ -1,149 +0,0 @@ -import org.jetbrains.kotlin.MPPTools -import org.jetbrains.kotlin.PlatformInfo -import org.jetbrains.kotlin.RunJvmTask -import org.jetbrains.kotlin.konan.target.HostManager -import org.jetbrains.kotlin.UtilsKt -import java.nio.file.Paths - -buildscript { - apply from: "$rootProject.projectDir/gradle/kotlinGradlePlugin.gradle" - repositories { - maven { - url 'https://cache-redirector.jetbrains.com/jcenter' - } - } -} - -apply plugin: 'kotlin-multiplatform' - -repositories { - maven { - url 'https://cache-redirector.jetbrains.com/jcenter' - } - maven { - url kotlinCompilerRepo - } - maven { - url buildKotlinCompilerRepo - } -} - -def toolsPath = '../../tools' -def frameworkName = 'CityMap' - -kotlin { - sourceSets { - commonMain { - dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion" - implementation project(":endorsedLibraries:kotlinx.cli") - } - kotlin.srcDir "src" - kotlin.srcDir "../shared/src/main/kotlin" - kotlin.srcDir "$toolsPath/benchmarks/shared/src/main/kotlin" - } - macosMain { - dependsOn commonMain - kotlin.srcDir "../shared/src/main/kotlin-native/common" - kotlin.srcDir "../shared/src/main/kotlin-native/posix" - - // Exclude kotlinx.cli added in commonMain dependencies and inherited by macosMain. - configurations.getByName(implementationConfigurationName) { - exclude module: "kotlinx.cli" - } - } - } - - macosX64("macos") { - compilations.main.kotlinOptions.freeCompilerArgs = (project.hasProperty('compilerArgs') ? compilerArgs.split() : []) - compilations.main.enableEndorsedLibs = true - binaries { - framework(frameworkName, [RELEASE]) - } - } -} - -MPPTools.addTimeListener(project) - -build.finalizedBy 'buildSwift' - -defaultTasks 'konanRun' - -def applicationName = "swiftInterop" -def swiftExecutable = Paths.get(buildDir.absolutePath, applicationName) - -if (PlatformInfo.isAppleTarget(HostManager.host)) { - MPPTools.createBenchmarksRunTask(project, 'konanRun') { - workingDir = project.provider { buildDir } - executable = swiftExecutable - depends("build") - args("-w", "$nativeWarmup", "-r", "$attempts", "-o", "${buildDir.absolutePath}/${nativeBenchResults}", "-p", "${applicationName}::") - } -} - -def framework = null -if (PlatformInfo.isMac()) { - framework = kotlin.targets.macos.binaries.getFramework(frameworkName, 'RELEASE') -} else { - framework = kotlin.targets.ios.binaries.getFramework(frameworkName, 'RELEASE') -} - -task buildSwift { - doLast { - def frameworkParentDirPath = framework.outputDirectory - def sources = ["$projectDir/swiftSrc/benchmarks.swift".toString(), "$projectDir/swiftSrc/main.swift".toString()] - def options = ['-g', '-Xlinker', '-rpath', '-Xlinker', frameworkParentDirPath, '-F', frameworkParentDirPath] - UtilsKt.compileSwift(project, HostManager.host, sources, options, swiftExecutable, false) - } -} - -task jvmRun(type: RunJvmTask) { - doLast { - println("JVM run is unsupported") - } -} - -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()] -} - -task konanJsonReport { - doLast { - if (PlatformInfo.isAppleTarget(HostManager.host)) { - def targetExtension = "" - if (PlatformInfo.isMac()) { - targetExtension = "Macos" - } else { - targetExtension = "Ios" - } - - def frameworkPath = framework.outputFile.absolutePath - def nativeFramework = new File("$frameworkPath/$frameworkName").canonicalPath - def nativeCompileTime = MPPTools.getNativeCompileTime(applicationName, ["compileKotlin${targetExtension}".toString(), - "link${frameworkName}ReleaseFramework${targetExtension}".toString()]) - - String benchContents = new File("${buildDir.absolutePath}/${nativeBenchResults}").text - def properties = getCommonProperties() + ['type' : 'native', - 'compilerVersion': "${konanVersion}".toString(), - 'flags' : kotlin.targets.macos.compilations.main.extraOpts.collect{ "\"$it\"" }, - 'benchmarks' : benchContents, - 'compileTime' : [nativeCompileTime], - 'codeSize' : MPPTools.getCodeSizeBenchmark(applicationName, nativeFramework)] - def output = MPPTools.createJsonReport(properties) - new File("${buildDir.absolutePath}/${nativeJson}").write(output) - } - } -} - -task jvmJsonReport { - doLast { - println("JVM run is unsupported") - } -} - -konanRun.finalizedBy 'konanJsonReport' -jvmRun.finalizedBy 'jvmJsonReport' diff --git a/performance/swiftinterop/build.gradle.kts b/performance/swiftinterop/build.gradle.kts new file mode 100644 index 00000000000..8283b8a4f54 --- /dev/null +++ b/performance/swiftinterop/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * 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("swift-benchmarking") +} + +val toolsPath = "../../tools" + +swiftBenchmark { + applicationName = "swiftInterop" + commonSrcDirs = listOf("$toolsPath/benchmarks/shared/src/main/kotlin", "src", "../shared/src/main/kotlin") + nativeSrcDirs = listOf("../shared/src/main/kotlin-native/common", "../shared/src/main/kotlin-native/posix") + swiftSources = listOf("$projectDir/swiftSrc/benchmarks.swift", "$projectDir/swiftSrc/main.swift") + compileTasks = listOf("compileKotlinNative", "linkBenchmarkReleaseFrameworkNative") + + dependencies.common(project(":endorsedLibraries:kotlinx.cli")) +} \ No newline at end of file diff --git a/performance/swiftinterop/swiftSrc/benchmarks.swift b/performance/swiftinterop/swiftSrc/benchmarks.swift index c4adc847522..7196dcf185a 100644 --- a/performance/swiftinterop/swiftSrc/benchmarks.swift +++ b/performance/swiftinterop/swiftSrc/benchmarks.swift @@ -4,7 +4,7 @@ */ import Foundation -import CityMap +import benchmark class SimpleCost: Cost { let value: Int diff --git a/performance/swiftinterop/swiftSrc/main.swift b/performance/swiftinterop/swiftSrc/main.swift index 7daa559c756..e6ac51d6253 100644 --- a/performance/swiftinterop/swiftSrc/main.swift +++ b/performance/swiftinterop/swiftSrc/main.swift @@ -4,7 +4,7 @@ */ import Foundation -import CityMap +import benchmark var runner = BenchmarksRunner() let args = KotlinArray(size: Int32(CommandLine.arguments.count - 1), init: {index in @@ -14,9 +14,7 @@ let args = KotlinArray(size: Int32(CommandLine.arguments.count - 1), init: {inde let companion = BenchmarkEntryWithInit.Companion() var swiftLauncher = SwiftLauncher() - -runner.runBenchmarks(args: args, run: { (arguments: BenchmarkArguments) -> [BenchmarkResult] in - swiftLauncher.add(name: "createMultigraphOfInt", benchmark: companion.create(ctor: { return SwiftInteropBenchmarks() }, +swiftLauncher.add(name: "createMultigraphOfInt", benchmark: companion.create(ctor: { return SwiftInteropBenchmarks() }, lambda: { ($0 as! SwiftInteropBenchmarks).createMultigraphOfInt() })) swiftLauncher.add(name: "fillCityMap", benchmark: companion.create(ctor: { return SwiftInteropBenchmarks() }, lambda: { ($0 as! SwiftInteropBenchmarks).fillCityMap() })) @@ -38,6 +36,7 @@ runner.runBenchmarks(args: args, run: { (arguments: BenchmarkArguments) -> [Benc lambda: { ($0 as! SwiftInteropBenchmarks).stringInterop() })) swiftLauncher.add(name: "simpleFunction", benchmark: companion.create(ctor: { return SwiftInteropBenchmarks() }, lambda: { ($0 as! SwiftInteropBenchmarks).simpleFunction() })) +runner.runBenchmarks(args: args, run: { (arguments: BenchmarkArguments) -> [BenchmarkResult] in if arguments is BaseBenchmarkArguments { let argumentsList: BaseBenchmarkArguments = arguments as! BaseBenchmarkArguments return swiftLauncher.launch(numWarmIterations: argumentsList.warmup,