diff --git a/HACKING.md b/HACKING.md index ba1adb20ed9..7e6c41ffd7d 100644 --- a/HACKING.md +++ b/HACKING.md @@ -103,33 +103,32 @@ To update the blackbox compiler tests set TeamCity build number in `gradle.prope To measure performance of Kotlin/Native compiler on existing benchmarks: - cd performance - ../gradlew konanRun + ./gradlew :performance:konanRun **konanRun** task can be run separately for one/several benchmark applications: - ../gradlew :cinterop:konanRun + ./gradlew :performance:cinterop:konanRun **konanRun** task has parameter `filter` which allows to run only some subset of benchmarks: - ../gradlew :cinterop:konanRun --filter=struct,macros + ./gradlew :performance:cinterop:konanRun --filter=struct,macros Or you can use `filterRegex` if you want to specify the filter as regexes: - ../gradlew :ring:konanRun --filterRegex=String.*,Loop.* + ./gradlew :performance:ring:konanRun --filterRegex=String.*,Loop.* There are also tasks for running benchmarks on JVM (pay attention, some benchmarks e.g. cinterop benchmarks can't be run on JVM) - ../gradlew jvmRun + ./gradlew :performance:jvmRun Files with results of benchmarks run are saved in `performance/build/nativeReport.json` for konanRun and `jvmReport.json` for jvmRun. You can change the output filename by setting the `nativeJson` property for konanRun and `jvmJson` for jvmRun: - ../gradlew :ring:konanRun --filter=String.*,Loop.* -PnativeJson=stringsAndLoops.json + ./gradlew :performance:ring:konanRun --filter=String.*,Loop.* -PnativeJson=stringsAndLoops.json You can use the `compilerArgs` property to pass flags to the compiler used to compile the benchmarks: - ../gradlew konanRun -PcompilerArgs="--time -g" + ./gradlew :performance:konanRun -PcompilerArgs="--time -g" To compare different results run benchmarksAnalyzer tool: diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 53eff618026..4dffd6acd58 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3098,7 +3098,7 @@ kotlinNativeInterop { defFile 'interop/basics/ccallbacksAndVarargs.def' } - if (isAppleTarget(project)) { + if (PlatformInfo.isAppleTarget(project)) { objcSmoke { defFile 'interop/objc/objcSmoke.def' headers "$projectDir/interop/objc/smoke.h" @@ -3447,7 +3447,7 @@ task runKonanRegexTests(type: RunKonanTest) { task coverage_basic_program(type: RunStandaloneKonanTest) { - disabled = getTarget(project).name != "ios_x64" && getTarget(project).name != "macos_x64" + disabled = PlatformInfo.getTarget(project).name != "ios_x64" && PlatformInfo.getTarget(project).name != "macos_x64" description = "Test that `-Xcoverage` generates correct __llvm_coverage information" @@ -3476,7 +3476,7 @@ task coverage_basic_program(type: RunStandaloneKonanTest) { task coverage_basic_library(type: RunStandaloneKonanTest) { - disabled = getTarget(project).name != "ios_x64" && getTarget(project).name != "macos_x64" + disabled = PlatformInfo.getTarget(project).name != "ios_x64" && PlatformInfo.getTarget(project).name != "macos_x64" description = "Test that `-Xlibrary-to-cover` generates correct __llvm_coverage information" @@ -3486,7 +3486,7 @@ task coverage_basic_library(type: RunStandaloneKonanTest) { doFirst { def konancScript = isWindows() ? "konanc.bat" : "konanc" def konanc = "$dist/bin/$konancScript" - "$konanc -target ${getTarget(project).name} $projectDir/coverage/basic/library/library.kt -p library -o $dir/lib_to_cover".execute().waitFor() + "$konanc -target ${PlatformInfo.getTarget(project).name} $projectDir/coverage/basic/library/library.kt -p library -o $dir/lib_to_cover".execute().waitFor() } flags = ["-Xcoverage-file=$dir/program.profraw", "-l", "$dir/lib_to_cover", "-Xlibrary-to-cover=$dir/lib_to_cover.klib", "-entry", "coverage.basic.library.main"] diff --git a/build.gradle b/build.gradle index f4db84c7e5b..1bf31937c81 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.konan.util.* import org.jetbrains.kotlin.CopySamples import org.jetbrains.kotlin.CopyCommonSources +import org.jetbrains.kotlin.PlatformInfo import org.jetbrains.kotlin.konan.* buildscript { @@ -41,7 +42,7 @@ wrapper.distributionType = Wrapper.DistributionType.ALL defaultTasks 'clean', 'dist' -convention.plugins.platformInfo = new PlatformInfo() +convention.plugins.platformInfo = PlatformInfo ext { distDir = file('dist') @@ -161,43 +162,6 @@ void loadCommandLineProperties() { ext.testTarget = project.hasProperty("test_target") ? ext.test_target : null } -class PlatformInfo { - boolean isMac() { - return HostManager.hostIsMac - } - - boolean isWindows() { - return HostManager.hostIsMingw - } - - boolean isLinux() { - return HostManager.hostIsLinux - } - - boolean isAppleTarget(Project project) { - def target = getTarget(project) - return target.family == Family.IOS || target.family == Family.OSX - } - - boolean isWindowsTarget(Project project) { - return getTarget(project).family == Family.MINGW - } - - boolean isWasmTarget(Project project) { - return getTarget(project).family == Family.WASM - } - - KonanTarget getTarget(Project project) { - def platformManager = project.rootProject.platformManager - def targetName = project.project.testTarget ?: 'host' - return platformManager.targetManager(targetName).target - } - - Throwable unsupportedPlatformException() { - return new TargetSupportException() - } -} - configurations { ftpAntTask kotlinCommonSources @@ -343,7 +307,7 @@ task distCompiler(type: Copy) { from(file('cmd')) { fileMode(0755) into('bin') - if (!isWindows()) { + if (!PlatformInfo.isWindows()) { exclude('**/*.bat') } } @@ -501,6 +465,12 @@ task samples { dependsOn 'samplesZip', 'samplesTar' } +task performance { + dependsOn 'dist' + dependsOn ':performance:clean' + dependsOn ':performance:konanRun' +} + task samplesZip(type: Zip) task samplesTar(type: Tar) { extension = 'tar.gz' @@ -562,12 +532,6 @@ task uploadBundle { } } -// TODO return when problems with HostManagers will be solved -/*task performance { - dependsOn 'dist' - dependsOn ':performance:bench' -}*/ - task teamcityKonanVersion { doLast { println("##teamcity[setParameter name='kotlin.native.version.base' value='$konanVersion']") @@ -583,4 +547,4 @@ task clean { delete distDir delete bundle.outputs.files } -} +} \ No newline at end of file diff --git a/buildSrc/plugins/build.gradle b/buildSrc/plugins/build.gradle index 4659de6c244..aa19e2a6c79 100644 --- a/buildSrc/plugins/build.gradle +++ b/buildSrc/plugins/build.gradle @@ -23,6 +23,7 @@ buildscript { apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" dependencies{ classpath "org.jetbrains.kotlin:kotlin-serialization:$buildKotlinVersion" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" } } @@ -37,13 +38,15 @@ dependencies { compile localGroovy() compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion" compile "org.jetbrains.kotlin:kotlin-reflect:$buildKotlinVersion" - compile group: 'com.ullink.slack', name: 'simpleslackapi', version: '0.6.0' + compile group: 'com.ullink.slack', name: 'simpleslackapi', version: '1.2.0' // An artifact from the included build 'shared' cannot be used here due to https://github.com/gradle/gradle/issues/3768 - // TODO: Remove this hack when the bug is fixed + implementation "org.jetbrains.kotlin:kotlin-gradle-plugin" compile project(':shared') compile "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.10.0" } +sourceSets.main.kotlin.srcDirs = ["src", "$projectDir/../../tools/benchmarks/shared/src"] + rootProject.dependencies { runtime project(path) } diff --git a/performance/buildSrc/src/main/kotlin/BuildRegister.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/BuildRegister.kt similarity index 99% rename from performance/buildSrc/src/main/kotlin/BuildRegister.kt rename to buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/BuildRegister.kt index 4e27af10d79..3d2611cdad6 100644 --- a/performance/buildSrc/src/main/kotlin/BuildRegister.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/BuildRegister.kt @@ -3,6 +3,8 @@ * that can be found in the license/LICENSE.txt file. */ +package org.jetbrains.kotlin + import groovy.lang.Closure import org.gradle.api.Action import org.gradle.api.DefaultTask diff --git a/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt index 2f6dbdc31f8..3cd7abcb2d6 100644 --- a/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/FrameworkTest.kt @@ -78,7 +78,7 @@ open class FrameworkTest : DefaultTask() { listOf(provider.toString(), swiftMain) val options = listOf("-g", "-Xlinker", "-rpath", "-Xlinker", frameworkParentDirPath, "-F", frameworkParentDirPath) val testExecutable = Paths.get(testOutput, frameworkName, "swiftTestExecutable") - swiftc(sources, options, testExecutable) + compileSwift(project, project.testTarget(), sources, options, testExecutable, fullBitcode) runTest(testExecutable) } @@ -117,36 +117,6 @@ open class FrameworkTest : DefaultTask() { check(exitCode == 0, { "Execution failed with exit code: $exitCode "}) } - private fun swiftc(sources: List, options: List, output: Path) { - val target = project.testTarget() - val platform = project.platformManager().platform(target) - assert(platform.configurables is AppleConfigurables) - val configs = platform.configurables as AppleConfigurables - val compiler = configs.absoluteTargetToolchain + "/usr/bin/swiftc" - - val swiftTarget = when (target) { - KonanTarget.IOS_X64 -> "x86_64-apple-ios" + configs.osVersionMin - KonanTarget.IOS_ARM64 -> "arm64_64-apple-ios" + configs.osVersionMin - KonanTarget.MACOS_X64 -> "x86_64-apple-macosx" + configs.osVersionMin - else -> throw IllegalStateException("Test target $target is not supported") - } - - val args = listOf("-sdk", configs.absoluteTargetSysRoot, "-target", swiftTarget) + - options + "-o" + output.toString() + sources + - if (fullBitcode) listOf("-embed-bitcode", "-Xlinker", "-bitcode_verify") else listOf("-embed-bitcode-marker") - - val (stdOut, stdErr, exitCode) = runProcess(executor = localExecutor(project), executable = compiler, args = args) - - println(""" - |$compiler finished with exit code: $exitCode - |options: ${args.joinToString(separator = " ")} - |stdout: $stdOut - |stderr: $stdErr - """.trimMargin()) - check(exitCode == 0, { "Compilation failed" }) - check(output.toFile().exists(), { "Compiler swiftc hasn't produced an output file: $output" }) - } - private fun validateBitcodeEmbedding(frameworkBinary: String) { // Check only the full bitcode embedding for now. if (!fullBitcode) { diff --git a/performance/buildSrc/src/main/kotlin/Internals.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/Internals.kt similarity index 98% rename from performance/buildSrc/src/main/kotlin/Internals.kt rename to buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/Internals.kt index 466f15e47f5..bd31f748882 100644 --- a/performance/buildSrc/src/main/kotlin/Internals.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/Internals.kt @@ -3,6 +3,8 @@ * that can be found in the license/LICENSE.txt file. */ +package org.jetbrains.kotlin + import org.gradle.api.NamedDomainObjectCollection import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project diff --git a/performance/buildSrc/src/main/kotlin/MPPTools.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/MPPTools.kt similarity index 89% rename from performance/buildSrc/src/main/kotlin/MPPTools.kt rename to buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/MPPTools.kt index 7e8971e3d73..4f6fb5c033c 100644 --- a/performance/buildSrc/src/main/kotlin/MPPTools.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/MPPTools.kt @@ -5,11 +5,14 @@ @file:JvmName("MPPTools") +package org.jetbrains.kotlin + import groovy.lang.Closure import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.tasks.TaskState import org.gradle.api.execution.TaskExecutionListener +import org.gradle.api.tasks.AbstractExecTask import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.report.* @@ -27,16 +30,6 @@ import java.util.Base64 * This file includes short-cuts that may potentially be implemented in Kotlin MPP Gradle plugin in the future. */ -// Short-cuts for detecting the host OS. -@get:JvmName("isMacos") -val isMacos by lazy { hostOs == "Mac OS X" } - -@get:JvmName("isWindows") -val isWindows by lazy { hostOs.startsWith("Windows") } - -@get:JvmName("isLinux") -val isLinux by lazy { hostOs == "Linux" } - // Short-cuts for mostly used paths. @get:JvmName("mingwPath") val mingwPath by lazy { System.getenv("MINGW64_DIR") ?: "c:/msys64/mingw64" } @@ -57,9 +50,9 @@ fun defaultHostPreset( throw Exception("Preset whitelist must not be empty in Kotlin/Native ${subproject.displayName}.") val presetCandidate = when { - isMacos -> subproject.kotlin.presets.macosX64 - isLinux -> subproject.kotlin.presets.linuxX64 - isWindows -> subproject.kotlin.presets.mingwX64 + PlatformInfo.isMac() -> subproject.kotlin.presets.macosX64 + PlatformInfo.isLinux() -> subproject.kotlin.presets.linuxX64 + PlatformInfo.isWindows() -> subproject.kotlin.presets.mingwX64 else -> null } @@ -70,9 +63,9 @@ fun defaultHostPreset( } fun getNativeProgramExtension(): String = when { - isMacos -> ".kexe" - isLinux -> ".kexe" - isWindows -> ".exe" + PlatformInfo.isMac() -> ".kexe" + PlatformInfo.isLinux() -> ".kexe" + PlatformInfo.isWindows() -> ".exe" else -> error("Unknown host") } @@ -161,10 +154,21 @@ fun sendUploadRequest(url: String, fileName: String, username: String? = null, p fun createRunTask( subproject: Project, name: String, - target: KotlinNativeTarget, + runTask: AbstractExecTask<*>, configureClosure: Closure? = null ): Task { - val task = subproject.tasks.create(name, RunKotlinNativeTask::class.java, target) + val task = subproject.tasks.create(name, RunKotlinNativeTask::class.java, runTask) + task.configure(configureClosure ?: task.emptyConfigureClosure()) + return task +} + +@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 } @@ -174,7 +178,7 @@ fun getJvmCompileTime(programName: String): BenchmarkResult = @JvmOverloads fun getNativeCompileTime(programName: String, - tasks: List = listOf("compileKotlinNative", "linkBenchmarkReleaseExecutableNative")): BenchmarkResult = + tasks: List = listOf("linkBenchmarkReleaseExecutableNative")): BenchmarkResult = TaskTimerListener.getBenchmarkResult(programName, tasks) fun getCompileBenchmarkTime(programName: String, tasksNames: Iterable, repeats: Int, exitCodes: Map) = @@ -207,7 +211,7 @@ class TaskTimerListener: TaskExecutionListener { fun getTime(taskName: String) = tasksTimes[taskName] ?: 0.0 } - private var startTime = System.currentTimeMillis() + private var startTime = System.nanoTime() override fun beforeExecute(task: Task) { startTime = System.nanoTime() diff --git a/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/PlatformInfo.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/PlatformInfo.kt new file mode 100644 index 00000000000..54e134c2c3c --- /dev/null +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/PlatformInfo.kt @@ -0,0 +1,41 @@ +package org.jetbrains.kotlin + +import org.jetbrains.kotlin.konan.target.* +import org.jetbrains.kotlin.konan.util.* +import org.gradle.api.Project + +object PlatformInfo { + @JvmStatic + fun isMac() = HostManager.hostIsMac + @JvmStatic + fun isWindows() = HostManager.hostIsMingw + @JvmStatic + fun isLinux() = HostManager.hostIsLinux + + @JvmStatic + fun isAppleTarget(project: Project): Boolean { + val target = getTarget(project) + return target.family == Family.IOS || target.family == Family.OSX + } + + @JvmStatic + fun isAppleTarget(target: KonanTarget): Boolean { + return target.family == Family.IOS || target.family == Family.OSX + } + + @JvmStatic + fun isWindowsTarget(project: Project) = getTarget(project).family == Family.MINGW + + @JvmStatic + fun isWasmTarget(project: Project) = + getTarget(project).family == Family.WASM + + @JvmStatic + fun getTarget(project: Project): KonanTarget { + val platformManager = project.rootProject.platformManager() + val targetName = project.project.testTarget().name + return platformManager.targetManager(targetName).target + } + + fun unsupportedPlatformException() = TargetSupportException() +} \ No newline at end of file diff --git a/performance/buildSrc/src/main/kotlin/RegressionsReporter.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RegressionsReporter.kt similarity index 99% rename from performance/buildSrc/src/main/kotlin/RegressionsReporter.kt rename to buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RegressionsReporter.kt index 84b42889225..f9370894194 100644 --- a/performance/buildSrc/src/main/kotlin/RegressionsReporter.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RegressionsReporter.kt @@ -2,6 +2,7 @@ * 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.Action @@ -37,7 +38,6 @@ import java.util.Properties */ open class RegressionsReporter : DefaultTask() { - val slackUsers = mapOf( "olonho" to "nikolay.igotti", "nikolay.igotti" to "nikolay.igotti", diff --git a/performance/buildSrc/src/main/kotlin/RegressionsSummaryReporter.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RegressionsSummaryReporter.kt similarity index 99% rename from performance/buildSrc/src/main/kotlin/RegressionsSummaryReporter.kt rename to buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RegressionsSummaryReporter.kt index f4a70b3460e..aaaf4447d96 100644 --- a/performance/buildSrc/src/main/kotlin/RegressionsSummaryReporter.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RegressionsSummaryReporter.kt @@ -3,6 +3,8 @@ * that can be found in the license/LICENSE.txt file. */ +package org.jetbrains.kotlin + import groovy.lang.Closure import org.gradle.api.Action import org.gradle.api.DefaultTask diff --git a/performance/buildSrc/src/main/kotlin/RunKotlinNativeTask.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunBenchmarksExecutableTask.kt similarity index 79% rename from performance/buildSrc/src/main/kotlin/RunKotlinNativeTask.kt rename to buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunBenchmarksExecutableTask.kt index dbbe194aca7..57f77e8b1a2 100644 --- a/performance/buildSrc/src/main/kotlin/RunKotlinNativeTask.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunBenchmarksExecutableTask.kt @@ -3,23 +3,21 @@ * 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 org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import javax.inject.Inject import java.io.File -open class RunKotlinNativeTask @Inject constructor( - private val curTarget: KotlinNativeTarget -) : DefaultTask() { - - var buildType = "RELEASE" +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 = "" @@ -40,7 +38,6 @@ open class RunKotlinNativeTask @Inject constructor( override fun configure(configureClosure: Closure): Task { val task = super.configure(configureClosure) - this.dependsOn += curTarget.binaries.getExecutable("benchmark", buildType).linkTaskName return task } @@ -52,7 +49,7 @@ open class RunKotlinNativeTask @Inject constructor( val filterArgs = filter.splitCommaSeparatedOption("-f") val filterRegexArgs = filterRegex.splitCommaSeparatedOption("-fr") project.exec { - it.executable = curTarget.binaries.getExecutable("benchmark", buildType).outputFile.getAbsolutePath() + it.executable = executable it.args = curArgs + filterArgs + filterRegexArgs it.environment = curEnvironment it.workingDir(workingDir) @@ -70,8 +67,8 @@ open class RunKotlinNativeTask @Inject constructor( } internal fun emptyConfigureClosure() = object : Closure(this) { - override fun call(): RunKotlinNativeTask { - return this@RunKotlinNativeTask + override fun call(): RunBenchmarksExecutableTask { + return this@RunBenchmarksExecutableTask } } -} +} \ No newline at end of file diff --git a/performance/buildSrc/src/main/kotlin/RunJvmTask.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunJvmTask.kt similarity index 97% rename from performance/buildSrc/src/main/kotlin/RunJvmTask.kt rename to buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunJvmTask.kt index 463feb0762f..372ebbe3a07 100644 --- a/performance/buildSrc/src/main/kotlin/RunJvmTask.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunJvmTask.kt @@ -3,6 +3,8 @@ * that can be found in the license/LICENSE.txt file. */ +package org.jetbrains.kotlin + import groovy.lang.Closure import org.gradle.api.tasks.JavaExec import org.gradle.api.Task diff --git a/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunKotlinNativeTask.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunKotlinNativeTask.kt new file mode 100644 index 00000000000..34329187c25 --- /dev/null +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/RunKotlinNativeTask.kt @@ -0,0 +1,55 @@ +/* + * 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.AbstractExecTask +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.options.Option +import org.gradle.api.tasks.Input +import javax.inject.Inject + +open class RunKotlinNativeTask @Inject constructor( + private val runTask: AbstractExecTask<*> +) : DefaultTask() { + @Input + var buildType = "RELEASE" + @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 = "" + + override fun configure(configureClosure: Closure): Task { + val task = super.configure(configureClosure) + this.finalizedBy(runTask.name) + runTask.finalizedBy("konanJsonReport") + return task + } + + fun depends(taskName: String) { + this.dependsOn += taskName + } + + @TaskAction + fun run() { + runTask.run { + val filterArgs = filter.splitCommaSeparatedOption("-f") + val filterRegexArgs = filterRegex.splitCommaSeparatedOption("-fr") + runTask.args(filterArgs) + runTask.args(filterRegexArgs) + } + } + + internal fun emptyConfigureClosure() = object : Closure(this) { + override fun call(): RunKotlinNativeTask { + return this@RunKotlinNativeTask + } + } +} diff --git a/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/Utils.kt b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/Utils.kt index cf810fd0e56..c4d85e7dc01 100644 --- a/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/Utils.kt +++ b/buildSrc/plugins/src/main/kotlin/org/jetbrains/kotlin/Utils.kt @@ -1,10 +1,21 @@ package org.jetbrains.kotlin import org.gradle.api.Project +import org.jetbrains.kotlin.konan.target.AppleConfigurables import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.PlatformManager +import java.io.FileInputStream +import java.io.IOException +import java.io.File +import java.util.concurrent.TimeUnit +import java.net.HttpURLConnection +import java.net.URL +import java.util.Base64 +import org.jetbrains.report.json.* +import java.nio.file.Path + fun Project.platformManager() = findProperty("platformManager") as PlatformManager fun Project.testTarget() = findProperty("target") as KonanTarget @@ -12,13 +23,126 @@ fun Project.testTarget() = findProperty("target") as KonanTarget * Ad-hoc signing of the specified path */ fun codesign(project: Project, path: String) { - check(HostManager.hostIsMac, { "Apple specific code signing" } ) + check(HostManager.hostIsMac, { "Apple specific code signing" }) val (stdOut, stdErr, exitCode) = runProcess(executor = localExecutor(project), executable = "/usr/bin/codesign", args = listOf("--verbose", "-s", "-", path)) - check(exitCode == 0, { """ + check(exitCode == 0, { + """ |Codesign failed with exitCode: $exitCode |stdout: $stdOut |stderr: $stdErr """.trimMargin() }) +} + +// Run command line from string. +fun Array.runCommand(workingDir: File = File("."), + timeoutAmount: Long = 60, + timeoutUnit: TimeUnit = TimeUnit.SECONDS): String { + return try { + ProcessBuilder(*this) + .directory(workingDir) + .redirectOutput(ProcessBuilder.Redirect.PIPE) + .redirectError(ProcessBuilder.Redirect.PIPE) + .start().apply { + waitFor(timeoutAmount, timeoutUnit) + }.inputStream.bufferedReader().readText() + } catch (e: IOException) { + error("Couldn't run command $this") + } +} + +fun String.splitCommaSeparatedOption(optionName: String) = + split("\\s*,\\s*".toRegex()).map { + if (it.isNotEmpty()) listOf(optionName, it) else listOf(null) + }.flatten().filterNotNull() + +data class Commit(val revision: String, val developer: String, val webUrlWithDescription: String) + +val teamCityUrl = "http://buildserver.labs.intellij.net" + +// List of commits. +class CommitsList(data: JsonElement): ConvertedFromJson { + + val commits: List + + init { + if (data !is JsonObject) { + error("Commits description is expected to be a json object!") + } + val changesElement = data.getOptionalField("change") + commits = changesElement?.let { + if (changesElement !is JsonArray) { + error("Change field is expected to be an array. Please, check source.") + } + changesElement.jsonArray.map { + with(it as JsonObject) { + Commit(elementToString(getRequiredField("version"), "version"), + elementToString(getRequiredField("username"), "username"), + elementToString(getRequiredField("webUrl"), "webUrl") + ) + } + } + } ?: listOf() + } +} + +fun buildsUrl(buildLocator: String) = + "$teamCityUrl/app/rest/builds/?locator=$buildLocator" + +fun getBuild(buildLocator: String, user: String, password: String) = + try { + sendGetRequest(buildsUrl(buildLocator), user, password) + } catch (t: Throwable) { + error("Try to get build! TeamCity is unreachable!") + } + +fun sendGetRequest(url: String, username: String? = null, password: String? = null) : String { + val connection = URL(url).openConnection() as HttpURLConnection + if (username != null && password != null) { + val auth = Base64.getEncoder().encode((username + ":" + password).toByteArray()).toString(Charsets.UTF_8) + connection.addRequestProperty("Authorization", "Basic $auth") + } + connection.setRequestProperty("Accept", "application/json"); + connection.connect() + return connection.inputStream.use { it.reader().use { reader -> reader.readText() } } +} + +fun getBuildProperty(buildJsonDescription: String, property: String) = + with(JsonTreeParser.parse(buildJsonDescription) as JsonObject) { + if (getPrimitive("count").int == 0) { + error("No build information on TeamCity for $buildJsonDescription!") + } + (getArray("build").getObject(0).getPrimitive(property) as JsonLiteral).unquoted() + } + +@JvmOverloads +fun compileSwift(project: Project, target: KonanTarget, sources: List, options: List, + output: Path, fullBitcode: Boolean = false) { + val platform = project.platformManager().platform(target) + assert(platform.configurables is AppleConfigurables) + val configs = platform.configurables as AppleConfigurables + val compiler = configs.absoluteTargetToolchain + "/usr/bin/swiftc" + + val swiftTarget = when (target) { + KonanTarget.IOS_X64 -> "x86_64-apple-ios" + configs.osVersionMin + KonanTarget.IOS_ARM64 -> "arm64_64-apple-ios" + configs.osVersionMin + KonanTarget.MACOS_X64 -> "x86_64-apple-macosx" + configs.osVersionMin + else -> throw IllegalStateException("Test target $target is not supported") + } + + val args = listOf("-sdk", configs.absoluteTargetSysRoot, "-target", swiftTarget) + + options + "-o" + output.toString() + sources + + if (fullBitcode) listOf("-embed-bitcode", "-Xlinker", "-bitcode_verify") else listOf("-embed-bitcode-marker") + + val (stdOut, stdErr, exitCode) = runProcess(executor = localExecutor(project), executable = compiler, args = args) + + println(""" + |$compiler finished with exit code: $exitCode + |options: ${args.joinToString(separator = " ")} + |stdout: $stdOut + |stderr: $stdErr + """.trimMargin()) + check(exitCode == 0, { "Compilation failed" }) + check(output.toFile().exists(), { "Compiler swiftc hasn't produced an output file: $output" }) } \ No newline at end of file diff --git a/libclangext/build.gradle b/libclangext/build.gradle index 32d3629a37e..e72fede05ea 100644 --- a/libclangext/build.gradle +++ b/libclangext/build.gradle @@ -27,7 +27,8 @@ buildscript { } } import org.jetbrains.kotlin.konan.target.ClangArgs -ext.isEnabled = isMac() +import org.jetbrains.kotlin.PlatformInfo +ext.isEnabled = PlatformInfo.isMac() model { components { diff --git a/performance/build.gradle b/performance/build.gradle index c6e2b9fa54e..e3c7167e449 100644 --- a/performance/build.gradle +++ b/performance/build.gradle @@ -1,3 +1,8 @@ +import org.jetbrains.kotlin.RegressionsReporter +import org.jetbrains.kotlin.RegressionsSummaryReporter +import org.jetbrains.kotlin.BuildRegister +import org.jetbrains.kotlin.MPPTools + buildscript { ext.rootBuildDirectory = file('..') @@ -42,16 +47,17 @@ defaultTasks 'konanRun' // Produce and send slack report. task slackReport(type: RegressionsReporter) { + def analyzerBinary = MPPTools.findFile("${analyzerTool}${MPPTools.getNativeProgramExtension()}", + "${rootBuildDirectory}/${analyzerToolDirectory}") def teamcityConfig = System.getenv("TEAMCITY_BUILD_PROPERTIES_FILE") - if (teamcityConfig) { + if (teamcityConfig && analyzerBinary != null) { // Create folder for report (root Kotlin project settings make create report in separate folder). def reportDirectory = new File(outputReport).parentFile mkdir reportDirectory def targetsResults = new File(new File("${rootBuildDirectory}"), "targetsResults").toString() mkdir targetsResults currentBenchmarksReportFile = "${buildDir.absolutePath}/${nativeJson}" - analyzer = MPPTools.findFile("${analyzerTool}${MPPTools.getNativeProgramExtension()}", - "${rootBuildDirectory}/${analyzerToolDirectory}") + analyzer = analyzerBinary htmlReport = outputReport defaultBranch = project.findProperty('kotlin.native.default.branch') ?: "master" def target = System.getProperty("os.name") @@ -138,4 +144,39 @@ task teamCityStat(type:Exec) { } else { println("No analyzer $analyzerTool found in subdirectories of ${rootBuildDirectory}/${analyzerToolDirectory}") } +} + +task сinterop { + dependsOn 'clean' + dependsOn 'сinterop:konanRun' +} + +task framework { + dependsOn 'clean' + dependsOn 'framework:konanRun' +} + +task helloworld { + dependsOn 'clean' + dependsOn 'helloworld:konanRun' +} + +task objcinterop { + dependsOn 'clean' + dependsOn 'objcinterop:konanRun' +} + +task ring { + dependsOn 'clean' + dependsOn 'ring:konanRun' +} + +task swiftinterop { + dependsOn 'clean' + dependsOn 'swiftinterop:konanRun' +} + +task videoplayer { + dependsOn 'clean' + dependsOn 'swiftinterop:konanRun' } \ No newline at end of file diff --git a/performance/buildSrc/build.gradle b/performance/buildSrc/build.gradle deleted file mode 100644 index 260a959986a..00000000000 --- a/performance/buildSrc/build.gradle +++ /dev/null @@ -1,36 +0,0 @@ -buildscript { - repositories { - maven { - url 'https://cache-redirector.jetbrains.com/maven-central' - } - maven { - url kotlinCompilerRepo - } - jcenter() - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - } -} - -apply plugin: 'kotlin' - -repositories { - maven { - url kotlinCompilerRepo - } - maven { - url buildKotlinCompilerRepo - } - jcenter() -} - -dependencies { - compileOnly gradleApi() - implementation "org.jetbrains.kotlin:kotlin-gradle-plugin" - implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion" - compile group: 'com.ullink.slack', name: 'simpleslackapi', version: '1.2.0' -} - -sourceSets.main.kotlin.srcDirs = ["src", "$projectDir/../../tools/benchmarks/shared/src"] \ No newline at end of file diff --git a/performance/buildSrc/settings.gradle b/performance/buildSrc/settings.gradle deleted file mode 100644 index 5625fa86bbb..00000000000 --- a/performance/buildSrc/settings.gradle +++ /dev/null @@ -1,21 +0,0 @@ -// Reuse Kotlin version from the root project. -File rootProjectGradlePropertiesFile = file("${rootProject.projectDir}/../../gradle.properties") -if (!rootProjectGradlePropertiesFile.isFile()) { - throw new Exception("File $rootProjectGradlePropertiesFile does not exist or is not a file") -} - -Properties rootProjectProperties = new Properties() -rootProjectGradlePropertiesFile.withInputStream { inputStream -> - rootProjectProperties.load(inputStream) - if (!rootProjectProperties.containsKey('kotlinVersion')) { - throw new Exception("No 'kotlinVersion' property in $rootProjectGradlePropertiesFile file") - } -} - -gradle.beforeProject { project -> - rootProjectProperties.forEach { String key, value -> - if (!project.hasProperty(key)) - project.ext[key] = value - } - -} \ No newline at end of file diff --git a/performance/buildSrc/src/main/kotlin/Utils.kt b/performance/buildSrc/src/main/kotlin/Utils.kt deleted file mode 100644 index b81354377c3..00000000000 --- a/performance/buildSrc/src/main/kotlin/Utils.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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/LICENSE.txt file. - */ - -import java.io.FileInputStream -import java.io.IOException -import java.io.File -import java.util.concurrent.TimeUnit -import java.net.HttpURLConnection -import java.net.URL -import java.util.Base64 -import org.jetbrains.report.json.* - -// Run command line from string. -fun Array.runCommand(workingDir: File = File("."), - timeoutAmount: Long = 60, - timeoutUnit: TimeUnit = TimeUnit.SECONDS): String { - return try { - ProcessBuilder(*this) - .directory(workingDir) - .redirectOutput(ProcessBuilder.Redirect.PIPE) - .redirectError(ProcessBuilder.Redirect.PIPE) - .start().apply { - waitFor(timeoutAmount, timeoutUnit) - }.inputStream.bufferedReader().readText() - } catch (e: IOException) { - error("Couldn't run command $this") - } -} - -fun String.splitCommaSeparatedOption(optionName: String) = - split("\\s*,\\s*".toRegex()).map { - if (it.isNotEmpty()) listOf(optionName, it) else listOf(null) - }.flatten().filterNotNull() - -data class Commit(val revision: String, val developer: String, val webUrlWithDescription: String) - -val teamCityUrl = "http://buildserver.labs.intellij.net" - -// List of commits. -class CommitsList(data: JsonElement): ConvertedFromJson { - - val commits: List - - init { - if (data !is JsonObject) { - error("Commits description is expected to be a json object!") - } - val changesElement = data.getOptionalField("change") - commits = changesElement?.let { - if (changesElement !is JsonArray) { - error("Change field is expected to be an array. Please, check source.") - } - changesElement.jsonArray.map { - with(it as JsonObject) { - Commit(elementToString(getRequiredField("version"), "version"), - elementToString(getRequiredField("username"), "username"), - elementToString(getRequiredField("webUrl"), "webUrl") - ) - } - } - } ?: listOf() - } -} - -fun buildsUrl(buildLocator: String) = - "$teamCityUrl/app/rest/builds/?locator=$buildLocator" - -fun getBuild(buildLocator: String, user: String, password: String) = - try { - sendGetRequest(buildsUrl(buildLocator), user, password) - } catch (t: Throwable) { - error("Try to get build! TeamCity is unreachable!") - } - -fun sendGetRequest(url: String, username: String? = null, password: String? = null) : String { - val connection = URL(url).openConnection() as HttpURLConnection - if (username != null && password != null) { - val auth = Base64.getEncoder().encode((username + ":" + password).toByteArray()).toString(Charsets.UTF_8) - connection.addRequestProperty("Authorization", "Basic $auth") - } - connection.setRequestProperty("Accept", "application/json"); - connection.connect() - return connection.inputStream.use { it.reader().use { reader -> reader.readText() } } -} - -fun getBuildProperty(buildJsonDescription: String, property: String) = - with(JsonTreeParser.parse(buildJsonDescription) as JsonObject) { - if (getPrimitive("count").int == 0) { - error("No build information on TeamCity for $buildJsonDescription!") - } - (getArray("build").getObject(0).getPrimitive(property) as JsonLiteral).unquoted() - } diff --git a/performance/cinterop/build.gradle b/performance/cinterop/build.gradle index e804c59d6cf..7224630f6bf 100644 --- a/performance/cinterop/build.gradle +++ b/performance/cinterop/build.gradle @@ -3,9 +3,8 @@ project.ext { 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}/gradle/benchmark.gradle") +apply from: rootProject.file("${rootProject.rootDir}/performance/gradle/benchmark.gradle") kotlin.targets.native.compilations.main.cinterops { macros struct diff --git a/performance/framework/build.gradle b/performance/framework/build.gradle index 4f5b787129a..059d05e98b7 100644 --- a/performance/framework/build.gradle +++ b/performance/framework/build.gradle @@ -1,3 +1,7 @@ +import org.jetbrains.kotlin.MPPTools +import org.jetbrains.kotlin.PlatformInfo +import org.jetbrains.kotlin.RunJvmTask + buildscript { ext.rootBuildDirectory = file('../..') @@ -64,7 +68,7 @@ kotlin { MPPTools.addTimeListener(project) task konanRun { - if (MPPTools.isMacos()) { + if (PlatformInfo.isMac()) { dependsOn 'build' } } @@ -77,7 +81,7 @@ task jvmRun(type: RunJvmTask) { task konanJsonReport { doLast { - if (MPPTools.isMacos()) { + if (PlatformInfo.isMac()) { def applicationName = "FrameworkBenchmarksAnalyzer" def frameworkPath = kotlin.macosX64("macos").binaries. getFramework(frameworkName, kotlin.macosX64("macos").binaries.RELEASE).outputFile.absolutePath diff --git a/performance/gradle/benchmark.gradle b/performance/gradle/benchmark.gradle index d63fd9676a3..05b538631c2 100644 --- a/performance/gradle/benchmark.gradle +++ b/performance/gradle/benchmark.gradle @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.MPPTools +import org.jetbrains.kotlin.RunJvmTask + apply plugin: 'kotlin-multiplatform' repositories { @@ -54,6 +57,7 @@ kotlin { } } + jvm() { compilations.all { tasks[compileKotlinTaskName].kotlinOptions { @@ -68,23 +72,20 @@ kotlin { compilations.main.extraOpts = (project.hasProperty('compilerArgs') ? compilerArgs.split() : []) + '-opt' binaries { executable('benchmark', [RELEASE]) { - if (org.gradle.internal.os.OperatingSystem.current().isWindows()) { - linkerOpts = ["-L${getMingwPath()}/lib".toString()] + 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) { - workingDir = project.provider { - kotlin.targets.native.binaries.getExecutable("benchmark", buildType).outputDirectory - } - depends("build") - args("-w", "$nativeWarmup", "-r", "$attempts", "-o", "${buildDir.absolutePath}/${nativeBenchResults}", "-p", "${project.ext.applicationName}::") -} +MPPTools.createRunTask(project, 'konanRun', kotlin.targets.native.binaries.getExecutable("benchmark", "RELEASE").runTask) task jvmRun(type: RunJvmTask) { dependsOn 'build' @@ -129,7 +130,6 @@ task jvmJsonReport { } jvmRun.finalizedBy jvmJsonReport -konanRun.finalizedBy konanJsonReport private def getCommonProperties() { return ['cpu': System.getProperty("os.arch"), diff --git a/performance/gradle/compileBenchmark.gradle b/performance/gradle/compileBenchmark.gradle index e9ec562fb56..f485e9c70ac 100644 --- a/performance/gradle/compileBenchmark.gradle +++ b/performance/gradle/compileBenchmark.gradle @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.MPPTools + def exitCodes = [:] MPPTools.addTimeListener(project) diff --git a/performance/helloworld/build.gradle b/performance/helloworld/build.gradle index 2e6428242fc..6faab469276 100644 --- a/performance/helloworld/build.gradle +++ b/performance/helloworld/build.gradle @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.MPPTools + def dist = findProperty('org.jetbrains.kotlin.native.home') ?: 'dist' dist = (new File(dist)).isAbsolute() ? dist : "${project.getProjectDir()}/$dist" def toolExtension = System.getProperty("os.name").startsWith('Windows') ? ".bat" : "" @@ -7,4 +9,4 @@ project.ext { buildSteps = ["runKonanc": ["$dist/bin/konanc$toolExtension", "${project.getProjectDir()}/src/main/kotlin/main.kt", "-o", "${buildDir.absolutePath}/program${MPPTools.getNativeProgramExtension()}"]] } -apply from: rootProject.file("${rootProject.rootDir}/gradle/compileBenchmark.gradle") \ No newline at end of file +apply from: rootProject.file("${rootProject.rootDir}/performance/gradle/compileBenchmark.gradle") \ No newline at end of file diff --git a/performance/objcinterop/build.gradle b/performance/objcinterop/build.gradle new file mode 100644 index 00000000000..8a9b901ad1e --- /dev/null +++ b/performance/objcinterop/build.gradle @@ -0,0 +1,29 @@ +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' diff --git a/performance/objcinterop/gradle.properties b/performance/objcinterop/gradle.properties new file mode 100644 index 00000000000..87803bed88a --- /dev/null +++ b/performance/objcinterop/gradle.properties @@ -0,0 +1 @@ +org.jetbrains.kotlin.native.home=../../dist \ No newline at end of file diff --git a/performance/objcinterop/src/main/kotlin-jvm/org/jetbrains/objCInteropBenchmarks/complexNumbers.kt b/performance/objcinterop/src/main/kotlin-jvm/org/jetbrains/objCInteropBenchmarks/complexNumbers.kt new file mode 100644 index 00000000000..0394b3f708e --- /dev/null +++ b/performance/objcinterop/src/main/kotlin-jvm/org/jetbrains/objCInteropBenchmarks/complexNumbers.kt @@ -0,0 +1,39 @@ +/* + * 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. + */ +package org.jetbrains.complexNumbers + +actual class ComplexNumber() {} + +actual class ComplexNumbersBenchmark actual constructor() { + actual fun generateNumbersSequence(): List { + error("Benchmark generateNumbersSequence is unsupported on JVM!") + } + + actual fun sumComplex() { + error("Benchmark sumComplex is unsupported on JVM!") + } + + actual fun subComplex() { + error("Benchmark subComplex is unsupported on JVM!") + } + actual fun classInheritance() { + error("Benchmark classInheritance is unsupported on JVM!") + } + actual fun categoryMethods() { + error("Benchmark categoryMethods is unsupported on JVM!") + } + actual fun stringToObjC() { + error("Benchmark stringToObjC is unsupported on JVM!") + } + actual fun stringFromObjC() { + error("Benchmark stringToObjC is unsupported on JVM!") + } + actual fun fft() { + error("Benchmark fft is unsupported on JVM!") + } + actual fun invertFft() { + error("Benchmark invertFft is unsupported on JVM!") + } +} diff --git a/performance/objcinterop/src/main/kotlin-native/org/jetbrains/objCinteropBenchmarks/complexNumbers.kt b/performance/objcinterop/src/main/kotlin-native/org/jetbrains/objCinteropBenchmarks/complexNumbers.kt new file mode 100644 index 00000000000..8b0f0600c26 --- /dev/null +++ b/performance/objcinterop/src/main/kotlin-native/org/jetbrains/objCinteropBenchmarks/complexNumbers.kt @@ -0,0 +1,134 @@ +/* + * 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. + */ + +package org.jetbrains.complexNumbers +import kotlinx.cinterop.* +import platform.posix.* +import kotlin.math.sqrt +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.sin +import kotlin.random.Random +import platform.Foundation.* +import platform.darwin.* + +actual typealias ComplexNumber = Complex + +actual class ComplexNumbersBenchmark actual constructor() { + val complexNumbersSequence = generateNumbersSequence() + + fun randomNumber() = Random.nextDouble(0.0, benchmarkSize.toDouble()) + + actual fun generateNumbersSequence(): List { + val result = mutableListOf() + for (i in 1..benchmarkSize) { + result.add(Complex(randomNumber(), randomNumber())) + } + return result + } + + actual fun sumComplex() { + complexNumbersSequence.map { it.add(it) }.reduce { acc, it -> acc.add(it) } + } + + actual fun subComplex() { + complexNumbersSequence.map { it.sub(it) }.reduce { acc, it -> acc.sub(it) } + } + + actual fun classInheritance() { + class InvertedNumber(val value: Double) : CustomNumberProtocol, NSObject() { + override fun add(other: CustomNumberProtocol) : CustomNumberProtocol = + if (other is InvertedNumber) + InvertedNumber(-value + sqrt(other.value)) + else + error("Expected object of InvertedNumber class") + + + override fun sub(other: CustomNumberProtocol) : CustomNumberProtocol = + if (other is InvertedNumber) + InvertedNumber(-value - sqrt(other.value)) + else + error("Expected object of InvertedNumber class") + } + + val result = InvertedNumber(0.0) + + for (i in 1..benchmarkSize) { + result.add(InvertedNumber(randomNumber())) + result.sub(InvertedNumber(randomNumber())) + } + } + + actual fun categoryMethods() { + complexNumbersSequence.map { it.mul(it) }.reduce { acc, it -> acc.mul(it) } + complexNumbersSequence.map { it.div(it) }.reduce { acc, it -> acc.mul(it) } + } + + actual fun stringToObjC() { + complexNumbersSequence.forEach { + it.setFormat("%.1lf|%.1lf") + } + } + + actual fun stringFromObjC() { + complexNumbersSequence.forEach { + it.description()?.split(" ") + } + } + + private fun revert(number: Int, lg: Int): Int { + var result = 0 + for (i in 0 until lg) { + if (number and (1 shl i) != 0) { + result = result or 1 shl (lg - i - 1) + } + } + return result + } + + inline private fun fftRoutine(invert:Boolean = false): Array { + var lg = 0 + while ((1 shl lg) < complexNumbersSequence.size) { + lg++ + } + val sequence = complexNumbersSequence.toTypedArray() + + sequence.forEachIndexed { index, number -> + if (index < revert(index, lg) && revert(index, lg) < complexNumbersSequence.size) { + sequence[index] = sequence[revert(index, lg)].also { sequence[revert(index, lg)] = sequence[index] } + } + } + + var length = 2 + while (length < complexNumbersSequence.size) { + val angle = 2 * PI / length * if (invert) -1 else 1 + val base = Complex(cos(angle), sin(angle)) + for (i in 0 until complexNumbersSequence.size / 2 step length) { + var value = Complex(1.0, 1.0) + for (j in 0 until length/2) { + val first = sequence[i + j] + val second = sequence[i + j + length/2].mul(value) + sequence[i + j] = (first.add(second) as Complex)!! + sequence[i + j + length/2] = (first.sub(second) as Complex)!! + value = value.mul(base)!! + } + } + length = length shl 1 + } + return sequence + } + + actual fun fft() { + fftRoutine() + } + + actual fun invertFft() { + val sequence = fftRoutine(true) + + sequence.forEachIndexed { index, number -> + sequence[index] = number.div(Complex(sequence.size.toDouble(), 0.0)) ?: Complex(0.0, 0.0) + } + } +} diff --git a/performance/objcinterop/src/main/kotlin/main.kt b/performance/objcinterop/src/main/kotlin/main.kt new file mode 100644 index 00000000000..8d8f95d8f6b --- /dev/null +++ b/performance/objcinterop/src/main/kotlin/main.kt @@ -0,0 +1,32 @@ +/* + * 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.benchmarksLauncher.* +import org.jetbrains.complexNumbers.* +import org.jetbrains.kliopt.* + +class ObjCInteropLauncher(numWarmIterations: Int, numberOfAttempts: Int, prefix: String): Launcher(numWarmIterations, numberOfAttempts, prefix) { + val complexNumbersBecnhmark = ComplexNumbersBenchmark() + override val benchmarks = BenchmarksCollection( + mutableMapOf( + "generateNumbersSequence" to complexNumbersBecnhmark::generateNumbersSequence, + "sumComplex" to complexNumbersBecnhmark::sumComplex, + "subComplex" to complexNumbersBecnhmark::subComplex, + "classInheritance" to complexNumbersBecnhmark::classInheritance, + "categoryMethods" to complexNumbersBecnhmark::categoryMethods, + "stringToObjC" to complexNumbersBecnhmark::stringToObjC, + "stringFromObjC" to complexNumbersBecnhmark::stringFromObjC, + "fft" to complexNumbersBecnhmark::fft, + "invertFft" to complexNumbersBecnhmark::invertFft + ) + ) +} + +fun main(args: Array) { + BenchmarksRunner.runBenchmarks(args, { parser: ArgParser -> + ObjCInteropLauncher(parser.get("warmup")!!, parser.get("repeat")!!, parser.get("prefix")!!).launch(parser.getAll("filter")) + }) +} \ No newline at end of file diff --git a/performance/objcinterop/src/main/kotlin/org/jetbrains/objCinteropBenchmarks/complexNumbers.kt b/performance/objcinterop/src/main/kotlin/org/jetbrains/objCinteropBenchmarks/complexNumbers.kt new file mode 100644 index 00000000000..4421bba47f8 --- /dev/null +++ b/performance/objcinterop/src/main/kotlin/org/jetbrains/objCinteropBenchmarks/complexNumbers.kt @@ -0,0 +1,22 @@ +/* + * 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. + */ + +package org.jetbrains.complexNumbers + +const val benchmarkSize = 10000 + +expect class ComplexNumber + +expect class ComplexNumbersBenchmark() { + fun generateNumbersSequence(): List + fun sumComplex() + fun subComplex() + fun classInheritance() + fun categoryMethods() + fun stringToObjC() + fun stringFromObjC() + fun fft() + fun invertFft() +} \ No newline at end of file diff --git a/performance/objcinterop/src/nativeInterop/cinterop/classes.def b/performance/objcinterop/src/nativeInterop/cinterop/classes.def new file mode 100644 index 00000000000..2b36d4ee0e6 --- /dev/null +++ b/performance/objcinterop/src/nativeInterop/cinterop/classes.def @@ -0,0 +1,2 @@ +package = org.jetbrains.complexNumbers +language = Objective-C \ No newline at end of file diff --git a/performance/objcinterop/src/nativeInterop/cinterop/complexNumbers.h b/performance/objcinterop/src/nativeInterop/cinterop/complexNumbers.h new file mode 100644 index 00000000000..bc61981df01 --- /dev/null +++ b/performance/objcinterop/src/nativeInterop/cinterop/complexNumbers.h @@ -0,0 +1,22 @@ +#import + +@protocol CustomNumber +@required +- (id _Nonnull)add: (id _Nonnull)other; +- (id _Nonnull)sub: (id _Nonnull)other; +@end + +@interface Complex : NSObject + +@property (nonatomic, readonly) double re; +@property (nonatomic, readonly) double im; +@property (nonatomic, readwrite) NSString *format; + +- (id)initWithRe: (double)re andIm: (double)im; ++ (Complex *)complexWithRe: (double)re im: (double)im; +@end + +@interface Complex (CategorizedComplex) +- (Complex * _Nonnull)mul: (Complex * _Nonnull)other; +- (Complex * _Nonnull)div: (Complex * _Nonnull)other; +@end diff --git a/performance/objcinterop/src/nativeInterop/cinterop/complexNumbers.m b/performance/objcinterop/src/nativeInterop/cinterop/complexNumbers.m new file mode 100644 index 00000000000..7d837f5775b --- /dev/null +++ b/performance/objcinterop/src/nativeInterop/cinterop/complexNumbers.m @@ -0,0 +1,62 @@ +#import +#import "complexNumbers.h" + +@implementation Complex +{ + double re; + double im; + NSString *format; +} +- (id)init { + return [self initWithRe: 0.0 andIm: 0.0]; +} + +- (id)initWithRe: (double)_re andIm: (double)_im { + if (self = [super init]) { + re = _re; + im = _im; + format = @"re: %.1lf im: %.1lf"; + } + return self; +} + ++ (Complex *)complexWithRe: (double)re im: (double)im { + return [[Complex alloc] initWithRe: re andIm: im]; +} +- (id _Nonnull)add: (id _Nonnull)other { + if ([self isKindOfClass:[Complex class]]) { + Complex * otherComplex = (Complex *)other; + return [[Complex alloc] initWithRe: re + otherComplex->re andIm: im + otherComplex->im]; + } + return NULL; +} + +- (id _Nonnull)sub: (id _Nonnull)other { + if ([self isKindOfClass:[Complex class]]) { + Complex * otherComplex = (Complex *)other; + return [[Complex alloc] initWithRe: re - otherComplex->re andIm: im - otherComplex->im]; + } + return NULL; +} + +- (NSString *)description { + return [NSString stringWithFormat: format, re, im]; +} +@end + +@implementation Complex (CategorizedComplex) +- (Complex * _Nonnull)mul: (Complex * _Nonnull)other { + return [Complex complexWithRe: re * other.re - im * other.im im: re * other.im + im * other.re]; +} +- (Complex * _Nonnull)div: (Complex * _Nonnull)other { + double retRe; + double retIm; + double denominator; + denominator = other.re * other.re + other.im * other.im; + if (!denominator) + return nil; + retRe = (re * other.re + im * other.im) / denominator; + retIm = (im * other.re - re * other.im) / denominator; + return [Complex complexWithRe: retRe im: retIm]; +} +@end \ No newline at end of file diff --git a/performance/ring/build.gradle b/performance/ring/build.gradle index 2af6e891f53..e07111d3128 100644 --- a/performance/ring/build.gradle +++ b/performance/ring/build.gradle @@ -5,4 +5,4 @@ project.ext { nativeSrcDirs = ['src/main/kotlin-native', '../shared/src/main/kotlin-native'] } -apply from: rootProject.file("${rootProject.rootDir}/gradle/benchmark.gradle") \ No newline at end of file +apply from: rootProject.file("${rootProject.rootDir}/performance/gradle/benchmark.gradle") \ No newline at end of file diff --git a/performance/settings.gradle b/performance/settings.gradle deleted file mode 100644 index ce753c6c8f2..00000000000 --- a/performance/settings.gradle +++ /dev/null @@ -1,5 +0,0 @@ -include ':ring' -include ':cinterop' -include ':helloworld' -include ':videoplayer' -include ':framework' \ No newline at end of file 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 6c656fbf4ba..9d224259a69 100644 --- a/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt +++ b/performance/shared/src/main/kotlin/org/jetbrains/benchmarksLauncher/launcher.kt @@ -19,12 +19,20 @@ package org.jetbrains.benchmarksLauncher import kotlin.math.sqrt import org.jetbrains.report.BenchmarkResult import org.jetbrains.kliopt.* +import kotlin.reflect.KFunction0 abstract class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int, val prefix: String = "") { class Results(val mean: Double, val variance: Double) abstract val benchmarks: BenchmarksCollection + fun add(name: String, benchmark:() -> Any?) { + fun benchmarkWrapper() { + benchmark() + } + benchmarks[name] = ::benchmarkWrapper + } + fun launch(filters: Collection? = null, filterRegexes: Collection? = null): List { val regexes = filterRegexes?.map { it.toRegex() } ?: listOf() diff --git a/performance/swiftinterop/build.gradle b/performance/swiftinterop/build.gradle new file mode 100644 index 00000000000..68f51df90bd --- /dev/null +++ b/performance/swiftinterop/build.gradle @@ -0,0 +1,153 @@ +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 { + ext.rootBuildDirectory = file('../..') + + apply from: "$rootBuildDirectory/gradle/loadRootProperties.gradle" + apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle" + + repositories { + maven { + url 'https://cache-redirector.jetbrains.com/jcenter' + } + maven { + url kotlinCompilerRepo + } + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" + } +} + +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" + } + kotlin.srcDir "src" + kotlin.srcDir "../shared/src/main/kotlin" + kotlin.srcDir "$toolsPath/kliopt" + kotlin.srcDir "$toolsPath/benchmarks/shared/src" + } + macosMain { + dependsOn commonMain + kotlin.srcDir "../shared/src/main/kotlin-native" + } + } + + macosX64("macos") { + compilations.main.extraOpts = (project.hasProperty('compilerArgs') ? compilerArgs.split() : []) + '-opt' + 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/gradle.properties b/performance/swiftinterop/gradle.properties new file mode 100644 index 00000000000..87803bed88a --- /dev/null +++ b/performance/swiftinterop/gradle.properties @@ -0,0 +1 @@ +org.jetbrains.kotlin.native.home=../../dist \ No newline at end of file diff --git a/performance/swiftinterop/src/main/kotlin/org/jetbrains/launcher/SwiftLauncher.kt b/performance/swiftinterop/src/main/kotlin/org/jetbrains/launcher/SwiftLauncher.kt new file mode 100644 index 00000000000..41df9a129d9 --- /dev/null +++ b/performance/swiftinterop/src/main/kotlin/org/jetbrains/launcher/SwiftLauncher.kt @@ -0,0 +1,14 @@ +/* + * 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.benchmarksLauncher.* +import org.jetbrains.kliopt.* + +class SwiftLauncher(numWarmIterations: Int, numberOfAttempts: Int, prefix: String): Launcher(numWarmIterations, numberOfAttempts, prefix) { + override val benchmarks = BenchmarksCollection( + mutableMapOf( + ) + ) +} \ No newline at end of file diff --git a/performance/swiftinterop/src/main/kotlin/org/jetbrains/model/CityMap.kt b/performance/swiftinterop/src/main/kotlin/org/jetbrains/model/CityMap.kt new file mode 100644 index 00000000000..9e8fddd046a --- /dev/null +++ b/performance/swiftinterop/src/main/kotlin/org/jetbrains/model/CityMap.kt @@ -0,0 +1,122 @@ +/* + * 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. + */ + +package org.jetbrains.model + +import org.jetbrains.multigraph.* +import kotlin.comparisons.compareBy + +enum class Transport { CAR, UNDERGROUND, BUS, TROLLEYBUS, TRAM, TAXI, FOOT } +enum class Interest { SIGHT, CULTURE, PARK, ENTERTAINMENT } + +class PlaceAbsenceException(message: String): Exception(message) {} + +data class RouteCost(val moneyCost: Double, val timeCost: Double, val interests: Set, val transport: Set): Cost { + private val comparator = compareBy({ it.moneyCost }, { it.timeCost }, { it.interests.size }, { it.transport.size }) + + override operator fun plus(other: Cost) = + if (other is RouteCost) { + RouteCost(moneyCost + other.moneyCost, timeCost + other.timeCost, + interests.union(other.interests), transport.union(other.transport)) + } else { + error("Expected type is RouteCost") + } + + override operator fun minus(other: Cost) = + if (other is RouteCost) { + RouteCost(if (moneyCost > other.moneyCost) moneyCost - other.moneyCost else 0.0, + if (timeCost > other.timeCost) timeCost - other.timeCost else 0.0, + interests.subtract(other.interests), transport.subtract(other.transport)) + } else { + error("Expected type is RouteCost") + } + + override operator fun compareTo(other: Cost) = + if (other is RouteCost) { + comparator.compare(this, other) + } else { + error("Expected type is RouteCost") + } +} + +internal var placeCounter = 0u + +data class Place(val geoCoordinateX: Double, val geoCoordinateY: Double, val name: String, val interestCategory: Interest) { + private val comparator = compareBy({ it.geoCoordinateX }, { it.geoCoordinateY }) + + val id: UInt + init { + id = placeCounter + placeCounter++ + } + + val fullDescription: String + get() = "Place $name($geoCoordinateX;$geoCoordinateY)" + + fun compareTo(other: Place) = + comparator.compare(this, other) +} + +data class Path(val from: Place, val to: Place, val cost: RouteCost) + +class CityMap { + data class RouteId(val id: UInt, val from: UInt, val to: UInt) + private val graph = Multigraph() + + val allPlaces: Set + get() = graph.allVertexes + val allRoutes: List + get() { + val edges = graph.allEdges + val result = mutableListOf() + edges.forEach { + result.add(RouteId(it, graph.getFrom(it).id, graph.getTo(it).id)) + } + return result + } + + fun addRoute(from: Place, to: Place, cost: RouteCost): UInt { + return graph.addEdge(from, to, cost) + } + + fun getPlaceById(id: UInt): Place { + graph.allVertexes.forEach { + if (it.id == id) { + return it + } + } + throw PlaceAbsenceException("Place with id $id wasn't found.") + } + + fun removePlaceById(id: UInt) { + graph.allVertexes.forEach { + if (it.id == id) { + graph.removeVertex(it) + return + } + } + } + + fun removeRouteById(id: UInt) { + graph.removeEdge(id) + } + + fun getRoutes(start: Place, finish: Place, limits: RouteCost): List> { + val result = graph.searchRoutesWithLimits(start, finish, limits) + return result.map { + it.map { + Path(graph.getFrom(it), graph.getTo(it), graph.getCost(it) as RouteCost) + } + } + } + + fun getRouteById(id: UInt) = + graph.getEdgeById(id) + + fun getAllStraightRoutesFrom(place: Place) = + graph.getEdgesFrom(place).map { Path(graph.getFrom(it.id), graph.getTo(it.id), graph.getCost(it.id) as RouteCost) } + + fun isEmpty() = graph.isEmpty() +} \ No newline at end of file diff --git a/performance/swiftinterop/src/main/kotlin/org/jetbrains/multigraph/Multigraph.kt b/performance/swiftinterop/src/main/kotlin/org/jetbrains/multigraph/Multigraph.kt new file mode 100644 index 00000000000..9bab1ea28ca --- /dev/null +++ b/performance/swiftinterop/src/main/kotlin/org/jetbrains/multigraph/Multigraph.kt @@ -0,0 +1,184 @@ +/* + * 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. + */ + +package org.jetbrains.multigraph + +import kotlin.math.pow + +interface Cost { + operator fun plus(other: Cost): Cost + operator fun minus(other: Cost): Cost + override operator fun equals(other: Any?): Boolean + operator fun compareTo(other: Cost): Int +} + +data class Edge(val id: UInt, val from: T, val to: T, val cost: Cost) { + override operator fun equals(other: Any?): Boolean { + return if (other is Edge<*>) (from == other.from && to == other.to && cost == other.cost) else false + } + + override fun hashCode(): Int = + from.hashCode() * 31.0.pow(2.0).toInt() + to.hashCode() * 31 + cost.hashCode() +} + +class EdgeAbsenceMultigraphException(message: String): Exception(message) {} +class VertexAbsenceMultigraphException(message: String): Exception(message) {} + +class Multigraph() { + private var edges = mutableMapOf>>() + private var idCounter = 0u + + val allVertexes: Set + get() { + val outerVertexes = edges.keys + val innerVertexes = edges.map { (_, values) -> + values.map { it.to }.filter { it !in outerVertexes } + }.flatten() + return outerVertexes.union(innerVertexes) + } + + val allEdges: List + get() = edges.values.flatten().map { it.id } + + fun getEdgeById(id: UInt): Edge { + edges.forEach { (_, value) -> + value.forEach { + if (it.id == id) + return it + } + } + throw EdgeAbsenceMultigraphException("Edge with id $id wasn't found.") + } + + fun copyMultigraph(): Multigraph { + val newInstance = Multigraph() + edges.forEach { (vertex, edges) -> + edges.forEach { edge -> + newInstance.addEdge(vertex, edge.to, edge.cost) + } + } + return newInstance + } + + fun addEdge(from: T, to: T, cost: Cost): UInt { + val edge = Edge(idCounter, from, to, cost) + edges.getOrPut(from) { mutableListOf() }.add(edge) + idCounter++ + return edge.id + } + + fun removeEdge(id: UInt) { + try { + val edge = getEdgeById(id) + edges[edge.from]?.remove(edge) + } catch (exception: EdgeAbsenceMultigraphException) { + println("WARNING: no edge with id $id was found.") + } + } + + fun removeVertex(vertex: T) { + edges.remove(vertex) + val edgesToRemove = edges.map { (_, values) -> + values.filter { + it.to == vertex + } + }.flatten() + edgesToRemove.forEach { + removeEdge(it.id) + } + } + + fun checkVertexExistance(vertex: T): Boolean { + return vertex in allVertexes + } + + fun getTo(edgeId: UInt) = + getEdgeById(edgeId).to + + fun getFrom(edgeId: UInt) = + getEdgeById(edgeId).from + + fun getCost(edgeId: UInt) = + getEdgeById(edgeId).cost + + fun getEdgesFrom(vertex: T) = + edges[vertex] ?: listOf>() + + fun isEmpty() = edges.isEmpty() + + fun searchRoutesWithLimits(start: T, finish: T, limits: Cost): List> { + data class WaveStep(val costs: MutableList = mutableListOf(), + val routes: MutableList> = mutableListOf(), + val vertexes: MutableList> = mutableListOf()) + + val currentStepsState = mutableMapOf() + var oldFront = mutableSetOf(start) + val newFront = mutableSetOf() + + if (!checkVertexExistance(start)) { + throw(VertexAbsenceMultigraphException("Start vertex wasn't found in graph.")) + } + if (!checkVertexExistance(finish)) { + throw(VertexAbsenceMultigraphException("Finish vertex wasn't found in graph.")) + } + + allVertexes.forEach { + currentStepsState[it] = WaveStep() + } + while (!oldFront.isEmpty()) { + oldFront.forEach { + val currentStepState = currentStepsState[it] ?: WaveStep() + // Lookup all edges from vertex. + val values = edges.get(it) ?: mutableListOf>() + values.forEach { edge -> + val newRoutes = mutableListOf() + val toStep = currentStepsState[edge.to] ?: WaveStep() + + // Create new pathes and count their costs. + if (currentStepState.routes.isEmpty()) { + val newVertexes = mutableListOf(edge.from, edge.to) + val newCost = edge.cost + if (newCost <= limits && edge.from != edge.to) { + newRoutes.add(edge.id) + toStep.routes.add(newRoutes) + toStep.costs.add(edge.cost) + toStep.vertexes.add(newVertexes) + currentStepsState[edge.to] = toStep + // Add new wave front. + if (edge.to != finish && edge.to !in oldFront) { + newFront.add(edge.to) + } + } + } else { + currentStepState.routes.forEachIndexed { index, it -> + val newRoutes = it.toMutableList() + val oldCost = currentStepState.costs.get(index) + val newCost = edge.cost + oldCost + val newVertexes = currentStepState.vertexes.get(index).toMutableList() + if (newCost <= limits && edge.to !in currentStepState.vertexes.get(index)) { + newRoutes.add(edge.id) + newVertexes.add(edge.to) + var addNewSequence = true + + if (newRoutes !in toStep.routes) { + toStep.routes.add(newRoutes) + toStep.costs.add(newCost) + toStep.vertexes.add(newVertexes) + currentStepsState[edge.to] = toStep + if (edge.to != finish && edge.to !in oldFront) { + newFront.add(edge.to) + } + } + } + } + } + } + } + oldFront = newFront.toMutableSet() + newFront.clear() + } + return currentStepsState[finish]?.routes ?: listOf>() + } +} \ No newline at end of file diff --git a/performance/swiftinterop/swiftSrc/benchmarks.swift b/performance/swiftinterop/swiftSrc/benchmarks.swift new file mode 100644 index 00000000000..c4adc847522 --- /dev/null +++ b/performance/swiftinterop/swiftSrc/benchmarks.swift @@ -0,0 +1,225 @@ +/* + * 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 Foundation +import CityMap + +class SimpleCost: Cost { + let value: Int + + init(cost: Int) { + value = cost + } + + func plus(other: Cost) -> Cost { + let otherInstance = other as! SimpleCost + return SimpleCost(cost: value + otherInstance.value) + } + + func minus(other: Cost) -> Cost { + let otherInstance = other as! SimpleCost + return SimpleCost(cost: value - otherInstance.value) + } + + func compareTo(other: Cost) -> Int32 { + let otherInstance = other as! SimpleCost + if (value < otherInstance.value) { + return -1 + } + if (value == otherInstance.value) { + return 0 + } + return 1 + } +} + +class SwiftInteropBenchmarks { + + let BENCHMARK_SIZE = 10000 + let SMALL_BENCHMARK_SIZE = 50 + let MEDIUM_BENCHMARK_SIZE = 100 + var multigraph = Multigraph() + var cityMap = CityMap() + + func randomInt(border: Int) -> Int { + return Int.random(in: 1 ..< border) + } + + func randomDouble(border: Double) -> Double { + return Double.random(in: 0 ..< border) + } + + func randomString(length: Int) -> String { + let letters = "abcdefghijklmnopqrst" + return String((0.. Transport { + let allValues = [Transport.car, Transport.underground, Transport.bus, Transport.trolleybus, Transport.tram, Transport.taxi, Transport.foot] + return allValues.randomElement()! + } + + func randomInterest() -> Interest { + let allValues = [Interest.sight, Interest.culture, Interest.park, Interest.entertainment] + return allValues.randomElement()! + } + + func randomPlace() -> Place { + return Place(geoCoordinateX: randomDouble(border: 180), geoCoordinateY: randomDouble(border: 90), name: randomString(length: 5), interestCategory: randomInterest()) + } + + func randomRouteCost() -> RouteCost { + let transportCount = randomInt(border: 7) + let interestCount = randomInt(border: 4) + var transports = Set() + var interests = Set() + + for _ in 0...transportCount { + transports.insert(randomTransport()) + } + + for _ in 0...interestCount { + interests.insert(randomInterest()) + } + + return RouteCost(moneyCost: randomDouble(border: 10000), timeCost: randomDouble(border: 24), interests: interests, transport: transports) + } + + func initMultigraph(size: Int) { + multigraph = Multigraph() + for i in 1...size { + multigraph.addEdge(from: i, to: i + 1, cost: SimpleCost(cost: (i + 1) % i )) + multigraph.addEdge(from: i, to: i * 2, cost: SimpleCost(cost: (i * 2) % i)) + multigraph.addEdge(from: i + 5, to: i, cost: SimpleCost(cost: (i + 5) % i)) + } + } + + func createMultigraphOfInt() { + initMultigraph(size: BENCHMARK_SIZE) + } + + func fillCityMap() { + cityMap = CityMap() + for _ in 0...BENCHMARK_SIZE { + cityMap.addRoute(from: randomPlace(), to: randomPlace(), cost: randomRouteCost()) + } + } + + func initCityMap(size: Int) { + cityMap = CityMap() + let allValuesInterests = [Interest.sight, Interest.culture, Interest.park, Interest.entertainment] + let allValuesTransport = [Transport.car, Transport.underground, Transport.bus, Transport.trolleybus, Transport.tram, Transport.taxi, Transport.foot] + for i in 1...size { + let from = Place(geoCoordinateX: Double(i % 180), geoCoordinateY: Double(i % 90 + 90 % i), + name: randomString(length: 5), interestCategory: allValuesInterests[i % allValuesInterests.count]) + let to = Place(geoCoordinateX: Double(i % 180 + 180 % i), geoCoordinateY: Double(i % 90), + name: randomString(length: 5), interestCategory: allValuesInterests[(i + 5) % allValuesInterests.count]) + var transports = Set() + var interests = Set() + for j in 0...i % 2 + 1 { + interests.insert(allValuesInterests[(i + j) % allValuesInterests.count]) + } + for j in 0...i % 2 + 1 { + transports.insert(allValuesTransport[i % allValuesTransport.count]) + } + let cost = RouteCost(moneyCost: Double((i * 2) % 10000), timeCost: Double((i + 3) % 24), interests: interests, transport: transports) + cityMap.addRoute(from: from, to: to, cost: cost) + } + } + + func searchRoutesInSwiftMultigraph() { + initMultigraph(size: SMALL_BENCHMARK_SIZE) + for _ in 0...SMALL_BENCHMARK_SIZE { + var vertexes = Array(multigraph.allVertexes) + var result = multigraph.searchRoutesWithLimits(start: 1, + finish: SMALL_BENCHMARK_SIZE / 2 + SMALL_BENCHMARK_SIZE / 4, + limits: SimpleCost(cost: SMALL_BENCHMARK_SIZE / 5)) + var count = result.map{ $0.count }.reduce(0, +) + } + } + + func searchTravelRoutes() { + cityMap = CityMap() + initCityMap(size: BENCHMARK_SIZE) + let transports: Set = [Transport.car, Transport.underground] + let interests: Set = [Interest.sight, Interest.culture] + let cost = RouteCost(moneyCost: 500, timeCost: 6, interests: interests, transport: transports) + for _ in 0...SMALL_BENCHMARK_SIZE { + var result = cityMap.getRoutes(start: Array(cityMap.allPlaces).first!, finish: Array(cityMap.allPlaces).last!, limits: cost) + var totalCost = result.flatMap{ $0 }.map { $0.cost.moneyCost }.reduce(0, +) + } + } + + func availableTransportOnMap() { + cityMap = CityMap() + initCityMap(size: MEDIUM_BENCHMARK_SIZE) + Set(cityMap.allRoutes.map { (cityMap.getRouteById(id: $0.id).cost as! RouteCost).transport }) + } + + func allPlacesMapedByInterests() { + cityMap = CityMap() + initCityMap(size: MEDIUM_BENCHMARK_SIZE) + let placesByInterests = cityMap.allPlaces.reduce([Interest: Array]()) { (dict, place) -> [Interest: Array] in + var dict = dict + if (dict[place.interestCategory] != nil) { + dict[place.interestCategory]?.append(place) + } else { + dict[place.interestCategory] = [place] + } + return dict + } + + } + + func getAllPlacesWithStraightRoutesTo() { + cityMap = CityMap() + initCityMap(size: MEDIUM_BENCHMARK_SIZE) + for _ in 0...SMALL_BENCHMARK_SIZE { + let start = cityMap.allPlaces.randomElement()! + let availableRoutes = cityMap.getAllStraightRoutesFrom(place: start) + var _ = availableRoutes.map { $0.to } + } + } + + func goToAllAvailablePlaces() { + cityMap = CityMap() + initCityMap(size: MEDIUM_BENCHMARK_SIZE) + for _ in 0...SMALL_BENCHMARK_SIZE { + let start = cityMap.allPlaces.randomElement()! + let availableRoutes = cityMap.getAllStraightRoutesFrom(place: start) + availableRoutes.map { 2 * $0.cost.moneyCost } + } + } + + func removeVertexAndEdgesSwiftMultigraph() { + initMultigraph(size: SMALL_BENCHMARK_SIZE) + let multigraphCopy = multigraph.doCopyMultigraph() + var edges = multigraphCopy.allEdges + while (!edges.isEmpty) { + multigraphCopy.removeEdge(id: UInt32(edges.randomElement()!)) + let vertexes = multigraphCopy.allVertexes + multigraphCopy.removeVertex(vertex: vertexes.randomElement()!) + edges = multigraphCopy.allEdges + } + } + + func stringInterop() { + cityMap = CityMap() + fillCityMap() + let place = cityMap.allPlaces.first! + for _ in 0...BENCHMARK_SIZE { + let _ = place.fullDescription + } + } + + func simpleFunction() { + cityMap = CityMap() + fillCityMap() + let place = cityMap.allPlaces.first! + for _ in 0...BENCHMARK_SIZE { + let _ = place.compareTo(other:place) + } + } +} \ No newline at end of file diff --git a/performance/swiftinterop/swiftSrc/main.swift b/performance/swiftinterop/swiftSrc/main.swift new file mode 100644 index 00000000000..99e3e74e5d7 --- /dev/null +++ b/performance/swiftinterop/swiftSrc/main.swift @@ -0,0 +1,31 @@ +/* + * 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 Foundation +import CityMap + +var runner = BenchmarksRunner() +let args = KotlinArray(size: Int32(CommandLine.arguments.count - 1), init: {index in + CommandLine.arguments[Int(truncating: index) + 1] +}) +runner.runBenchmarks(args: args, run: { (parser: ArgParser) -> [BenchmarkResult] in + var swiftBenchmarks = SwiftInteropBenchmarks() + var swiftLauncher = SwiftLauncher(numWarmIterations: parser.get(name: "warmup") as! Int32, + numberOfAttempts: parser.get(name: "repeat") as! Int32, prefix: parser.get(name: "prefix") as! String) + swiftLauncher.add(name: "createMultigraphOfInt", benchmark: swiftBenchmarks.createMultigraphOfInt) + swiftLauncher.add(name: "fillCityMap", benchmark: swiftBenchmarks.fillCityMap) + swiftLauncher.add(name: "searchRoutesInSwiftMultigraph", benchmark: swiftBenchmarks.searchRoutesInSwiftMultigraph) + swiftLauncher.add(name: "searchTravelRoutes", benchmark: swiftBenchmarks.searchTravelRoutes) + swiftLauncher.add(name: "availableTransportOnMap", benchmark: swiftBenchmarks.availableTransportOnMap) + swiftLauncher.add(name: "allPlacesMapedByInterests", benchmark: swiftBenchmarks.allPlacesMapedByInterests) + swiftLauncher.add(name: "getAllPlacesWithStraightRoutesTo", benchmark: swiftBenchmarks.getAllPlacesWithStraightRoutesTo) + swiftLauncher.add(name: "goToAllAvailablePlaces", benchmark: swiftBenchmarks.goToAllAvailablePlaces) + swiftLauncher.add(name: "removeVertexAndEdgesSwiftMultigraph", benchmark: swiftBenchmarks.removeVertexAndEdgesSwiftMultigraph) + swiftLauncher.add(name: "stringInterop", benchmark: swiftBenchmarks.stringInterop) + swiftLauncher.add(name: "simpleFunction", benchmark: swiftBenchmarks.simpleFunction) + return swiftLauncher.launch(filters: parser.getAll(name: "filter"), filterRegexes: parser.getAll(name: "filterRegex")) +}, parseArgs: runner.parse, collect: { (benchmarks: [BenchmarkResult], parser: ArgParser) -> Void in + runner.collect(results: benchmarks, parser: parser) +}) \ No newline at end of file diff --git a/performance/videoplayer/build.gradle b/performance/videoplayer/build.gradle index beb32cc1bac..06a322c8224 100644 --- a/performance/videoplayer/build.gradle +++ b/performance/videoplayer/build.gradle @@ -1,36 +1,39 @@ +import org.jetbrains.kotlin.PlatformInfo +import org.jetbrains.kotlin.MPPTools + def dist = findProperty('org.jetbrains.kotlin.native.home') ?: 'dist' dist = (new File(dist)).isAbsolute() ? dist : "${project.getProjectDir()}/$dist" def toolExtension = System.getProperty("os.name").startsWith('Windows') ? ".bat" : "" def linkerOpts = [] -if (MPPTools.isMacos()) { +if (PlatformInfo.isMac()) { linkerOpts += ['-linker-options','-L/opt/local/lib', '-linker-options', '-L/usr/local/lib'] -} else if (MPPTools.isLinux()) { +} else if (PlatformInfo.isLinux()) { linkerOpts += ['-linker-options', '-L/usr/lib/x86_64-linux-gnu', '-linker-options', '-L/usr/lib64'] -} else if (MPPTools.isWindows()) { +} else if (PlatformInfo.isWindows()) { linkerOpts += ['-linker-options', "-L${MPPTools.mingwPath()}/lib"] } def includeDirsFfmpeg = [] def filterDirsFfmpeg = [] -if (MPPTools.isMacos()) { +if (PlatformInfo.isMac()) { filterDirsFfmpeg += ['-headerFilterAdditionalSearchPrefix', '/opt/local/include', '-headerFilterAdditionalSearchPrefix', '/usr/local/include'] -} else if (MPPTools.isLinux()) { +} else if (PlatformInfo.isLinux()) { filterDirsFfmpeg += ['-headerFilterAdditionalSearchPrefix', '/usr/include', '-headerFilterAdditionalSearchPrefix', '/usr/include/x86_64-linux-gnu', '-headerFilterAdditionalSearchPrefix', '/usr/include/ffmpeg'] -} else if (MPPTools.isWindows()) { +} else if (PlatformInfo.isWindows()) { includeDirsFfmpeg += ['-compiler-option', "-I${MPPTools.mingwPath()}/include"] } def includeDirsSdl = [] -if (MPPTools.isMacos()) { +if (PlatformInfo.isMac()) { includeDirsSdl += ['-compiler-option', '-I/opt/local/include/SDL2', '-compiler-option', '-I/usr/local/include/SDL2'] -} else if (MPPTools.isLinux()) { +} else if (PlatformInfo.isLinux()) { includeDirsSdl += ['-compiler-option', '-I/usr/include/SDL2'] -} else if (MPPTools.isWindows()) { +} else if (PlatformInfo.isWindows()) { includeDirsSdl += ['-compiler-option', "-I${MPPTools.mingwPath()}/include/SDL2"] } @@ -47,4 +50,4 @@ project.ext { "-Xmulti-platform", "$dist/../samples/videoplayer/src/videoPlayerMain/kotlin", "-entry", "sample.videoplayer.main"] + linkerOpts] } -apply from: rootProject.file("${rootProject.rootDir}/gradle/compileBenchmark.gradle") \ No newline at end of file +apply from: rootProject.file("${rootProject.rootDir}/performance/gradle/compileBenchmark.gradle") \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 685d04a4119..c47749f0b40 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.PlatformInfo +import org.jetbrains.kotlin.konan.target.HostManager + /* * Copyright 2010-2017 JetBrains s.r.o. * @@ -29,8 +32,16 @@ include ':common' include ':backend.native:tests' include ':backend.native:debugger-tests' include ':utilities' -//include ':performance' TODO: return when there is one HostManager class version -//include ':tools:benchmarksAnalyzer' +include ':performance' +include ':performance:ring' +include ':performance:cinterop' +include ':performance:helloworld' +include ':performance:videoplayer' +include ':performance:framework' +if (PlatformInfo.isAppleTarget(HostManager.host)) { + include ':performance:objcinterop' + include ':performance:swiftinterop' +} include ':platformLibs' includeBuild 'shared' @@ -38,7 +49,6 @@ includeBuild 'extracted/konan.metadata' includeBuild 'extracted/konan.serializer' includeBuild 'tools/kotlin-native-gradle-plugin' - if (hasProperty("kotlinProjectPath")) { include ':runtime:generator' includeBuild(kotlinProjectPath) {