Rewrote application with benchmarks using MPP plugin and added collec… (#2471)
Rewrote application with benchmarks using MPP plugin and added collecting information in json format.
This commit is contained in:
+104
-51
@@ -1,95 +1,148 @@
|
||||
buildscript {
|
||||
ext.rootBuildDirectory = file('..')
|
||||
|
||||
apply from: "$rootBuildDirectory/gradle/loadRootProperties.gradle"
|
||||
apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle"
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://cache-redirector.jetbrains.com/maven-central'
|
||||
url 'https://cache-redirector.jetbrains.com/jcenter'
|
||||
}
|
||||
maven {
|
||||
url buildKotlinCompilerRepo
|
||||
url kotlinCompilerRepo
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$buildKotlinVersion"
|
||||
classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:$gradlePluginVersion"
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO: property
|
||||
def jvmWarmup = 10000
|
||||
def nativeWarmup = 10
|
||||
def attempts = 10
|
||||
private def determinePreset() {
|
||||
def preset = MPPTools.defaultHostPreset(project)
|
||||
println("$project has been configured for ${preset.name} platform.")
|
||||
preset
|
||||
}
|
||||
|
||||
ext."konan.home" = distDir
|
||||
def hostPreset = determinePreset()
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'application'
|
||||
apply plugin: 'konan'
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion"
|
||||
}
|
||||
kotlin.srcDir '../tools/benchmarks/shared/src'
|
||||
kotlin.srcDir 'src/main/kotlin'
|
||||
|
||||
checkKonanCompiler.dependsOn ':dist'
|
||||
}
|
||||
nativeMain {
|
||||
kotlin.srcDir 'src/main/kotlin-native'
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
}
|
||||
kotlin.srcDir 'src/main/kotlin-jvm'
|
||||
}
|
||||
}
|
||||
|
||||
konanArtifacts {
|
||||
program('Ring') {
|
||||
srcDir 'src/main/kotlin'
|
||||
srcDir 'src/main/kotlin-native'
|
||||
enableOptimizations true
|
||||
targets {
|
||||
fromPreset(presets.jvm, 'jvm') {
|
||||
def mainOutput = compilations.main.output
|
||||
compilations.all {
|
||||
tasks[compileKotlinTaskName].kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
tasks[compileKotlinTaskName].kotlinOptions.suppressWarnings = true
|
||||
}
|
||||
}
|
||||
|
||||
fromPreset(hostPreset, 'native') {
|
||||
compilations.main.outputKinds('EXECUTABLE')
|
||||
compilations.main.extraOpts '-opt'
|
||||
compilations.main.buildTypes = [RELEASE]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.kotlin.srcDir 'src/main/kotlin'
|
||||
main.kotlin.srcDir 'src/main/kotlin-jvm'
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.suppressWarnings = true
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
MPPTools.createRunTask(project, 'konanRun', kotlin.targets.native) {
|
||||
workingDir = project.provider {
|
||||
kotlin.targets.native.compilations.main.getBinary('EXECUTABLE', buildType).parentFile
|
||||
}
|
||||
args("$nativeWarmup", "$attempts", "${buildDir.absolutePath}/${nativeBenchResults}")
|
||||
outputFileName("${buildDir.absolutePath}/${nativeTextReport}")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$buildKotlinVersion"
|
||||
}
|
||||
|
||||
task jvmRun(type: JavaExec) {
|
||||
task jvmRun(type: JavaExec) {
|
||||
dependsOn 'build'
|
||||
def output = new ByteArrayOutputStream()
|
||||
classpath sourceSets.main.runtimeClasspath
|
||||
def runtimeClasspath = files(
|
||||
kotlin.targets.jvm.compilations.main.output.allOutputs,
|
||||
project.configurations.getByName(kotlin.targets.jvm.compilations.main.runtimeDependencyConfigurationName)
|
||||
)
|
||||
classpath runtimeClasspath
|
||||
main = "MainKt"
|
||||
args "$jvmWarmup", "$attempts"
|
||||
args "$jvmWarmup", "$attempts", "${buildDir.absolutePath}/${jvmBenchResults}"
|
||||
standardOutput = output
|
||||
doLast {
|
||||
dumpReport('jvmReport', output)
|
||||
dumpReport("${buildDir.absolutePath}/${jvmTextReport}", output)
|
||||
}
|
||||
}
|
||||
|
||||
task konanJsonReport {
|
||||
doLast {
|
||||
String benchContents = new File("${buildDir.absolutePath}/${nativeBenchResults}").text
|
||||
def properties = getCommonProperties() + ['type' : 'native',
|
||||
'compilerVersion': "${konanVersion}".toString(),
|
||||
'flags' : kotlin.targets.native.compilations.main.extraOpts.collect{ '"' + it + '"'},
|
||||
'benchmarks' : benchContents]
|
||||
def output = MPPTools.createJsonReport(properties)
|
||||
new File("${buildDir.absolutePath}/${nativeJson}").write(output)
|
||||
}
|
||||
}
|
||||
|
||||
task jvmJsonReport {
|
||||
doLast {
|
||||
String benchContents = new File("${buildDir.absolutePath}/${jvmBenchResults}").text
|
||||
def properties = getCommonProperties() + ['type' : 'jvm',
|
||||
'compilerVersion': "${buildKotlinVersion}".toString(),
|
||||
'benchmarks' : benchContents]
|
||||
println(properties['compilerVersion'])
|
||||
def output = MPPTools.createJsonReport(properties)
|
||||
new File("${buildDir.absolutePath}/${jvmJson}").write(output)
|
||||
}
|
||||
}
|
||||
|
||||
jvmRun.finalizedBy jvmJsonReport
|
||||
konanRun.finalizedBy konanJsonReport
|
||||
|
||||
private void dumpReport(String name, ByteArrayOutputStream output) {
|
||||
new File("${buildDir.absolutePath}/${name}.txt").withOutputStream {
|
||||
new File("${name}").withOutputStream {
|
||||
it.write(output.toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
task konanRun(type: Exec) {
|
||||
dependsOn 'build'
|
||||
def output = new ByteArrayOutputStream()
|
||||
commandLine konanArtifacts.Ring.getByTarget('host').artifact.absolutePath, "$nativeWarmup", "$attempts"
|
||||
standardOutput = output
|
||||
doLast {
|
||||
dumpReport('konanReport', output)
|
||||
}
|
||||
}
|
||||
|
||||
startScripts{
|
||||
setEnabled(false)
|
||||
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 bench(type:DefaultTask) {
|
||||
@@ -97,8 +150,8 @@ task bench(type:DefaultTask) {
|
||||
dependsOn konanRun
|
||||
|
||||
doLast {
|
||||
def jvmReport = new Report(project.file("build/jvmReport.txt"))
|
||||
def konanReport = new Report(project.file("build/konanReport.txt"))
|
||||
def jvmReport = new Report(project.file("${buildDir.absolutePath}/${jvmTextReport}"))
|
||||
def konanReport = new Report(project.file("${buildDir.absolutePath}/${nativeTextReport}"))
|
||||
def average = "none"
|
||||
def absoluteAverage = "none"
|
||||
jvmReport.report
|
||||
@@ -138,7 +191,7 @@ class Results {
|
||||
}
|
||||
|
||||
class Report {
|
||||
def Map<String, Results> report = new TreeMap()
|
||||
Map<String, Results> report = new TreeMap()
|
||||
|
||||
Report(File path) {
|
||||
path.readLines().drop(3).findAll { it.split(':').length == 3 }.each {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
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"
|
||||
}
|
||||
|
||||
sourceSets.main.kotlin.srcDirs = ["src", "$projectDir/../../tools/benchmarks/shared/src"]
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.gradle.api.NamedDomainObjectCollection
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
|
||||
|
||||
/*
|
||||
* This file includes internal short-cuts visible only inside of the 'buildSrc' module.
|
||||
*/
|
||||
|
||||
internal val hostOs by lazy { System.getProperty("os.name") }
|
||||
internal val userHome by lazy { System.getProperty("user.home") }
|
||||
|
||||
internal val Project.ext: ExtraPropertiesExtension
|
||||
get() = extensions.getByName("ext") as ExtraPropertiesExtension
|
||||
|
||||
internal val Project.kotlin: KotlinMultiplatformExtension
|
||||
get() = extensions.getByName("kotlin") as KotlinMultiplatformExtension
|
||||
|
||||
internal val NamedDomainObjectCollection<KotlinTargetPreset<*>>.macosX64: KotlinTargetPreset<*>
|
||||
get() = getByName(::macosX64.name) as KotlinTargetPreset<*>
|
||||
|
||||
internal val NamedDomainObjectCollection<KotlinTargetPreset<*>>.linuxX64: KotlinTargetPreset<*>
|
||||
get() = getByName(::linuxX64.name) as KotlinTargetPreset<*>
|
||||
|
||||
internal val NamedDomainObjectCollection<KotlinTargetPreset<*>>.mingwX64: KotlinTargetPreset<*>
|
||||
get() = getByName(::mingwX64.name) as KotlinTargetPreset<*>
|
||||
|
||||
internal val NamedDomainObjectContainer<out KotlinCompilation<*>>.main: KotlinNativeCompilation
|
||||
get() = getByName(::main.name) as KotlinNativeCompilation
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:JvmName("MPPTools")
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
|
||||
import org.jetbrains.report.*
|
||||
import org.jetbrains.report.json.*
|
||||
import java.nio.file.Paths
|
||||
|
||||
/*
|
||||
* 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" }
|
||||
|
||||
@get:JvmName("kotlinNativeDataPath")
|
||||
val kotlinNativeDataPath by lazy {
|
||||
System.getenv("KONAN_DATA_DIR") ?: Paths.get(userHome, ".konan").toString()
|
||||
}
|
||||
|
||||
// A short-cut for evaluation of the default host Kotlin/Native preset.
|
||||
@JvmOverloads
|
||||
fun defaultHostPreset(
|
||||
subproject: Project,
|
||||
whitelist: List<KotlinTargetPreset<*>> = listOf(subproject.kotlin.presets.macosX64, subproject.kotlin.presets.linuxX64, subproject.kotlin.presets.mingwX64)
|
||||
): KotlinTargetPreset<*> {
|
||||
|
||||
if (whitelist.isEmpty())
|
||||
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
|
||||
else -> null
|
||||
}
|
||||
|
||||
return if (presetCandidate != null && presetCandidate in whitelist)
|
||||
presetCandidate
|
||||
else
|
||||
throw Exception("Host OS '$hostOs' is not supported in Kotlin/Native ${subproject.displayName}.")
|
||||
}
|
||||
|
||||
// Create benchmarks json report based on information get from gradle project
|
||||
fun createJsonReport(projectProperties: Map<String, Any>): String {
|
||||
fun getValue(key: String): String = projectProperties[key] as? String ?: "uknown"
|
||||
val machine = Environment.Machine(getValue("cpu"), getValue("os"))
|
||||
val jdk = Environment.JDKInstance(getValue("jdkVersion"), getValue("jdkVendor"))
|
||||
val env = Environment(machine, jdk)
|
||||
val flags = (projectProperties["flags"] ?: emptyList<String>()) as List<String>
|
||||
val backend = Compiler.Backend(Compiler.backendTypeFromString(getValue("type"))!! ,
|
||||
getValue("compilerVersion"), flags)
|
||||
val kotlin = Compiler(backend, getValue("kotlinVersion"))
|
||||
val benchDesc = getValue("benchmarks")
|
||||
val benchmarksArray = JsonTreeParser.parse(benchDesc)
|
||||
val benchmarks = BenchmarksReport.parseBenchmarksArray(benchmarksArray)
|
||||
val report = BenchmarksReport(env, benchmarks, kotlin)
|
||||
return report.toJson()
|
||||
}
|
||||
|
||||
// A short-cut to add a Kotlin/Native run task.
|
||||
@JvmOverloads
|
||||
fun createRunTask(
|
||||
subproject: Project,
|
||||
name: String,
|
||||
target: KotlinTarget,
|
||||
configureClosure: Closure<Any>? = null
|
||||
): Task {
|
||||
val task = subproject.tasks.create(name, RunKotlinNativeTask::class.java, target)
|
||||
task.configure(configureClosure ?: task.emptyConfigureClosure())
|
||||
return task
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import javax.inject.Inject
|
||||
import java.io.File
|
||||
|
||||
open class RunKotlinNativeTask @Inject constructor(
|
||||
private val curTarget: KotlinTarget
|
||||
): DefaultTask() {
|
||||
|
||||
var buildType = "RELEASE"
|
||||
var workingDir: Any = project.projectDir
|
||||
var outputFileName: String? = null
|
||||
private var curArgs: List<String> = emptyList()
|
||||
private val curEnvironment: MutableMap<String, Any> = mutableMapOf()
|
||||
|
||||
fun args(vararg args: Any) {
|
||||
curArgs = args.map { it.toString() }
|
||||
}
|
||||
|
||||
fun environment(map: Map<String, Any>) {
|
||||
curEnvironment += map
|
||||
}
|
||||
|
||||
override fun configure(configureClosure: Closure<Any>): Task {
|
||||
val task = super.configure(configureClosure)
|
||||
this.dependsOn += curTarget.compilations.main.linkTaskName("EXECUTABLE", buildType)
|
||||
return task
|
||||
}
|
||||
|
||||
fun depends(taskName: String) {
|
||||
this.dependsOn += taskName
|
||||
}
|
||||
|
||||
private fun executeTask(output: java.io.OutputStream? = null) {
|
||||
project.exec {
|
||||
it.executable = curTarget.compilations.main.getBinary("EXECUTABLE", buildType).toString()
|
||||
it.args = curArgs
|
||||
it.environment = curEnvironment
|
||||
it.workingDir(workingDir)
|
||||
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<Any>(this) {
|
||||
override fun call(): RunKotlinNativeTask {
|
||||
return this@RunKotlinNativeTask
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
org.jetbrains.kotlin.native.home=../dist
|
||||
jvmWarmup = 10000
|
||||
nativeWarmup = 10
|
||||
attempts = 10
|
||||
jvmBenchResults = jvmBenchResults.json
|
||||
nativeBenchResults = nativeBenchResults.json
|
||||
nativeTextReport = nativeReport.txt
|
||||
jvmTextReport = jvmReport.txt
|
||||
nativeJson = nativeReport.json
|
||||
jvmJson = jvmReport.json
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package org.jetbrains.ring
|
||||
|
||||
fun cleanup() { }
|
||||
actual fun cleanup() { }
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.jetbrains.ring
|
||||
|
||||
import java.io.File
|
||||
|
||||
//-----------------------------------------------------------------------------//
|
||||
|
||||
class Blackhole {
|
||||
companion object {
|
||||
var consumer = 0
|
||||
fun consume(value: Any) {
|
||||
actual class Blackhole {
|
||||
actual companion object {
|
||||
actual var consumer = 0
|
||||
actual fun consume(value: Any) {
|
||||
consumer += value.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -29,18 +31,37 @@ class Blackhole {
|
||||
|
||||
//-----------------------------------------------------------------------------//
|
||||
|
||||
class Random() {
|
||||
companion object {
|
||||
var seedInt = 0
|
||||
fun nextInt(boundary: Int = 100): Int {
|
||||
actual class Random actual constructor() {
|
||||
actual companion object {
|
||||
actual var seedInt = 0
|
||||
actual fun nextInt(boundary: Int): Int {
|
||||
seedInt = (3 * seedInt + 11) % boundary
|
||||
return seedInt
|
||||
}
|
||||
|
||||
var seedDouble: Double = 0.1
|
||||
fun nextDouble(boundary: Double = 100.0): Double {
|
||||
actual var seedDouble: Double = 0.1
|
||||
actual fun nextDouble(boundary: Double): Double {
|
||||
seedDouble = (7.0 * seedDouble + 7.0) % boundary
|
||||
return seedDouble
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------//
|
||||
|
||||
actual fun writeToFile(fileName: String, text: String) {
|
||||
File(fileName).printWriter().use { out ->
|
||||
out.println(text)
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for assert funtion in stdlib
|
||||
actual fun assert(value: Boolean) {
|
||||
kotlin.assert(value)
|
||||
}
|
||||
|
||||
// Wrapper for measureNanoTime funtion in stdlib
|
||||
actual inline fun measureNanoTime(block: () -> Unit): Long {
|
||||
return kotlin.system.measureNanoTime(block)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ package org.jetbrains.ring
|
||||
|
||||
import kotlin.native.internal.GC
|
||||
|
||||
fun cleanup() { GC.collect() }
|
||||
actual fun cleanup() { GC.collect() }
|
||||
@@ -16,13 +16,15 @@
|
||||
|
||||
package org.jetbrains.ring
|
||||
|
||||
import platform.posix.*
|
||||
|
||||
//-----------------------------------------------------------------------------//
|
||||
|
||||
class Blackhole {
|
||||
actual class Blackhole {
|
||||
@kotlin.native.ThreadLocal
|
||||
companion object {
|
||||
var consumer = 0
|
||||
fun consume(value: Any) {
|
||||
actual companion object {
|
||||
actual var consumer = 0
|
||||
actual fun consume(value: Any) {
|
||||
consumer += value.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -30,19 +32,41 @@ class Blackhole {
|
||||
|
||||
//-----------------------------------------------------------------------------//
|
||||
|
||||
class Random() {
|
||||
actual class Random actual constructor() {
|
||||
@kotlin.native.ThreadLocal
|
||||
companion object {
|
||||
var seedInt = 0
|
||||
fun nextInt(boundary: Int = 100): Int {
|
||||
actual companion object {
|
||||
actual var seedInt = 0
|
||||
actual fun nextInt(boundary: Int): Int {
|
||||
seedInt = (3 * seedInt + 11) % boundary
|
||||
return seedInt
|
||||
}
|
||||
|
||||
var seedDouble: Double = 0.1
|
||||
fun nextDouble(boundary: Double = 100.0): Double {
|
||||
actual var seedDouble: Double = 0.1
|
||||
actual fun nextDouble(boundary: Double): Double {
|
||||
seedDouble = (7.0 * seedDouble + 7.0) % boundary
|
||||
return seedDouble
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------//
|
||||
|
||||
|
||||
actual fun writeToFile(fileName: String, text: String) {
|
||||
val file = fopen(fileName, "wt") ?: error("Cannot write file '$fileName'")
|
||||
try {
|
||||
if (fputs(text, file) == EOF) throw Error("File write error")
|
||||
} finally {
|
||||
fclose(file)
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for assert funtion in stdlib
|
||||
actual fun assert(value: Boolean) {
|
||||
kotlin.assert(value)
|
||||
}
|
||||
|
||||
// Wrapper for measureNanoTime funtion in stdlib
|
||||
actual inline fun measureNanoTime(block: () -> Unit): Long {
|
||||
return kotlin.system.measureNanoTime(block)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package org.jetbrains.ring
|
||||
|
||||
expect fun cleanup()
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
|
||||
import org.jetbrains.ring.Launcher
|
||||
import org.jetbrains.ring.JsonReportCreator
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
var numWarmIterations = 0 // Should be 100000 for jdk based run
|
||||
var numberOfAttempts = 10
|
||||
var jsonReport: String? = null
|
||||
|
||||
when (args.size) {
|
||||
0 -> { }
|
||||
@@ -27,13 +29,20 @@ fun main(args: Array<String>) {
|
||||
numWarmIterations = args[0].toInt()
|
||||
numberOfAttempts = args[1].toInt()
|
||||
}
|
||||
3 -> {
|
||||
numWarmIterations = args[0].toInt()
|
||||
numberOfAttempts = args[1].toInt()
|
||||
jsonReport = args[2].toString()
|
||||
}
|
||||
else -> {
|
||||
println("Usage: perf [# warmup iterations] [# attempts]")
|
||||
println("Usage: perf [# warmup iterations] [# attempts] [# path of json report]")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
println("Ring starting")
|
||||
println(" warmup iterations count: $numWarmIterations")
|
||||
Launcher(numWarmIterations, numberOfAttempts).runBenchmarks()
|
||||
val results = Launcher(numWarmIterations, numberOfAttempts).runBenchmarks()
|
||||
if (jsonReport != null)
|
||||
JsonReportCreator(results).printJsonReport(jsonReport)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.ring
|
||||
|
||||
import org.jetbrains.report.BenchmarkResult
|
||||
|
||||
class JsonReportCreator(val data: Iterable<BenchmarkResult>) {
|
||||
fun printJsonReport(jsonReport: String): Unit {
|
||||
val reportText = data.joinToString(prefix = "[", postfix = "]") {
|
||||
it.toJson()
|
||||
}
|
||||
writeToFile(jsonReport, reportText)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
/**
|
||||
* Created by semoro on 07.07.17.
|
||||
*/
|
||||
|
||||
import org.jetbrains.ring.assert
|
||||
|
||||
fun octoTest() {
|
||||
val tree = OctoTree<Boolean>(4)
|
||||
val to = (2 shl tree.depth)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.ring
|
||||
|
||||
expect fun writeToFile(fileName: String, text: String)
|
||||
|
||||
expect class Blackhole {
|
||||
companion object {
|
||||
var consumer: Int
|
||||
fun consume(value: Any)
|
||||
}
|
||||
}
|
||||
|
||||
expect class Random() {
|
||||
companion object {
|
||||
var seedInt: Int
|
||||
fun nextInt(boundary: Int = 100): Int
|
||||
|
||||
var seedDouble: Double
|
||||
fun nextDouble(boundary: Double = 100.0): Double
|
||||
}
|
||||
}
|
||||
|
||||
expect fun assert(value: Boolean)
|
||||
|
||||
expect inline fun measureNanoTime(block: () -> Unit): Long
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.jetbrains.ring
|
||||
|
||||
import octoTest
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.system.measureNanoTime
|
||||
import org.jetbrains.report.BenchmarkResult
|
||||
|
||||
val BENCHMARK_SIZE = 100
|
||||
|
||||
@@ -28,8 +28,9 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
class Results(val mean: Double, val variance: Double)
|
||||
|
||||
val results = mutableMapOf<String, Results>()
|
||||
val benchmarkResults = mutableListOf<BenchmarkResult>()
|
||||
|
||||
fun launch(benchmark: () -> Any?): Results { // If benchmark runs too long - use coeff to speed it up.
|
||||
fun launch(benchmark: () -> Any?, name: String) { // If benchmark runs too long - use coeff to speed it up.
|
||||
var i = numWarmIterations
|
||||
|
||||
while (i-- > 0) benchmark()
|
||||
@@ -58,17 +59,22 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
}
|
||||
cleanup()
|
||||
}
|
||||
samples[k] = time * 1.0 / autoEvaluatedNumberOfMeasureIteration
|
||||
val scaledTime = time * 1.0 / autoEvaluatedNumberOfMeasureIteration
|
||||
samples[k] = scaledTime
|
||||
// Save benchmark object
|
||||
benchmarkResults.add(BenchmarkResult(name, BenchmarkResult.Status.PASSED,
|
||||
scaledTime / 1000, scaledTime / 1000,
|
||||
k + 1, numWarmIterations))
|
||||
}
|
||||
val mean = samples.sum() / numberOfAttempts
|
||||
val variance = samples.indices.sumByDouble { (samples[it] - mean) * (samples[it] - mean) } / numberOfAttempts
|
||||
|
||||
return Results(mean, variance)
|
||||
results[name] = Results(mean, variance)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun runBenchmarks() {
|
||||
fun runBenchmarks(): List<BenchmarkResult> {
|
||||
runAbstractMethodBenchmark()
|
||||
runClassArrayBenchmark()
|
||||
runClassBaselineBenchmark()
|
||||
@@ -96,6 +102,7 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
runOctoTest()
|
||||
|
||||
printResultsNormalized()
|
||||
return benchmarkResults
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -135,8 +142,8 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runAbstractMethodBenchmark() {
|
||||
val benchmark = AbstractMethodBenchmark()
|
||||
|
||||
results["AbstractMethod.sortStrings"] = launch(benchmark::sortStrings)
|
||||
results["AbstractMethod.sortStringsWithComparator"] = launch(benchmark::sortStringsWithComparator)
|
||||
launch(benchmark::sortStrings, "AbstractMethod.sortStrings")
|
||||
launch(benchmark::sortStringsWithComparator, "AbstractMethod.sortStringsWithComparator")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -145,16 +152,16 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = ClassArrayBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["ClassArray.copy"] = launch(benchmark::copy)
|
||||
results["ClassArray.copyManual"] = launch(benchmark::copyManual)
|
||||
results["ClassArray.filterAndCount"] = launch(benchmark::filterAndCount)
|
||||
results["ClassArray.filterAndMap"] = launch(benchmark::filterAndMap)
|
||||
results["ClassArray.filterAndMapManual"] = launch(benchmark::filterAndMapManual)
|
||||
results["ClassArray.filter"] = launch(benchmark::filter)
|
||||
results["ClassArray.filterManual"] = launch(benchmark::filterManual)
|
||||
results["ClassArray.countFilteredManual"] = launch(benchmark::countFilteredManual)
|
||||
results["ClassArray.countFiltered"] = launch(benchmark::countFiltered)
|
||||
results["ClassArray.countFilteredLocal"] = launch(benchmark::countFilteredLocal)
|
||||
launch(benchmark::copy,"ClassArray.copy")
|
||||
launch(benchmark::copyManual, "ClassArray.copyManual")
|
||||
launch(benchmark::filterAndCount, "ClassArray.filterAndCount")
|
||||
launch(benchmark::filterAndMap, "ClassArray.filterAndMap")
|
||||
launch(benchmark::filterAndMapManual, "ClassArray.filterAndMapManual")
|
||||
launch(benchmark::filter, "ClassArray.filter")
|
||||
launch(benchmark::filterManual, "ClassArray.filterManual")
|
||||
launch(benchmark::countFilteredManual, "ClassArray.countFilteredManual")
|
||||
launch(benchmark::countFiltered, "ClassArray.countFiltered")
|
||||
launch(benchmark::countFilteredLocal, "ClassArray.countFilteredLocal")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -162,13 +169,13 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runClassBaselineBenchmark() {
|
||||
val benchmark = ClassBaselineBenchmark()
|
||||
|
||||
results["ClassBaseline.consume"] = launch(benchmark::consume)
|
||||
results["ClassBaseline.consumeField"] = launch(benchmark::consumeField)
|
||||
results["ClassBaseline.allocateList"] = launch(benchmark::allocateList)
|
||||
results["ClassBaseline.allocateArray"] = launch(benchmark::allocateArray)
|
||||
results["ClassBaseline.allocateListAndFill"] = launch(benchmark::allocateListAndFill)
|
||||
results["ClassBaseline.allocateListAndWrite"] = launch(benchmark::allocateListAndWrite)
|
||||
results["ClassBaseline.allocateArrayAndFill"] = launch(benchmark::allocateArrayAndFill)
|
||||
launch(benchmark::consume, "ClassBaseline.consume")
|
||||
launch(benchmark::consumeField, "ClassBaseline.consumeField")
|
||||
launch(benchmark::allocateList, "ClassBaseline.allocateList")
|
||||
launch(benchmark::allocateArray, "ClassBaseline.allocateArray")
|
||||
launch(benchmark::allocateListAndFill, "ClassBaseline.allocateListAndFill")
|
||||
launch(benchmark::allocateListAndWrite, "ClassBaseline.allocateListAndWrite")
|
||||
launch(benchmark::allocateArrayAndFill, "ClassBaseline.allocateArrayAndFill")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -177,22 +184,22 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = ClassListBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["ClassList.copy"] = launch(benchmark::copy)
|
||||
results["ClassList.copyManual"] = launch(benchmark::copyManual)
|
||||
results["ClassList.filterAndCount"] = launch(benchmark::filterAndCount)
|
||||
results["ClassList.filterAndCountWithLambda"] = launch(benchmark::filterAndCountWithLambda)
|
||||
results["ClassList.filterWithLambda"] = launch(benchmark::filterWithLambda)
|
||||
results["ClassList.mapWithLambda"] = launch(benchmark::mapWithLambda)
|
||||
results["ClassList.countWithLambda"] = launch(benchmark::countWithLambda)
|
||||
results["ClassList.filterAndMapWithLambda"] = launch(benchmark::filterAndMapWithLambda)
|
||||
results["ClassList.filterAndMapWithLambdaAsSequence"] = launch(benchmark::filterAndMapWithLambdaAsSequence)
|
||||
results["ClassList.filterAndMap"] = launch(benchmark::filterAndMap)
|
||||
results["ClassList.filterAndMapManual"] = launch(benchmark::filterAndMapManual)
|
||||
results["ClassList.filter"] = launch(benchmark::filter)
|
||||
results["ClassList.filterManual"] = launch(benchmark::filterManual)
|
||||
results["ClassList.countFilteredManual"] = launch(benchmark::countFilteredManual)
|
||||
results["ClassList.countFiltered"] = launch(benchmark::countFiltered)
|
||||
results["ClassList.reduce"] = launch(benchmark::reduce)
|
||||
launch(benchmark::copy, "ClassList.copy")
|
||||
launch(benchmark::copyManual, "ClassList.copyManual")
|
||||
launch(benchmark::filterAndCount, "ClassList.filterAndCount")
|
||||
launch(benchmark::filterAndCountWithLambda, "ClassList.filterAndCountWithLambda")
|
||||
launch(benchmark::filterWithLambda, "ClassList.filterWithLambda")
|
||||
launch(benchmark::mapWithLambda, "ClassList.mapWithLambda")
|
||||
launch(benchmark::countWithLambda, "ClassList.countWithLambda")
|
||||
launch(benchmark::filterAndMapWithLambda, "ClassList.filterAndMapWithLambda")
|
||||
launch(benchmark::filterAndMapWithLambdaAsSequence, "ClassList.filterAndMapWithLambdaAsSequence")
|
||||
launch(benchmark::filterAndMap, "ClassList.filterAndMap")
|
||||
launch(benchmark::filterAndMapManual, "ClassList.filterAndMapManual")
|
||||
launch(benchmark::filter, "ClassList.filter")
|
||||
launch(benchmark::filterManual, "ClassList.filterManual")
|
||||
launch(benchmark::countFilteredManual, "ClassList.countFilteredManual")
|
||||
launch(benchmark::countFiltered, "ClassList.countFiltered")
|
||||
launch(benchmark::reduce, "ClassList.reduce")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -201,16 +208,16 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = ClassStreamBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["ClassStream.copy"] = launch(benchmark::copy)
|
||||
results["ClassStream.copyManual"] = launch(benchmark::copyManual)
|
||||
results["ClassStream.filterAndCount"] = launch(benchmark::filterAndCount)
|
||||
results["ClassStream.filterAndMap"] = launch(benchmark::filterAndMap)
|
||||
results["ClassStream.filterAndMapManual"] = launch(benchmark::filterAndMapManual)
|
||||
results["ClassStream.filter"] = launch(benchmark::filter)
|
||||
results["ClassStream.filterManual"] = launch(benchmark::filterManual)
|
||||
results["ClassStream.countFilteredManual"] = launch(benchmark::countFilteredManual)
|
||||
results["ClassStream.countFiltered"] = launch(benchmark::countFiltered)
|
||||
results["ClassStream.reduce"] = launch(benchmark::reduce)
|
||||
launch(benchmark::copy, "ClassStream.copy")
|
||||
launch(benchmark::copyManual, "ClassStream.copyManual")
|
||||
launch(benchmark::filterAndCount, "ClassStream.filterAndCount")
|
||||
launch(benchmark::filterAndMap, "ClassStream.filterAndMap")
|
||||
launch(benchmark::filterAndMapManual, "ClassStream.filterAndMapManual")
|
||||
launch(benchmark::filter, "ClassStream.filter")
|
||||
launch(benchmark::filterManual, "ClassStream.filterManual")
|
||||
launch(benchmark::countFilteredManual, "ClassStream.countFilteredManual")
|
||||
launch(benchmark::countFiltered, "ClassStream.countFiltered")
|
||||
launch(benchmark::reduce, "ClassStream.reduce")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -218,8 +225,8 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runCompanionObjectBenchmark() {
|
||||
val benchmark = CompanionObjectBenchmark()
|
||||
|
||||
results["CompanionObject.invokeRegularFunction"] = launch(benchmark::invokeRegularFunction)
|
||||
results["CompanionObject.invokeJvmStaticFunction"] = launch(benchmark::invokeJvmStaticFunction)
|
||||
launch(benchmark::invokeRegularFunction, "CompanionObject.invokeRegularFunction")
|
||||
launch(benchmark::invokeJvmStaticFunction, "CompanionObject.invokeJvmStaticFunction")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -228,12 +235,12 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = DefaultArgumentBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["DefaultArgument.testOneOfTwo"] = launch(benchmark::testOneOfTwo)
|
||||
results["DefaultArgument.testTwoOfTwo"] = launch(benchmark::testTwoOfTwo)
|
||||
results["DefaultArgument.testOneOfFour"] = launch(benchmark::testOneOfFour)
|
||||
results["DefaultArgument.testFourOfFour"] = launch(benchmark::testFourOfFour)
|
||||
results["DefaultArgument.testOneOfEight"] = launch(benchmark::testOneOfEight)
|
||||
results["DefaultArgument.testEightOfEight"] = launch(benchmark::testEightOfEight)
|
||||
launch(benchmark::testOneOfTwo, "DefaultArgument.testOneOfTwo")
|
||||
launch(benchmark::testTwoOfTwo, "DefaultArgument.testTwoOfTwo")
|
||||
launch(benchmark::testOneOfFour, "DefaultArgument.testOneOfFour")
|
||||
launch(benchmark::testFourOfFour, "DefaultArgument.testFourOfFour")
|
||||
launch(benchmark::testOneOfEight, "DefaultArgument.testOneOfEight")
|
||||
launch(benchmark::testEightOfEight, "DefaultArgument.testEightOfEight")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -242,7 +249,7 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = ElvisBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["Elvis.testElvis"] = launch(benchmark::testElvis)
|
||||
launch(benchmark::testElvis, "Elvis.testElvis")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -250,14 +257,14 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runEulerBenchmark() {
|
||||
val benchmark = EulerBenchmark()
|
||||
|
||||
results["Euler.problem1bySequence"] = launch(benchmark::problem1bySequence)
|
||||
results["Euler.problem1"] = launch(benchmark::problem1)
|
||||
results["Euler.problem2"] = launch(benchmark::problem2)
|
||||
results["Euler.problem4"] = launch(benchmark::problem4)
|
||||
results["Euler.problem8"] = launch(benchmark::problem8)
|
||||
results["Euler.problem9"] = launch(benchmark::problem9)
|
||||
results["Euler.problem14"] = launch(benchmark::problem14)
|
||||
results["Euler.problem14full"] = launch(benchmark::problem14full)
|
||||
launch(benchmark::problem1bySequence, "Euler.problem1bySequence")
|
||||
launch(benchmark::problem1, "Euler.problem1")
|
||||
launch(benchmark::problem2, "Euler.problem2")
|
||||
launch(benchmark::problem4, "Euler.problem4")
|
||||
launch(benchmark::problem8, "Euler.problem8")
|
||||
launch(benchmark::problem9, "Euler.problem9")
|
||||
launch(benchmark::problem14, "Euler.problem14")
|
||||
launch(benchmark::problem14full, "Euler.problem14full")
|
||||
}
|
||||
|
||||
|
||||
@@ -265,10 +272,10 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
|
||||
fun runFibonacciBenchmark() {
|
||||
val benchmark = FibonacciBenchmark()
|
||||
results["Fibonacci.calcClassic"] = launch(benchmark::calcClassic)
|
||||
results["Fibonacci.calc"] = launch(benchmark::calc)
|
||||
results["Fibonacci.calcWithProgression"] = launch(benchmark::calcWithProgression)
|
||||
results["Fibonacci.calcSquare"] = launch(benchmark::calcSquare)
|
||||
launch(benchmark::calcClassic, "Fibonacci.calcClassic")
|
||||
launch(benchmark::calc, "Fibonacci.calc")
|
||||
launch(benchmark::calcWithProgression, "Fibonacci.calcWithProgression")
|
||||
launch(benchmark::calcSquare, "Fibonacci.calcSquare")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -276,27 +283,27 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runForLoopBenchmark() {
|
||||
val benchmark = ForLoopsBenchmark()
|
||||
|
||||
results["ForLoops.arrayLoop"] = launch(benchmark::arrayLoop)
|
||||
results["ForLoops.intArrayLoop"] = launch(benchmark::intArrayLoop)
|
||||
results["ForLoops.floatArrayLoop"] = launch(benchmark::floatArrayLoop)
|
||||
results["ForLoops.charArrayLoop"] = launch(benchmark::charArrayLoop)
|
||||
results["ForLoops.stringLoop"] = launch(benchmark::stringLoop)
|
||||
launch(benchmark::arrayLoop, "ForLoops.arrayLoop")
|
||||
launch(benchmark::intArrayLoop, "ForLoops.intArrayLoop")
|
||||
launch(benchmark::floatArrayLoop, "ForLoops.floatArrayLoop")
|
||||
launch(benchmark::charArrayLoop, "ForLoops.charArrayLoop")
|
||||
launch(benchmark::stringLoop, "ForLoops.stringLoop")
|
||||
|
||||
results["ForLoops.arrayIndicesLoop"] = launch(benchmark::arrayIndicesLoop)
|
||||
results["ForLoops.intArrayIndicesLoop"] = launch(benchmark::intArrayIndicesLoop)
|
||||
results["ForLoops.floatArrayIndicesLoop"] = launch(benchmark::floatArrayIndicesLoop)
|
||||
results["ForLoops.charArrayIndicesLoop"] = launch(benchmark::charArrayIndicesLoop)
|
||||
results["ForLoops.stringIndicesLoop"] = launch(benchmark::stringIndicesLoop)
|
||||
launch(benchmark::arrayIndicesLoop, "ForLoops.arrayIndicesLoop")
|
||||
launch(benchmark::intArrayIndicesLoop, "ForLoops.intArrayIndicesLoop")
|
||||
launch(benchmark::floatArrayIndicesLoop, "ForLoops.floatArrayIndicesLoop")
|
||||
launch(benchmark::charArrayIndicesLoop, "ForLoops.charArrayIndicesLoop")
|
||||
launch(benchmark::stringIndicesLoop, "ForLoops.stringIndicesLoop")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun runInlineBenchmark() {
|
||||
val benchmark = InlineBenchmark()
|
||||
results["Inline.calculate"] = launch(benchmark::calculate)
|
||||
results["Inline.calculateInline"] = launch(benchmark::calculateInline)
|
||||
results["Inline.calculateGeneric"] = launch(benchmark::calculateGeneric)
|
||||
results["Inline.calculateGenericInline"] = launch(benchmark::calculateGenericInline)
|
||||
launch(benchmark::calculate, "Inline.calculate")
|
||||
launch(benchmark::calculateInline, "Inline.calculateInline")
|
||||
launch(benchmark::calculateGeneric, "Inline.calculateGeneric")
|
||||
launch(benchmark::calculateGenericInline, "Inline.calculateGenericInline")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -305,26 +312,26 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = IntArrayBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["IntArray.copy"] = launch(benchmark::copy)
|
||||
results["IntArray.copyManual"] = launch(benchmark::copyManual)
|
||||
results["IntArray.filterAndCount"] = launch(benchmark::filterAndCount)
|
||||
results["IntArray.filterSomeAndCount"] = launch(benchmark::filterSomeAndCount)
|
||||
results["IntArray.filterAndMap"] = launch(benchmark::filterAndMap)
|
||||
results["IntArray.filterAndMapManual"] = launch(benchmark::filterAndMapManual)
|
||||
results["IntArray.filter"] = launch(benchmark::filter)
|
||||
results["IntArray.filterSome"] = launch(benchmark::filterSome)
|
||||
results["IntArray.filterPrime"] = launch(benchmark::filterPrime)
|
||||
results["IntArray.filterManual"] = launch(benchmark::filterManual)
|
||||
results["IntArray.filterSomeManual"] = launch(benchmark::filterSomeManual)
|
||||
results["IntArray.countFilteredManual"] = launch(benchmark::countFilteredManual)
|
||||
results["IntArray.countFilteredSomeManual"] = launch(benchmark::countFilteredSomeManual)
|
||||
results["IntArray.countFilteredPrimeManual"] = launch(benchmark::countFilteredPrimeManual)
|
||||
results["IntArray.countFiltered"] = launch(benchmark::countFiltered)
|
||||
results["IntArray.countFilteredSome"] = launch(benchmark::countFilteredSome)
|
||||
results["IntArray.countFilteredPrime"] = launch(benchmark::countFilteredPrime)
|
||||
results["IntArray.countFilteredLocal"] = launch(benchmark::countFilteredLocal)
|
||||
results["IntArray.countFilteredSomeLocal"] = launch(benchmark::countFilteredSomeLocal)
|
||||
results["IntArray.reduce"] = launch(benchmark::reduce)
|
||||
launch(benchmark::copy, "IntArray.copy")
|
||||
launch(benchmark::copyManual, "IntArray.copyManual")
|
||||
launch(benchmark::filterAndCount, "IntArray.filterAndCount")
|
||||
launch(benchmark::filterSomeAndCount, "IntArray.filterSomeAndCount")
|
||||
launch(benchmark::filterAndMap, "IntArray.filterAndMap")
|
||||
launch(benchmark::filterAndMapManual, "IntArray.filterAndMapManual")
|
||||
launch(benchmark::filter, "IntArray.filter")
|
||||
launch(benchmark::filterSome, "IntArray.filterSome")
|
||||
launch(benchmark::filterPrime, "IntArray.filterPrime")
|
||||
launch(benchmark::filterManual, "IntArray.filterManual")
|
||||
launch(benchmark::filterSomeManual, "IntArray.filterSomeManual")
|
||||
launch(benchmark::countFilteredManual, "IntArray.countFilteredManual")
|
||||
launch(benchmark::countFilteredSomeManual, "IntArray.countFilteredSomeManual")
|
||||
launch(benchmark::countFilteredPrimeManual, "IntArray.countFilteredPrimeManual")
|
||||
launch(benchmark::countFiltered, "IntArray.countFiltered")
|
||||
launch(benchmark::countFilteredSome, "IntArray.countFilteredSome")
|
||||
launch(benchmark::countFilteredPrime, "IntArray.countFilteredPrime")
|
||||
launch(benchmark::countFilteredLocal, "IntArray.countFilteredLocal")
|
||||
launch(benchmark::countFilteredSomeLocal, "IntArray.countFilteredSomeLocal")
|
||||
launch(benchmark::reduce, "IntArray.reduce")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -332,11 +339,11 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runIntBaselineBenchmark() {
|
||||
val benchmark = IntBaselineBenchmark()
|
||||
|
||||
results["IntBaseline.consume"] = launch(benchmark::consume)
|
||||
results["IntBaseline.allocateList"] = launch(benchmark::allocateList)
|
||||
results["IntBaseline.allocateArray"] = launch(benchmark::allocateArray)
|
||||
results["IntBaseline.allocateListAndFill"] = launch(benchmark::allocateListAndFill)
|
||||
results["IntBaseline.allocateArrayAndFill"] = launch(benchmark::allocateArrayAndFill)
|
||||
launch(benchmark::consume, "IntBaseline.consume")
|
||||
launch(benchmark::allocateList, "IntBaseline.allocateList")
|
||||
launch(benchmark::allocateArray, "IntBaseline.allocateArray")
|
||||
launch(benchmark::allocateListAndFill, "IntBaseline.allocateListAndFill")
|
||||
launch(benchmark::allocateArrayAndFill, "IntBaseline.allocateArrayAndFill")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -345,17 +352,17 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = IntListBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["IntList.copy"] = launch(benchmark::copy)
|
||||
results["IntList.copyManual"] = launch(benchmark::copyManual)
|
||||
results["IntList.filterAndCount"] = launch(benchmark::filterAndCount)
|
||||
results["IntList.filterAndMap"] = launch(benchmark::filterAndMap)
|
||||
results["IntList.filterAndMapManual"] = launch(benchmark::filterAndMapManual)
|
||||
results["IntList.filter"] = launch(benchmark::filter)
|
||||
results["IntList.filterManual"] = launch(benchmark::filterManual)
|
||||
results["IntList.countFilteredManual"] = launch(benchmark::countFilteredManual)
|
||||
results["IntList.countFiltered"] = launch(benchmark::countFiltered)
|
||||
results["IntList.countFilteredLocal"] = launch(benchmark::countFilteredLocal)
|
||||
results["IntList.reduce"] = launch(benchmark::reduce)
|
||||
launch(benchmark::copy, "IntList.copy")
|
||||
launch(benchmark::copyManual, "IntList.copyManual")
|
||||
launch(benchmark::filterAndCount, "IntList.filterAndCount")
|
||||
launch(benchmark::filterAndMap, "IntList.filterAndMap")
|
||||
launch(benchmark::filterAndMapManual, "IntList.filterAndMapManual")
|
||||
launch(benchmark::filter, "IntList.filter")
|
||||
launch(benchmark::filterManual, "IntList.filterManual")
|
||||
launch(benchmark::countFilteredManual, "IntList.countFilteredManual")
|
||||
launch(benchmark::countFiltered, "IntList.countFiltered")
|
||||
launch(benchmark::countFilteredLocal, "IntList.countFilteredLocal")
|
||||
launch(benchmark::reduce, "IntList.reduce")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -364,17 +371,17 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = IntStreamBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["IntStream.copy"] = launch(benchmark::copy)
|
||||
results["IntStream.copyManual"] = launch(benchmark::copyManual)
|
||||
results["IntStream.filterAndCount"] = launch(benchmark::filterAndCount)
|
||||
results["IntStream.filterAndMap"] = launch(benchmark::filterAndMap)
|
||||
results["IntStream.filterAndMapManual"] = launch(benchmark::filterAndMapManual)
|
||||
results["IntStream.filter"] = launch(benchmark::filter)
|
||||
results["IntStream.filterManual"] = launch(benchmark::filterManual)
|
||||
results["IntStream.countFilteredManual"] = launch(benchmark::countFilteredManual)
|
||||
results["IntStream.countFiltered"] = launch(benchmark::countFiltered)
|
||||
results["IntStream.countFilteredLocal"] = launch(benchmark::countFilteredLocal)
|
||||
results["IntStream.reduce"] = launch(benchmark::reduce)
|
||||
launch(benchmark::copy, "IntStream.copy")
|
||||
launch(benchmark::copyManual, "IntStream.copyManual")
|
||||
launch(benchmark::filterAndCount, "IntStream.filterAndCount")
|
||||
launch(benchmark::filterAndMap, "IntStream.filterAndMap")
|
||||
launch(benchmark::filterAndMapManual, "IntStream.filterAndMapManual")
|
||||
launch(benchmark::filter, "IntStream.filter")
|
||||
launch(benchmark::filterManual, "IntStream.filterManual")
|
||||
launch(benchmark::countFilteredManual, "IntStream.countFilteredManual")
|
||||
launch(benchmark::countFiltered, "IntStream.countFiltered")
|
||||
launch(benchmark::countFilteredLocal, "IntStream.countFilteredLocal")
|
||||
launch(benchmark::reduce, "IntStream.reduce")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -383,14 +390,14 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = LambdaBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["Lambda.noncapturingLambda"] = launch(benchmark::noncapturingLambda)
|
||||
results["Lambda.noncapturingLambdaNoInline"] = launch(benchmark::noncapturingLambdaNoInline)
|
||||
results["Lambda.capturingLambda"] = launch(benchmark::capturingLambda)
|
||||
results["Lambda.capturingLambdaNoInline"] = launch(benchmark::capturingLambdaNoInline)
|
||||
results["Lambda.mutatingLambda"] = launch(benchmark::mutatingLambda)
|
||||
results["Lambda.mutatingLambdaNoInline"] = launch(benchmark::mutatingLambdaNoInline)
|
||||
results["Lambda.methodReference"] = launch(benchmark::methodReference)
|
||||
results["Lambda.methodReferenceNoInline"] = launch(benchmark::methodReferenceNoInline)
|
||||
launch(benchmark::noncapturingLambda, "Lambda.noncapturingLambda")
|
||||
launch(benchmark::noncapturingLambdaNoInline, "Lambda.noncapturingLambdaNoInline")
|
||||
launch(benchmark::capturingLambda, "Lambda.capturingLambda")
|
||||
launch(benchmark::capturingLambdaNoInline, "Lambda.capturingLambdaNoInline")
|
||||
launch(benchmark::mutatingLambda, "Lambda.mutatingLambda")
|
||||
launch(benchmark::mutatingLambdaNoInline, "Lambda.mutatingLambdaNoInline")
|
||||
launch(benchmark::methodReference, "Lambda.methodReference")
|
||||
launch(benchmark::methodReferenceNoInline, "Lambda.methodReferenceNoInline")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -399,13 +406,13 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = LoopBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["Loop.arrayLoop"] = launch(benchmark::arrayLoop)
|
||||
results["Loop.arrayIndexLoop"] = launch(benchmark::arrayIndexLoop)
|
||||
results["Loop.rangeLoop"] = launch(benchmark::rangeLoop)
|
||||
results["Loop.arrayListLoop"] = launch(benchmark::arrayListLoop)
|
||||
results["Loop.arrayWhileLoop"] = launch(benchmark::arrayWhileLoop)
|
||||
results["Loop.arrayForeachLoop"] = launch(benchmark::arrayForeachLoop)
|
||||
results["Loop.arrayListForeachLoop"] = launch(benchmark::arrayListForeachLoop)
|
||||
launch(benchmark::arrayLoop, "Loop.arrayLoop")
|
||||
launch(benchmark::arrayIndexLoop, "Loop.arrayIndexLoop")
|
||||
launch(benchmark::rangeLoop, "Loop.rangeLoop")
|
||||
launch(benchmark::arrayListLoop, "Loop.arrayListLoop")
|
||||
launch(benchmark::arrayWhileLoop, "Loop.arrayWhileLoop")
|
||||
launch(benchmark::arrayForeachLoop, "Loop.arrayForeachLoop")
|
||||
launch(benchmark::arrayListForeachLoop, "Loop.arrayListForeachLoop")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -413,7 +420,7 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runMatrixMapBenchmark() {
|
||||
val benchmark = MatrixMapBenchmark()
|
||||
|
||||
results["MatrixMap.add"] = launch(benchmark::add)
|
||||
launch(benchmark::add, "MatrixMap.add")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -421,12 +428,12 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runParameterNotNullAssertionBenchmark() {
|
||||
val benchmark = ParameterNotNullAssertionBenchmark()
|
||||
|
||||
results["ParameterNotNull.invokeOneArgWithNullCheck"] = launch(benchmark::invokeOneArgWithNullCheck)
|
||||
results["ParameterNotNull.invokeOneArgWithoutNullCheck"] = launch(benchmark::invokeOneArgWithoutNullCheck)
|
||||
results["ParameterNotNull.invokeTwoArgsWithNullCheck"] = launch(benchmark::invokeTwoArgsWithNullCheck)
|
||||
results["ParameterNotNull.invokeTwoArgsWithoutNullCheck"] = launch(benchmark::invokeTwoArgsWithoutNullCheck)
|
||||
results["ParameterNotNull.invokeEightArgsWithNullCheck"] = launch(benchmark::invokeEightArgsWithNullCheck)
|
||||
results["ParameterNotNull.invokeEightArgsWithoutNullCheck"] = launch(benchmark::invokeEightArgsWithoutNullCheck)
|
||||
launch(benchmark::invokeOneArgWithNullCheck, "ParameterNotNull.invokeOneArgWithNullCheck")
|
||||
launch(benchmark::invokeOneArgWithoutNullCheck, "ParameterNotNull.invokeOneArgWithoutNullCheck")
|
||||
launch(benchmark::invokeTwoArgsWithNullCheck, "ParameterNotNull.invokeTwoArgsWithNullCheck")
|
||||
launch(benchmark::invokeTwoArgsWithoutNullCheck, "ParameterNotNull.invokeTwoArgsWithoutNullCheck")
|
||||
launch(benchmark::invokeEightArgsWithNullCheck, "ParameterNotNull.invokeEightArgsWithNullCheck")
|
||||
launch(benchmark::invokeEightArgsWithoutNullCheck, "ParameterNotNull.invokeEightArgsWithoutNullCheck")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -434,8 +441,8 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
fun runPrimeListBenchmark() {
|
||||
val benchmark = PrimeListBenchmark()
|
||||
|
||||
results["PrimeList.calcDirect"] = launch(benchmark::calcDirect)
|
||||
results["PrimeList.calcEratosthenes"] = launch(benchmark::calcEratosthenes)
|
||||
launch(benchmark::calcDirect, "PrimeList.calcDirect")
|
||||
launch(benchmark::calcEratosthenes, "PrimeList.calcEratosthenes")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -444,11 +451,11 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = StringBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["String.stringConcat"] = launch(benchmark::stringConcat)
|
||||
results["String.stringConcatNullable"] = launch(benchmark::stringConcatNullable)
|
||||
results["String.stringBuilderConcat"] = launch(benchmark::stringBuilderConcat)
|
||||
results["String.stringBuilderConcatNullable"] = launch(benchmark::stringBuilderConcatNullable)
|
||||
results["String.summarizeSplittedCsv"] = launch(benchmark::summarizeSplittedCsv)
|
||||
launch(benchmark::stringConcat, "String.stringConcat")
|
||||
launch(benchmark::stringConcatNullable, "String.stringConcatNullable")
|
||||
launch(benchmark::stringBuilderConcat, "String.stringBuilderConcat")
|
||||
launch(benchmark::stringBuilderConcatNullable, "String.stringBuilderConcatNullable")
|
||||
launch(benchmark::summarizeSplittedCsv, "String.summarizeSplittedCsv")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -460,15 +467,15 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
benchmark.setupEnums()
|
||||
benchmark.setupSealedClassses()
|
||||
|
||||
results["Switch.testSparseIntSwitch"] = launch(benchmark::testSparseIntSwitch)
|
||||
results["Switch.testDenseIntSwitch"] = launch(benchmark::testDenseIntSwitch)
|
||||
results["Switch.testConstSwitch"] = launch(benchmark::testConstSwitch)
|
||||
results["Switch.testObjConstSwitch"] = launch(benchmark::testObjConstSwitch)
|
||||
results["Switch.testVarSwitch"] = launch(benchmark::testVarSwitch)
|
||||
results["Switch.testStringsSwitch"] = launch(benchmark::testStringsSwitch)
|
||||
results["Switch.testEnumsSwitch"] = launch(benchmark::testEnumsSwitch)
|
||||
results["Switch.testDenseEnumsSwitch"] = launch(benchmark::testDenseEnumsSwitch)
|
||||
results["Switch.testSealedWhenSwitch"] = launch(benchmark::testSealedWhenSwitch)
|
||||
launch(benchmark::testSparseIntSwitch, "Switch.testSparseIntSwitch")
|
||||
launch(benchmark::testDenseIntSwitch, "Switch.testDenseIntSwitch")
|
||||
launch(benchmark::testConstSwitch, "Switch.testConstSwitch")
|
||||
launch(benchmark::testObjConstSwitch, "Switch.testObjConstSwitch")
|
||||
launch(benchmark::testVarSwitch, "Switch.testVarSwitch")
|
||||
launch(benchmark::testStringsSwitch, "Switch.testStringsSwitch")
|
||||
launch(benchmark::testEnumsSwitch, "Switch.testEnumsSwitch")
|
||||
launch(benchmark::testDenseEnumsSwitch, "Switch.testDenseEnumsSwitch")
|
||||
launch(benchmark::testSealedWhenSwitch, "Switch.testSealedWhenSwitch")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -477,14 +484,14 @@ class Launcher(val numWarmIterations: Int, val numberOfAttempts: Int) {
|
||||
val benchmark = WithIndiciesBenchmark()
|
||||
benchmark.setup()
|
||||
|
||||
results["WithIndicies.withIndicies"] = launch(benchmark::withIndicies)
|
||||
results["WithIndicies.withIndiciesManual"] = launch(benchmark::withIndiciesManual)
|
||||
launch(benchmark::withIndicies, "WithIndicies.withIndicies")
|
||||
launch(benchmark::withIndiciesManual, "WithIndicies.withIndiciesManual")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun runOctoTest() {
|
||||
results["OctoTest"] = launch(::octoTest)
|
||||
launch(::octoTest, "OctoTest")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ include ':common'
|
||||
include ':backend.native:tests'
|
||||
include ':backend.native:debugger-tests'
|
||||
include ':utilities'
|
||||
include ':performance'
|
||||
//include ':performance' TODO: return when there is one HostManager class version
|
||||
include ':platformLibs'
|
||||
|
||||
includeBuild 'shared'
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package org.jetbrains.report
|
||||
|
||||
import org.jetbrains.report.json.*
|
||||
|
||||
interface JsonSerializable {
|
||||
fun toJson(): String
|
||||
|
||||
// Convert iterable objects arrays, lists to json.
|
||||
fun <T> arrayToJson(data: Iterable<T>): String {
|
||||
return data.joinToString(prefix = "[", postfix = "]") {
|
||||
if (it is JsonSerializable) it.toJson() else it.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entity can be created from json description.
|
||||
interface ConvertedFromJson {
|
||||
fun getRequiredField(data: JsonObject, fieldName: String): JsonElement {
|
||||
return data.getOrNull(fieldName) ?: error("Field '$fieldName' doesn't exist in '$data'. Please, check origin files.")
|
||||
}
|
||||
|
||||
fun getOptionalField(data: JsonObject, fieldName: String): JsonElement? {
|
||||
return data.getOrNull(fieldName)
|
||||
}
|
||||
|
||||
// Parse json array to list.
|
||||
// Takes function to convert elements in array to expected type.
|
||||
fun <T> arrayToList(array: JsonArray, convert: JsonArray.(Int) -> T?): List<T> {
|
||||
var results = mutableListOf<T>()
|
||||
var index = 0
|
||||
var current: T? = array.convert(index)
|
||||
while (current != null) {
|
||||
results.add(current)
|
||||
index++
|
||||
current = array.convert(index)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// Methods for conversion to expected type with checks of possibility of such conversions.
|
||||
fun elementToDouble(element: JsonElement, name: String): Double =
|
||||
if (element is JsonPrimitive)
|
||||
element.double
|
||||
else
|
||||
error("Field '$name' in '$element' is expected to be a double number. Please, check origin files.")
|
||||
|
||||
fun elementToInt(element: JsonElement, name: String): Int =
|
||||
if (element is JsonPrimitive)
|
||||
element.int
|
||||
else
|
||||
error("Field '$name' in '$element' is expected to be an integer number. Please, check origin files.")
|
||||
|
||||
fun elementToString(element: JsonElement, name:String): String =
|
||||
if (element is JsonLiteral)
|
||||
element.unquoted()
|
||||
else
|
||||
error("Field '$name' in '$element' is expected to be a string. Please, check origin files.")
|
||||
}
|
||||
|
||||
interface EntityFromJsonFactory<T>: ConvertedFromJson {
|
||||
fun create(data: JsonElement): T
|
||||
}
|
||||
|
||||
// Class for benchcmarks report with all information of run.
|
||||
data class BenchmarksReport(val env: Environment, val benchmarks: List<BenchmarkResult>, val compiler: Compiler):
|
||||
JsonSerializable {
|
||||
|
||||
companion object: EntityFromJsonFactory<BenchmarksReport> {
|
||||
override fun create(data: JsonElement): BenchmarksReport {
|
||||
if (data is JsonObject) {
|
||||
val env = Environment.create(getRequiredField(data, "env"))
|
||||
val benchmarksObj = getRequiredField(data, "benchmarks")
|
||||
val compiler = Compiler.create(getRequiredField(data, "kotlin"))
|
||||
val benchmarks = parseBenchmarksArray(benchmarksObj)
|
||||
return BenchmarksReport(env, benchmarks, compiler)
|
||||
} else {
|
||||
error("Top level entity is expected to be an object. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
|
||||
// Parse array with benchmarks to list
|
||||
fun parseBenchmarksArray(data: JsonElement): List<BenchmarkResult> {
|
||||
if (data is JsonArray) {
|
||||
return arrayToList(data.jsonArray, { index ->
|
||||
if (this.getObjectOrNull(index) != null)
|
||||
BenchmarkResult.create(this.getObjectOrNull(index) as JsonObject)
|
||||
else null
|
||||
})
|
||||
} else {
|
||||
error("Benchmarks field is expected to be an array. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(): String {
|
||||
return """
|
||||
{
|
||||
"env": ${env.toJson()},
|
||||
"kotlin": ${compiler.toJson()},
|
||||
"benchmarks": ${arrayToJson(benchmarks)}
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
// Class for kotlin compiler
|
||||
data class Compiler(val backend: Backend, val kotlinVersion: String): JsonSerializable {
|
||||
|
||||
enum class BackendType(val type: String) {
|
||||
JVM("jvm"),
|
||||
NATIVE("native")
|
||||
}
|
||||
|
||||
companion object: EntityFromJsonFactory<Compiler> {
|
||||
override fun create(data: JsonElement): Compiler {
|
||||
if (data is JsonObject) {
|
||||
val backend = Backend.create(getRequiredField(data, "backend"))
|
||||
val kotlinVersion = elementToString(getRequiredField(data, "kotlinVersion"), "kotlinVersion")
|
||||
|
||||
return Compiler(backend, kotlinVersion)
|
||||
} else {
|
||||
error("Kotlin entity is expected to be an object. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
|
||||
fun backendTypeFromString(s: String): BackendType? = BackendType.values().find { it.type == s }
|
||||
}
|
||||
|
||||
// Class for compiler backend
|
||||
data class Backend(val type: BackendType, val version: String, val flags: List<String>): JsonSerializable {
|
||||
companion object: EntityFromJsonFactory<Backend> {
|
||||
override fun create(data: JsonElement): Backend {
|
||||
if (data is JsonObject) {
|
||||
val typeElement = getRequiredField(data, "type")
|
||||
if (typeElement is JsonLiteral) {
|
||||
val type = backendTypeFromString(typeElement.unquoted()) ?: error("Backend type should be 'jvm' or 'native'")
|
||||
val version = elementToString(getRequiredField(data, "version"), "version")
|
||||
val flagsArray = getOptionalField(data, "flags")
|
||||
var flags: List<String> = emptyList()
|
||||
if (flagsArray != null && flagsArray is JsonArray) {
|
||||
flags = arrayToList(flagsArray.jsonArray, { index ->
|
||||
this.getPrimitiveOrNull(index)?.toString()
|
||||
})
|
||||
}
|
||||
return Backend(type, version, flags)
|
||||
} else {
|
||||
error("Backend type should be string literal.")
|
||||
}
|
||||
} else {
|
||||
error("Backend entity is expected to be an object. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(): String {
|
||||
val result = """
|
||||
{
|
||||
"type": "${type.type}",
|
||||
"version": "${version}""""
|
||||
// Don't print flags field if there is no one.
|
||||
if (flags.isEmpty()) {
|
||||
return """$result
|
||||
}"""
|
||||
}
|
||||
else {
|
||||
return """
|
||||
$result,
|
||||
"flags": ${arrayToJson(flags)}
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(): String {
|
||||
return """
|
||||
{
|
||||
"backend": ${backend.toJson()},
|
||||
"kotlinVersion": "${kotlinVersion}"
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
// Class for description of environment of benchmarks run
|
||||
data class Environment(val machine: Machine, val jdk: JDKInstance): JsonSerializable {
|
||||
|
||||
companion object: EntityFromJsonFactory<Environment> {
|
||||
override fun create(data: JsonElement): Environment {
|
||||
if (data is JsonObject) {
|
||||
val machine = Machine.create(getRequiredField(data, "machine"))
|
||||
val jdk = JDKInstance.create(getRequiredField(data, "jdk"))
|
||||
|
||||
return Environment(machine, jdk)
|
||||
} else {
|
||||
error("Environment entity is expected to be an object. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Class for description of machine used for benchmarks run.
|
||||
data class Machine(val cpu: String, val os: String): JsonSerializable {
|
||||
companion object: EntityFromJsonFactory<Machine> {
|
||||
override fun create(data: JsonElement): Machine {
|
||||
if (data is JsonObject) {
|
||||
val cpu = elementToString(getRequiredField(data, "cpu"), "cpu")
|
||||
val os = elementToString(getRequiredField(data, "os"), "os")
|
||||
|
||||
return Machine(cpu, os)
|
||||
} else {
|
||||
error("Machine entity is expected to be an object. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(): String {
|
||||
return """
|
||||
{
|
||||
"cpu": "$cpu",
|
||||
"os": "$os"
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
// Class for description of jdk used for benchmarks run.
|
||||
data class JDKInstance(val version: String, val vendor: String): JsonSerializable {
|
||||
companion object: EntityFromJsonFactory<JDKInstance> {
|
||||
override fun create(data: JsonElement): JDKInstance {
|
||||
if (data is JsonObject) {
|
||||
val version = elementToString(getRequiredField(data, "version"), "version")
|
||||
val vendor = elementToString(getRequiredField(data, "vendor"), "vendor")
|
||||
|
||||
return JDKInstance(version, vendor)
|
||||
} else {
|
||||
error("JDK entity is expected to be an object. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(): String {
|
||||
return """
|
||||
{
|
||||
"version": "$version",
|
||||
"vendor": "$vendor"
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
override fun toJson(): String {
|
||||
return """
|
||||
{
|
||||
"machine": ${machine.toJson()},
|
||||
"jdk": ${jdk.toJson()}
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
class BenchmarkResult(val name: String, val status: Status,
|
||||
val score: Double, val runtimeInUs: Double,
|
||||
val repeat: Int, val warmup: Int): JsonSerializable {
|
||||
|
||||
companion object: EntityFromJsonFactory<BenchmarkResult> {
|
||||
|
||||
override fun create(data: JsonElement): BenchmarkResult {
|
||||
if (data is JsonObject) {
|
||||
val name = elementToString(getRequiredField(data, "name"), "name")
|
||||
val statusElement = getRequiredField(data, "status")
|
||||
if (statusElement is JsonLiteral) {
|
||||
val status = statusFromString(statusElement.unquoted())
|
||||
?: error("Status should be PASSED or FAILED")
|
||||
val score = elementToDouble(getRequiredField(data, "score"), "score")
|
||||
val runtimeInUs = elementToDouble(getRequiredField(data, "runtimeInUs"), "runtimeInUs")
|
||||
val repeat = elementToInt(getRequiredField(data, "repeat"), "repeat")
|
||||
val warmup = elementToInt(getRequiredField(data, "warmup"), "warmup")
|
||||
|
||||
return BenchmarkResult(name, status, score, runtimeInUs, repeat, warmup)
|
||||
} else {
|
||||
error("Status should be string literal.")
|
||||
}
|
||||
} else {
|
||||
error("Benchmark entity is expected to be an object. Please, check origin files.")
|
||||
}
|
||||
}
|
||||
|
||||
fun statusFromString(s: String): Status? = Status.values().find { it.value == s }
|
||||
}
|
||||
|
||||
enum class Status(val value: String) {
|
||||
PASSED("PASSED"),
|
||||
FAILED("FAILED")
|
||||
}
|
||||
|
||||
override fun toJson(): String {
|
||||
return """
|
||||
{
|
||||
"name": "$name",
|
||||
"status": "${status.value}",
|
||||
"score": ${score},
|
||||
"runtimeInUs": ${runtimeInUs},
|
||||
"repeat": ${repeat},
|
||||
"warmup": ${warmup}
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.report.json
|
||||
|
||||
/**
|
||||
* Class representing single JSON element.
|
||||
* Can be [JsonPrimitive], [JsonArray] or [JsonObject].
|
||||
*
|
||||
* [JsonElement.toString] properly prints JSON tree as valid JSON, taking into
|
||||
* account quoted values and primitives
|
||||
*/
|
||||
sealed class JsonElement {
|
||||
|
||||
/**
|
||||
* Convenience method to get current element as [JsonPrimitive]
|
||||
* @throws JsonElementTypeMismatchException is current element is not a [JsonPrimitive]
|
||||
*/
|
||||
open val primitive: JsonPrimitive
|
||||
get() = error("JsonLiteral")
|
||||
|
||||
/**
|
||||
* Convenience method to get current element as [JsonObject]
|
||||
* @throws JsonElementTypeMismatchException is current element is not a [JsonObject]
|
||||
*/
|
||||
open val jsonObject: JsonObject
|
||||
get() = error("JsonObject")
|
||||
|
||||
/**
|
||||
* Convenience method to get current element as [JsonArray]
|
||||
* @throws JsonElementTypeMismatchException is current element is not a [JsonArray]
|
||||
*/
|
||||
open val jsonArray: JsonArray
|
||||
get() = error("JsonArray")
|
||||
|
||||
/**
|
||||
* Convenience method to get current element as [JsonNull]
|
||||
* @throws JsonElementTypeMismatchException is current element is not a [JsonNull]
|
||||
*/
|
||||
open val jsonNull: JsonNull
|
||||
get() = error("JsonPrimitive")
|
||||
|
||||
/**
|
||||
* Checks whether current element is [JsonNull]
|
||||
*/
|
||||
val isNull: Boolean
|
||||
get() = this === JsonNull
|
||||
|
||||
private fun error(element: String): Nothing =
|
||||
throw JsonElementTypeMismatchException(this::class.toString(), element)
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing JSON primitive value. Can be either [JsonLiteral] or [JsonNull].
|
||||
*/
|
||||
sealed class JsonPrimitive : JsonElement() {
|
||||
|
||||
/**
|
||||
* Content of given element without quotes. For [JsonNull] this methods returns `"null"`
|
||||
*/
|
||||
abstract val content: String
|
||||
|
||||
/**
|
||||
* Content of the given element without quotes or `null` if current element is [JsonNull]
|
||||
*/
|
||||
abstract val contentOrNull: String?
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
final override val primitive: JsonPrimitive = this
|
||||
|
||||
/**
|
||||
* Returns content of current element as int
|
||||
* @throws NumberFormatException if current element is not a valid representation of number
|
||||
*/
|
||||
val int: Int get() = content.toInt()
|
||||
|
||||
/**
|
||||
* Returns content of current element as int or `null` if current element is not a valid representation of number
|
||||
**/
|
||||
val intOrNull: Int? get() = content.toIntOrNull()
|
||||
|
||||
/**
|
||||
* Returns content of current element as long
|
||||
* @throws NumberFormatException if current element is not a valid representation of number
|
||||
*/
|
||||
val long: Long get() = content.toLong()
|
||||
|
||||
/**
|
||||
* Returns content of current element as long or `null` if current element is not a valid representation of number
|
||||
*/
|
||||
val longOrNull: Long? get() = content.toLongOrNull()
|
||||
|
||||
/**
|
||||
* Returns content of current element as double
|
||||
* @throws NumberFormatException if current element is not a valid representation of number
|
||||
*/
|
||||
val double: Double get() = content.toDouble()
|
||||
|
||||
/**
|
||||
* Returns content of current element as double or `null` if current element is not a valid representation of number
|
||||
*/
|
||||
val doubleOrNull: Double? get() = content.toDoubleOrNull()
|
||||
|
||||
/**
|
||||
* Returns content of current element as float
|
||||
* @throws NumberFormatException if current element is not a valid representation of number
|
||||
*/
|
||||
val float: Float get() = content.toFloat()
|
||||
|
||||
/**
|
||||
* Returns content of current element as float or `null` if current element is not a valid representation of number
|
||||
*/
|
||||
val floatOrNull: Float? get() = content.toFloatOrNull()
|
||||
|
||||
/**
|
||||
* Returns content of current element as boolean
|
||||
* @throws IllegalStateException if current element doesn't represent boolean
|
||||
*/
|
||||
val boolean: Boolean get() = content.toBooleanStrict()
|
||||
|
||||
/**
|
||||
* Returns content of current element as boolean or `null` if current element is not a valid representation of boolean
|
||||
*/
|
||||
val booleanOrNull: Boolean? get() = content.toBooleanStrictOrNull()
|
||||
|
||||
override fun toString() = content
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing JSON literals: numbers, booleans and string.
|
||||
* Strings are always quoted.
|
||||
*/
|
||||
data class JsonLiteral internal constructor(
|
||||
private val body: Any,
|
||||
private val isString: Boolean
|
||||
) : JsonPrimitive() {
|
||||
|
||||
override val content = body.toString()
|
||||
override val contentOrNull: String = content
|
||||
|
||||
/**
|
||||
* Creates number literal
|
||||
*/
|
||||
constructor(number: Number) : this(number, false)
|
||||
|
||||
/**
|
||||
* Creates boolean literal
|
||||
*/
|
||||
constructor(boolean: Boolean) : this(boolean, false)
|
||||
|
||||
/**
|
||||
* Creates quoted string literal
|
||||
*/
|
||||
constructor(string: String) : this(string, true)
|
||||
|
||||
override fun toString() =
|
||||
if (isString) buildString { printQuoted(content) }
|
||||
else content
|
||||
|
||||
fun unquoted() = content
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing JSON `null` value
|
||||
*/
|
||||
object JsonNull : JsonPrimitive() {
|
||||
override val jsonNull: JsonNull = this
|
||||
override val content: String = "null"
|
||||
override val contentOrNull: String? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing JSON object, consisting of name-value pairs, where value is arbitrary [JsonElement]
|
||||
*/
|
||||
data class JsonObject(val content: Map<String, JsonElement>) : JsonElement(), Map<String, JsonElement> by content {
|
||||
|
||||
override val jsonObject: JsonObject = this
|
||||
|
||||
/**
|
||||
* Returns [JsonElement] associated with given [key]
|
||||
* @throws NoSuchElementException if element is not present
|
||||
*/
|
||||
override fun get(key: String): JsonElement = content[key] ?: throw NoSuchElementException("Element $key is missing")
|
||||
|
||||
/**
|
||||
* Returns [JsonElement] associated with given [key] or `null` if element is not present
|
||||
*/
|
||||
fun getOrNull(key: String): JsonElement? = content[key]
|
||||
|
||||
/**
|
||||
* Returns [JsonPrimitive] associated with given [key]
|
||||
*
|
||||
* @throws NoSuchElementException if element is not present
|
||||
* @throws JsonElementTypeMismatchException if element is present, but has invalid type
|
||||
*/
|
||||
fun getPrimitive(key: String): JsonPrimitive = get(key) as? JsonPrimitive
|
||||
?: unexpectedJson(key, "JsonPrimitive")
|
||||
|
||||
/**
|
||||
* Returns [JsonObject] associated with given [key]
|
||||
*
|
||||
* @throws NoSuchElementException if element is not present
|
||||
* @throws JsonElementTypeMismatchException if element is present, but has invalid type
|
||||
*/
|
||||
fun getObject(key: String): JsonObject = get(key) as? JsonObject
|
||||
?: unexpectedJson(key, "JsonObject")
|
||||
|
||||
/**
|
||||
* Returns [JsonArray] associated with given [key]
|
||||
*
|
||||
* @throws NoSuchElementException if element is not present
|
||||
* @throws JsonElementTypeMismatchException if element is present, but has invalid type
|
||||
*/
|
||||
fun getArray(key: String): JsonArray = get(key) as? JsonArray
|
||||
?: unexpectedJson(key, "JsonArray")
|
||||
|
||||
/**
|
||||
* Returns [JsonPrimitive] associated with given [key] or `null` if element
|
||||
* is not present or has different type
|
||||
*/
|
||||
fun getPrimitiveOrNull(key: String): JsonPrimitive? = content[key] as? JsonPrimitive
|
||||
|
||||
/**
|
||||
* Returns [JsonObject] associated with given [key] or `null` if element
|
||||
* is not present or has different type
|
||||
*/
|
||||
fun getObjectOrNull(key: String): JsonObject? = content[key] as? JsonObject
|
||||
|
||||
/**
|
||||
* Returns [JsonArray] associated with given [key] or `null` if element
|
||||
* is not present or has different type
|
||||
*/
|
||||
fun getArrayOrNull(key: String): JsonArray? = content[key] as? JsonArray
|
||||
|
||||
/**
|
||||
* Returns [J] associated with given [key]
|
||||
*
|
||||
* @throws NoSuchElementException if element is not present
|
||||
* @throws JsonElementTypeMismatchException if element is present, but has invalid type
|
||||
*/
|
||||
inline fun <reified J : JsonElement> getAs(key: String): J = get(key) as? J
|
||||
?: unexpectedJson(key, J::class.toString())
|
||||
|
||||
/**
|
||||
* Returns [J] associated with given [key] or `null` if element
|
||||
* is not present or has different type
|
||||
*/
|
||||
inline fun <reified J : JsonElement> lookup(key: String): J? = content[key] as? J
|
||||
|
||||
override fun toString(): String {
|
||||
return content.entries.joinToString(
|
||||
prefix = "{",
|
||||
postfix = "}",
|
||||
transform = {(k, v) -> """"$k": $v"""}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class JsonArray(val content: List<JsonElement>) : JsonElement(), List<JsonElement> by content {
|
||||
|
||||
override val jsonArray: JsonArray = this
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [JsonPrimitive]
|
||||
* @throws JsonElementTypeMismatchException if element has invalid type
|
||||
*/
|
||||
fun getPrimitive(index: Int) = content[index] as? JsonPrimitive
|
||||
?: unexpectedJson("at $index", "JsonPrimitive")
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [JsonObject]
|
||||
* @throws JsonElementTypeMismatchException if element has invalid type
|
||||
*/
|
||||
fun getObject(index: Int) = content[index] as? JsonObject
|
||||
?: unexpectedJson("at $index", "JsonObject")
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [JsonArray]
|
||||
* @throws JsonElementTypeMismatchException if element has invalid type
|
||||
*/
|
||||
fun getArray(index: Int) = content[index] as? JsonArray
|
||||
?: unexpectedJson("at $index", "JsonArray")
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [JsonPrimitive] or `null` if element is missing or has different type
|
||||
*/
|
||||
fun getPrimitiveOrNull(index: Int) = content.getOrNull(index) as? JsonPrimitive
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [JsonObject] or `null` if element is missing or has different type
|
||||
*/
|
||||
fun getObjectOrNull(index: Int) = content.getOrNull(index) as? JsonObject
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [JsonArray] or `null` if element is missing or has different type
|
||||
*/
|
||||
fun getArrayOrNull(index: Int) = content.getOrNull(index) as? JsonArray
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [J]
|
||||
* @throws JsonElementTypeMismatchException if element has invalid type
|
||||
*/
|
||||
inline fun <reified J : JsonElement> getAs(index: Int): J = content[index] as? J
|
||||
?: unexpectedJson("at $index", J::class.toString())
|
||||
|
||||
/**
|
||||
* Returns [index]-th element of an array as [J] or `null` if element is missing or has different type
|
||||
*/
|
||||
inline fun <reified J : JsonElement> getAsOrNull(index: Int): J? = content.getOrNull(index) as? J
|
||||
|
||||
override fun toString() = content.joinToString(prefix = "[", postfix = "]")
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun unexpectedJson(key: String, expected: String): Nothing =
|
||||
throw JsonElementTypeMismatchException(key, expected)
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.report.json
|
||||
|
||||
class JsonInvalidValueInStrictModeException(value: Any, valueDescription: String) : Exception(
|
||||
"$value is not a valid $valueDescription as per JSON spec.\n" +
|
||||
"You can disable strict mode to serialize such values"
|
||||
) {
|
||||
constructor(floatValue: Float) : this(floatValue, "float")
|
||||
constructor(doubleValue: Double) : this(doubleValue, "double")
|
||||
}
|
||||
|
||||
class JsonUnknownKeyException(key: String) : Exception(
|
||||
"Strict JSON encountered unknown key: $key\n" +
|
||||
"You can disable strict mode to skip unknown keys"
|
||||
)
|
||||
|
||||
class JsonParsingException(position: Int, message: String) : Exception("Invalid JSON at $position: $message")
|
||||
|
||||
class JsonElementTypeMismatchException(key: String, expected: String) : Exception("Element $key is not a $expected")
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.report.json
|
||||
|
||||
import org.jetbrains.report.json.EscapeCharMappings.ESC2C
|
||||
// special strings
|
||||
internal const val NULL = "null"
|
||||
|
||||
// special chars
|
||||
internal const val COMMA = ','
|
||||
internal const val COLON = ':'
|
||||
internal const val BEGIN_OBJ = '{'
|
||||
internal const val END_OBJ = '}'
|
||||
internal const val BEGIN_LIST = '['
|
||||
internal const val END_LIST = ']'
|
||||
internal const val STRING = '"'
|
||||
internal const val STRING_ESC = '\\'
|
||||
internal const val INVALID = 0.toChar()
|
||||
internal const val UNICODE_ESC = 'u'
|
||||
// token classes
|
||||
internal const val TC_OTHER: Byte = 0
|
||||
internal const val TC_STRING: Byte = 1
|
||||
internal const val TC_STRING_ESC: Byte = 2
|
||||
internal const val TC_WS: Byte = 3
|
||||
internal const val TC_COMMA: Byte = 4
|
||||
internal const val TC_COLON: Byte = 5
|
||||
internal const val TC_BEGIN_OBJ: Byte = 6
|
||||
internal const val TC_END_OBJ: Byte = 7
|
||||
internal const val TC_BEGIN_LIST: Byte = 8
|
||||
internal const val TC_END_LIST: Byte = 9
|
||||
internal const val TC_NULL: Byte = 10
|
||||
internal const val TC_INVALID: Byte = 11
|
||||
internal const val TC_EOF: Byte = 12
|
||||
// mapping from chars to token classes
|
||||
private const val CTC_MAX = 0x7e
|
||||
// mapping from escape chars real chars
|
||||
private const val C2ESC_MAX = 0x5d
|
||||
private const val ESC2C_MAX = 0x75
|
||||
|
||||
internal val C2TC = ByteArray(CTC_MAX).apply {
|
||||
for (i in 0..0x20)
|
||||
initC2TC(i, TC_INVALID)
|
||||
initC2TC(0x09, TC_WS)
|
||||
initC2TC(0x0a, TC_WS)
|
||||
initC2TC(0x0d, TC_WS)
|
||||
initC2TC(0x20, TC_WS)
|
||||
initC2TC(COMMA, TC_COMMA)
|
||||
initC2TC(COLON, TC_COLON)
|
||||
initC2TC(BEGIN_OBJ, TC_BEGIN_OBJ)
|
||||
initC2TC(END_OBJ, TC_END_OBJ)
|
||||
initC2TC(BEGIN_LIST, TC_BEGIN_LIST)
|
||||
initC2TC(END_LIST, TC_END_LIST)
|
||||
initC2TC(STRING, TC_STRING)
|
||||
initC2TC(STRING_ESC, TC_STRING_ESC)
|
||||
}
|
||||
// object instead of @SharedImmutable because there is mutual initialization in [initC2ESC]
|
||||
internal object EscapeCharMappings {
|
||||
internal val ESC2C = CharArray(ESC2C_MAX)
|
||||
internal val C2ESC = CharArray(C2ESC_MAX).apply {
|
||||
for (i in 0x00..0x1f)
|
||||
initC2ESC(i, UNICODE_ESC)
|
||||
initC2ESC(0x08, 'b')
|
||||
initC2ESC(0x09, 't')
|
||||
initC2ESC(0x0a, 'n')
|
||||
initC2ESC(0x0c, 'f')
|
||||
initC2ESC(0x0d, 'r')
|
||||
initC2ESC('/', '/')
|
||||
initC2ESC(STRING, STRING)
|
||||
initC2ESC(STRING_ESC, STRING_ESC)
|
||||
}
|
||||
private fun CharArray.initC2ESC(c: Int, esc: Char) {
|
||||
this[c] = esc
|
||||
if (esc != UNICODE_ESC) ESC2C[esc.toInt()] = c.toChar()
|
||||
}
|
||||
private fun CharArray.initC2ESC(c: Char, esc: Char) = initC2ESC(c.toInt(), esc)
|
||||
}
|
||||
private fun ByteArray.initC2TC(c: Int, cl: Byte) {
|
||||
this[c] = cl
|
||||
}
|
||||
private fun ByteArray.initC2TC(c: Char, cl: Byte) {
|
||||
initC2TC(c.toInt(), cl)
|
||||
}
|
||||
internal fun charToTokenClass(c: Char) = if (c.toInt() < CTC_MAX) C2TC[c.toInt()] else TC_OTHER
|
||||
internal fun escapeToChar(c: Int): Char = if (c < ESC2C_MAX) ESC2C[c] else INVALID
|
||||
// JSON low level parser
|
||||
internal class Parser(val source: String) {
|
||||
var curPos: Int = 0 // position in source
|
||||
private set
|
||||
// updated by nextToken
|
||||
var tokenPos: Int = 0
|
||||
private set
|
||||
var tc: Byte = TC_EOF
|
||||
private set
|
||||
// update by nextString/nextLiteral
|
||||
private var offset = -1 // when offset >= 0 string is in source, otherwise in buf
|
||||
private var length = 0 // length of string
|
||||
private var buf = CharArray(16) // only used for strings with escapes
|
||||
init {
|
||||
nextToken()
|
||||
}
|
||||
internal inline fun requireTc(expected: Byte, lazyErrorMsg: () -> String) {
|
||||
if (tc != expected)
|
||||
fail(tokenPos, lazyErrorMsg())
|
||||
}
|
||||
val canBeginValue: Boolean
|
||||
get() = when (tc) {
|
||||
TC_BEGIN_LIST, TC_BEGIN_OBJ, TC_OTHER, TC_STRING, TC_NULL -> true
|
||||
else -> false
|
||||
}
|
||||
fun takeStr(): String {
|
||||
if (tc != TC_OTHER && tc != TC_STRING) fail(tokenPos, "Expected string or non-null literal")
|
||||
val prevStr = if (offset < 0)
|
||||
String(buf, 0, length) else
|
||||
source.substring(offset, offset + length)
|
||||
nextToken()
|
||||
return prevStr
|
||||
}
|
||||
private fun append(ch: Char) {
|
||||
if (length >= buf.size) buf = buf.copyOf(2 * buf.size)
|
||||
buf[length++] = ch
|
||||
}
|
||||
// initializes buf usage upon the first encountered escaped char
|
||||
private fun appendRange(source: String, fromIndex: Int, toIndex: Int) {
|
||||
val addLen = toIndex - fromIndex
|
||||
val oldLen = length
|
||||
val newLen = oldLen + addLen
|
||||
if (newLen > buf.size) buf = buf.copyOf(newLen.coerceAtLeast(2 * buf.size))
|
||||
for (i in 0 until addLen) buf[oldLen + i] = source[fromIndex + i]
|
||||
length += addLen
|
||||
}
|
||||
fun nextToken() {
|
||||
val source = source
|
||||
var curPos = curPos
|
||||
val maxLen = source.length
|
||||
while (true) {
|
||||
if (curPos >= maxLen) {
|
||||
tokenPos = curPos
|
||||
tc = TC_EOF
|
||||
return
|
||||
}
|
||||
val ch = source[curPos]
|
||||
val tc = charToTokenClass(ch)
|
||||
when (tc) {
|
||||
TC_WS -> curPos++ // skip whitespace
|
||||
TC_OTHER -> {
|
||||
nextLiteral(source, curPos)
|
||||
return
|
||||
}
|
||||
TC_STRING -> {
|
||||
nextString(source, curPos)
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
this.tokenPos = curPos
|
||||
this.tc = tc
|
||||
this.curPos = curPos + 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun nextLiteral(source: String, startPos: Int) {
|
||||
tokenPos = startPos
|
||||
offset = startPos
|
||||
var curPos = startPos
|
||||
val maxLen = source.length
|
||||
while (true) {
|
||||
curPos++
|
||||
if (curPos >= maxLen || charToTokenClass(source[curPos]) != TC_OTHER) break
|
||||
}
|
||||
this.curPos = curPos
|
||||
length = curPos - offset
|
||||
tc = if (rangeEquals(source, offset, length, NULL)) TC_NULL else TC_OTHER
|
||||
}
|
||||
private fun nextString(source: String, startPos: Int) {
|
||||
tokenPos = startPos
|
||||
length = 0 // in buffer
|
||||
var curPos = startPos + 1
|
||||
var lastPos = curPos
|
||||
val maxLen = source.length
|
||||
parse@ while (true) {
|
||||
if (curPos >= maxLen) fail(curPos, "Unexpected end in string")
|
||||
if (source[curPos] == STRING) {
|
||||
break@parse
|
||||
} else if (source[curPos] == STRING_ESC) {
|
||||
appendRange(source, lastPos, curPos)
|
||||
val newPos = appendEsc(source, curPos + 1)
|
||||
curPos = newPos
|
||||
lastPos = newPos
|
||||
} else {
|
||||
curPos++
|
||||
}
|
||||
}
|
||||
if (lastPos == startPos + 1) {
|
||||
// there was no escaped chars
|
||||
this.offset = lastPos
|
||||
this.length = curPos - lastPos
|
||||
} else {
|
||||
// some escaped chars were there
|
||||
appendRange(source, lastPos, curPos)
|
||||
this.offset = -1
|
||||
}
|
||||
this.curPos = curPos + 1
|
||||
tc = TC_STRING
|
||||
}
|
||||
private fun appendEsc(source: String, startPos: Int): Int {
|
||||
var curPos = startPos
|
||||
require(curPos < source.length, curPos) { "Unexpected end after escape char" }
|
||||
val curChar = source[curPos++]
|
||||
if (curChar == UNICODE_ESC) {
|
||||
curPos = appendHex(source, curPos)
|
||||
} else {
|
||||
val c = escapeToChar(curChar.toInt())
|
||||
require(c != INVALID, curPos) { "Invalid escaped char '$curChar'" }
|
||||
append(c)
|
||||
}
|
||||
return curPos
|
||||
}
|
||||
private fun appendHex(source: String, startPos: Int): Int {
|
||||
var curPos = startPos
|
||||
append(
|
||||
((fromHexChar(source, curPos++) shl 12) +
|
||||
(fromHexChar(source, curPos++) shl 8) +
|
||||
(fromHexChar(source, curPos++) shl 4) +
|
||||
fromHexChar(source, curPos++)).toChar()
|
||||
)
|
||||
return curPos
|
||||
}
|
||||
fun skipElement() {
|
||||
if (tc != TC_BEGIN_OBJ && tc != TC_BEGIN_LIST) {
|
||||
nextToken()
|
||||
return
|
||||
}
|
||||
val tokenStack = mutableListOf<Byte>()
|
||||
do {
|
||||
when (tc) {
|
||||
TC_BEGIN_LIST, TC_BEGIN_OBJ -> tokenStack.add(tc)
|
||||
TC_END_LIST -> {
|
||||
if (tokenStack.last() != TC_BEGIN_LIST) throw JsonParsingException(curPos, "found ] instead of }")
|
||||
tokenStack.removeAt(tokenStack.size - 1)
|
||||
}
|
||||
TC_END_OBJ -> {
|
||||
if (tokenStack.last() != TC_BEGIN_OBJ) throw JsonParsingException(curPos, "found } instead of ]")
|
||||
tokenStack.removeAt(tokenStack.size - 1)
|
||||
}
|
||||
}
|
||||
nextToken()
|
||||
} while (tokenStack.isNotEmpty())
|
||||
}
|
||||
}
|
||||
// Utility functions
|
||||
private fun fromHexChar(source: String, curPos: Int): Int {
|
||||
require(curPos < source.length, curPos) { "Unexpected end in unicode escape" }
|
||||
val curChar = source[curPos]
|
||||
return when (curChar) {
|
||||
in '0'..'9' -> curChar.toInt() - '0'.toInt()
|
||||
in 'a'..'f' -> curChar.toInt() - 'a'.toInt() + 10
|
||||
in 'A'..'F' -> curChar.toInt() - 'A'.toInt() + 10
|
||||
else -> fail(curPos, "Invalid toHexChar char '$curChar' in unicode escape")
|
||||
}
|
||||
}
|
||||
private fun rangeEquals(source: String, start: Int, length: Int, str: String): Boolean {
|
||||
val n = str.length
|
||||
if (length != n) return false
|
||||
for (i in 0 until n) if (source[start + i] != str[i]) return false
|
||||
return true
|
||||
}
|
||||
internal inline fun require(condition: Boolean, pos: Int, msg: () -> String) {
|
||||
if (!condition)
|
||||
fail(pos, msg())
|
||||
}
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
internal inline fun fail(pos: Int, msg: String): Nothing {
|
||||
throw JsonParsingException(pos, msg)
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
// A bit changed part of kotlinx.serialization plugin
|
||||
package org.jetbrains.report.json
|
||||
|
||||
class JsonTreeParser internal constructor(private val p: Parser) {
|
||||
|
||||
companion object {
|
||||
fun parse(input: String): JsonElement = JsonTreeParser(input).readFully()
|
||||
}
|
||||
|
||||
constructor(input: String) : this(Parser(input))
|
||||
|
||||
private fun readObject(): JsonElement {
|
||||
p.requireTc(TC_BEGIN_OBJ) { "Expected start of object" }
|
||||
p.nextToken()
|
||||
val result: MutableMap<String, JsonElement> = hashMapOf()
|
||||
while (true) {
|
||||
if (p.tc == TC_COMMA) p.nextToken()
|
||||
if (!p.canBeginValue) break
|
||||
val key = p.takeStr()
|
||||
p.requireTc(TC_COLON) { "Expected ':'" }
|
||||
p.nextToken()
|
||||
val elem = read()
|
||||
result[key] = elem
|
||||
}
|
||||
p.requireTc(TC_END_OBJ) { "Expected end of object" }
|
||||
p.nextToken()
|
||||
return JsonObject(result)
|
||||
}
|
||||
|
||||
private fun readValue(isString: Boolean): JsonElement {
|
||||
val str = p.takeStr()
|
||||
return JsonLiteral(str, isString)
|
||||
}
|
||||
|
||||
private fun readArray(): JsonElement {
|
||||
p.requireTc(TC_BEGIN_LIST) { "Expected start of array" }
|
||||
p.nextToken()
|
||||
val result: MutableList<JsonElement> = arrayListOf()
|
||||
while (true) {
|
||||
if (p.tc == TC_COMMA) p.nextToken()
|
||||
if (!p.canBeginValue) break
|
||||
val elem = read()
|
||||
result.add(elem)
|
||||
}
|
||||
p.requireTc(TC_END_LIST) { "Expected end of array" }
|
||||
p.nextToken()
|
||||
return JsonArray(result)
|
||||
}
|
||||
|
||||
fun read(): JsonElement {
|
||||
if (!p.canBeginValue) fail(p.curPos, "Can't begin reading value from here")
|
||||
val tc = p.tc
|
||||
return when (tc) {
|
||||
TC_NULL -> JsonNull.also { p.nextToken() }
|
||||
TC_STRING -> readValue(isString = true)
|
||||
TC_OTHER -> readValue(isString = false)
|
||||
TC_BEGIN_OBJ -> readObject()
|
||||
TC_BEGIN_LIST -> readArray()
|
||||
else -> fail(p.curPos, "Can't begin reading element")
|
||||
}
|
||||
}
|
||||
|
||||
fun readFully(): JsonElement {
|
||||
val r = read()
|
||||
p.requireTc(TC_EOF) { "Input wasn't consumed fully" }
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.report.json
|
||||
|
||||
private fun toHexChar(i: Int) : Char {
|
||||
val d = i and 0xf
|
||||
return if (d < 10) (d + '0'.toInt()).toChar()
|
||||
else (d - 10 + 'a'.toInt()).toChar()
|
||||
}
|
||||
|
||||
private val ESCAPE_CHARS: Array<String?> = arrayOfNulls<String>(128).apply {
|
||||
for (c in 0..0x1f) {
|
||||
val c1 = toHexChar(c shr 12)
|
||||
val c2 = toHexChar(c shr 8)
|
||||
val c3 = toHexChar(c shr 4)
|
||||
val c4 = toHexChar(c)
|
||||
this[c] = "\\u$c1$c2$c3$c4"
|
||||
}
|
||||
this['"'.toInt()] = "\\\""
|
||||
this['\\'.toInt()] = "\\\\"
|
||||
this['\t'.toInt()] = "\\t"
|
||||
this['\b'.toInt()] = "\\b"
|
||||
this['\n'.toInt()] = "\\n"
|
||||
this['\r'.toInt()] = "\\r"
|
||||
this[0x0c] = "\\f"
|
||||
}
|
||||
|
||||
internal fun StringBuilder.printQuoted(value: String) {
|
||||
append(STRING)
|
||||
var lastPos = 0
|
||||
val length = value.length
|
||||
for (i in 0 until length) {
|
||||
val c = value[i].toInt()
|
||||
// Do not replace this constant with C2ESC_MAX (which is smaller than ESCAPE_CHARS size),
|
||||
// otherwise JIT won't eliminate range check and won't vectorize this loop
|
||||
if (c >= ESCAPE_CHARS.size) continue // no need to escape
|
||||
val esc = ESCAPE_CHARS[c] ?: continue
|
||||
append(value, lastPos, i) // flush prev
|
||||
append(esc)
|
||||
lastPos = i + 1
|
||||
}
|
||||
append(value, lastPos, length)
|
||||
append(STRING)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the contents of this string is equal to the word "true", ignoring case, `false` if content equals "false",
|
||||
* and throws [IllegalStateException] otherwise.
|
||||
*/
|
||||
fun String.toBooleanStrict(): Boolean = toBooleanStrictOrNull() ?: throw IllegalStateException("$this does not represent a Boolean")
|
||||
|
||||
/**
|
||||
* Returns `true` if the contents of this string is equal to the word "true", ignoring case, `false` if content equals "false",
|
||||
* and returns `null` otherwise.
|
||||
*/
|
||||
fun String.toBooleanStrictOrNull(): Boolean? = when {
|
||||
this.equals("true", ignoreCase = true) -> true
|
||||
this.equals("false", ignoreCase = true) -> false
|
||||
else -> null
|
||||
}
|
||||
Reference in New Issue
Block a user