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:
LepilkinaElena
2018-12-21 14:35:23 +03:00
committed by GitHub
parent 8d00a1ebcf
commit 828b2702bf
25 changed files with 1836 additions and 249 deletions
+104 -51
View File
@@ -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 {
+35
View File
@@ -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"]
+21
View File
@@ -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
}
}
}
+11
View File
@@ -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
View File
+1 -1
View File
@@ -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)
}
+3
View File
@@ -0,0 +1,3 @@
package org.jetbrains.ring
expect fun cleanup()
+11 -2
View File
@@ -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
View File
@@ -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
}