Skip to content

Commit a95f767

Browse files
Abduqodiri Qurbonzodaqurbonzoda
authored andcommitted
Fix IntelliJ progress reporting in Native
1 parent 949c391 commit a95f767

File tree

9 files changed

+100
-53
lines changed

9 files changed

+100
-53
lines changed

plugin/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ sourceSets {
6565
}
6666
}
6767

68+
compileKotlin {
69+
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
70+
}
71+
6872
dependencies {
6973
compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
7074

plugin/main/src/kotlinx/benchmark/gradle/NativeMultiplatformTasks.kt

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import org.gradle.api.tasks.*
55
import org.jetbrains.kotlin.gradle.plugin.mpp.*
66
import org.jetbrains.kotlin.konan.target.*
77
import java.io.File
8-
import kotlin.io.path.ExperimentalPathApi
9-
import kotlin.io.path.createTempFile
8+
import java.nio.file.Path
9+
import kotlin.io.path.*
1010

1111
fun Project.processNativeCompilation(target: NativeBenchmarkTarget) {
1212
val compilation = target.compilation
@@ -97,6 +97,7 @@ private fun Project.createNativeBenchmarkCompileTask(target: NativeBenchmarkTarg
9797
return benchmarkCompilation
9898
}
9999

100+
@OptIn(ExperimentalPathApi::class)
100101
fun Project.createNativeBenchmarkExecTask(
101102
config: BenchmarkConfiguration,
102103
target: NativeBenchmarkTarget,
@@ -118,6 +119,7 @@ fun Project.createNativeBenchmarkExecTask(
118119
executable = executableFile.absolutePath
119120
this.config = config
120121
this.workingDir = target.workingDir?.let { File(it) }
122+
this.benchProgressPath = createTempFile("bench", ".txt").absolutePath
121123

122124
onlyIf { executableFile.exists() }
123125
benchsDescriptionDir = file(project.buildDir.resolve(target.extension.benchsDescriptionDir).resolve(config.name))
@@ -155,6 +157,9 @@ open class NativeBenchmarkExec() : DefaultTask() {
155157
@Input
156158
lateinit var benchsDescriptionDir: File
157159

160+
@Input
161+
lateinit var benchProgressPath: String
162+
158163
private fun execute(args: Collection<String>) {
159164
project.exec {
160165
it.executable = executable
@@ -164,11 +169,11 @@ open class NativeBenchmarkExec() : DefaultTask() {
164169
}
165170
}
166171

167-
@ExperimentalPathApi
172+
@OptIn(ExperimentalPathApi::class)
168173
@TaskAction
169174
fun run() {
170175
// Get full list of running benchmarks
171-
execute(listOf(configFile.absolutePath, "--list", benchsDescriptionDir.absolutePath))
176+
execute(listOf(configFile.absolutePath, "--list", benchProgressPath, benchsDescriptionDir.absolutePath))
172177
val detailedConfigFiles = project.fileTree(benchsDescriptionDir).files.sortedBy { it.absolutePath }
173178
val runResults = mutableMapOf<String, String>()
174179

@@ -180,8 +185,8 @@ open class NativeBenchmarkExec() : DefaultTask() {
180185

181186
// Execute benchmark
182187
if (config.nativeIterationMode == "internal") {
183-
val suiteResultsFile = createTempFile("bench", ".txt").toFile()
184-
execute(listOf(configFile.absolutePath, "--internal", runConfigPath, suiteResultsFile.absolutePath))
188+
val suiteResultsFile = createTempFile("bench", ".txt")
189+
execute(listOf(configFile.absolutePath, "--internal", benchProgressPath, runConfigPath, suiteResultsFile.absolutePath))
185190
val suiteResults = suiteResultsFile.readText()
186191
if (suiteResults.isNotEmpty())
187192
runResults[runConfigPath] = suiteResults
@@ -192,10 +197,10 @@ open class NativeBenchmarkExec() : DefaultTask() {
192197
.substringBefore(',').toInt()
193198
// Warm up
194199
var exceptionDuringExecution = false
195-
var textResult: File? = null
200+
var textResult: Path? = null
196201
for (i in 0 until warmups) {
197-
textResult = createTempFile("bench", ".txt").toFile()
198-
execute(listOf(configFile.absolutePath, "--warmup", runConfigPath, i.toString(), textResult.absolutePath))
202+
textResult = createTempFile("bench", ".txt")
203+
execute(listOf(configFile.absolutePath, "--warmup", benchProgressPath, runConfigPath, i.toString(), textResult.absolutePath))
199204
val result = textResult.readLines().getOrNull(0)
200205
if (result == "null") {
201206
exceptionDuringExecution = true
@@ -208,9 +213,9 @@ open class NativeBenchmarkExec() : DefaultTask() {
208213
val iterationResults = mutableListOf<Double>()
209214
var iteration = 0
210215
while (!exceptionDuringExecution && iteration in 0 until iterations) {
211-
val textResult = createTempFile("bench", ".txt").toFile()
216+
textResult = createTempFile("bench", ".txt")
212217
execute(
213-
listOf(configFile.absolutePath, "--iteration", runConfigPath, iteration.toString(),
218+
listOf(configFile.absolutePath, "--iteration", benchProgressPath, runConfigPath, iteration.toString(),
214219
cycles, textResult.absolutePath)
215220
)
216221
val result = textResult.readLines()[0]
@@ -221,22 +226,22 @@ open class NativeBenchmarkExec() : DefaultTask() {
221226
}
222227
// Store results
223228
if (iterationResults.size == iterations) {
224-
val iterationsResultsFile = createTempFile("bench_results").toFile()
225-
iterationsResultsFile.printWriter().use { out ->
229+
val iterationsResultsFile = createTempFile("bench_results")
230+
iterationsResultsFile.bufferedWriter().use { out ->
226231
out.write(iterationResults.joinToString { it.toString() })
227232
}
228233
execute(
229-
listOf(configFile.absolutePath, "--end-run", runConfigPath, iterationsResultsFile.absolutePath)
234+
listOf(configFile.absolutePath, "--end-run", benchProgressPath, runConfigPath, iterationsResultsFile.absolutePath)
230235
)
231236
runResults[runConfigPath] = iterationResults.joinToString()
232237
}
233238
}
234239
}
235240
// Merge results
236-
val samplesFile = createTempFile("bench_results").toFile()
237-
samplesFile.printWriter().use { out ->
241+
val samplesFile = createTempFile("bench_results")
242+
samplesFile.bufferedWriter().use { out ->
238243
out.write(runResults.toList().joinToString("\n") { "${it.first}: ${it.second}"})
239244
}
240-
execute(listOf(configFile.absolutePath, "--store-results", samplesFile.absolutePath))
245+
execute(listOf(configFile.absolutePath, "--store-results", benchProgressPath, samplesFile.absolutePath))
241246
}
242247
}

plugin/main/src/kotlinx/benchmark/gradle/Utils.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import org.gradle.api.Project
66
import org.gradle.api.Task
77
import org.gradle.api.invocation.Gradle
88
import org.gradle.api.tasks.TaskProvider
9-
import org.gradle.util.GradleVersion
109
import java.io.File
10+
import java.nio.file.Path
1111
import java.time.LocalDateTime
1212
import java.time.format.DateTimeFormatter
13+
import kotlin.io.path.ExperimentalPathApi
14+
import kotlin.io.path.invariantSeparatorsPath
1315

1416
fun cleanup(file: File) {
1517
if (file.exists()) {
@@ -91,6 +93,9 @@ fun Task.traceFormat(): String {
9193
return if (ideaActive) "xml" else "text"
9294
}
9395

96+
@OptIn(ExperimentalPathApi::class)
97+
val Path.absolutePath: String get() = toAbsolutePath().invariantSeparatorsPath
98+
9499
fun writeParameters(
95100
name: String,
96101
reportFile: File,

runtime/commonMain/src/kotlinx/benchmark/BenchmarkProgress.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ abstract class BenchmarkProgress {
1010
abstract fun output(suite: String, benchmark: String, message: String)
1111

1212
companion object {
13-
fun create(format: String) = when (format) {
13+
fun create(format: String, xml: (() -> BenchmarkProgress)? = null) = when (format) {
1414
"xml" -> {
15-
IntelliJBenchmarkProgress()
15+
xml?.invoke() ?: IntelliJBenchmarkProgress()
1616
}
1717
"text" -> {
1818
ConsoleBenchmarkProgress()
@@ -27,7 +27,7 @@ abstract class BenchmarkProgress {
2727
}
2828
}
2929

30-
class IntelliJBenchmarkProgress : BenchmarkProgress() {
30+
open class IntelliJBenchmarkProgress : BenchmarkProgress() {
3131
private val rootId = "[root]"
3232

3333
override fun startSuite(suite: String) {
@@ -45,9 +45,9 @@ class IntelliJBenchmarkProgress : BenchmarkProgress() {
4545
println(ijSuiteFinish("", rootId, suiteStatus))
4646
}
4747

48-
private var currentClass = ""
49-
private var currentStatus = FinishStatus.Success
50-
private var suiteStatus = FinishStatus.Success
48+
protected var currentClass = ""
49+
protected var currentStatus = FinishStatus.Success
50+
protected var suiteStatus = FinishStatus.Success
5151

5252
override fun startBenchmark(suite: String, benchmark: String) {
5353
val methodName = benchmark.substringAfterLast('.')

runtime/commonMain/src/kotlinx/benchmark/SuiteExecutor.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package kotlinx.benchmark
22

3-
abstract class SuiteExecutor(val executionName: String, arguments: Array<out String>) {
4-
private val config = RunnerConfiguration(arguments.first().readFile())
5-
6-
protected val additionalArguments = arguments.drop(1)
3+
abstract class SuiteExecutor(
4+
val executionName: String,
5+
configPath: String,
6+
xmlReporter: (() -> BenchmarkProgress)? = null
7+
) {
8+
private val config = RunnerConfiguration(configPath.readFile())
79

8-
val reporter = BenchmarkProgress.create(config.traceFormat)
10+
val reporter = BenchmarkProgress.create(config.traceFormat, xmlReporter)
911

1012
private val reportFormatter = BenchmarkReportFormatter.create(config.reportFormat)
1113

runtime/jsMain/src/kotlinx/benchmark/js/JsExecutor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import kotlinx.benchmark.*
44
import kotlin.js.Promise
55

66
class JsExecutor(name: String, @Suppress("UNUSED_PARAMETER") dummy_args: Array<out String>) :
7-
SuiteExecutor(name, (process["argv"] as Array<String>).drop(2).toTypedArray()) {
7+
SuiteExecutor(name, (process["argv"] as Array<String>)[2]) {
88

99
private val benchmarkJs: dynamic = require("benchmark")
1010

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package kotlinx.benchmark
2+
3+
// This implementation is used in Kotlin/Native target, where each benchmark is run in a separate process.
4+
// Previous benchmark id is stored to identify when previous suite (benchmark class) ends.
5+
class NativeIntelliJBenchmarkProgress(
6+
private val benchProgressPath: String
7+
) : IntelliJBenchmarkProgress() {
8+
9+
init {
10+
decode()
11+
}
12+
13+
override fun startBenchmark(suite: String, benchmark: String) {
14+
super.startBenchmark(suite, benchmark)
15+
encode()
16+
}
17+
18+
override fun endBenchmarkException(suite: String, benchmark: String, error: String, stacktrace: String) {
19+
super.endBenchmarkException(suite, benchmark, error, stacktrace)
20+
encode()
21+
}
22+
23+
private fun encode() {
24+
writeFile(benchProgressPath, listOf(currentClass, currentStatus, suiteStatus).joinToString(separator = "\n"))
25+
}
26+
27+
private fun decode() {
28+
val content = benchProgressPath.readFile()
29+
if (content.isEmpty()) {
30+
return
31+
}
32+
33+
val (currentClass, currentStatus, suiteStatus) = content.lines()
34+
this.currentClass = currentClass
35+
this.currentStatus = FinishStatus.valueOf(currentStatus)
36+
this.suiteStatus = FinishStatus.valueOf(suiteStatus)
37+
}
38+
}

runtime/nativeMain/src/kotlinx/benchmark/Utils.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ actual fun String.readFile(): String = buildString {
4343
while (true) {
4444
val bufferLength = 64 * 1024
4545
val buffer = allocArray<ByteVar>(bufferLength)
46-
val line = fgets(buffer, bufferLength, file)?.toKString()
46+
val line = fgets(buffer, bufferLength, file)?.toKString() // newline symbol is included
4747
if (line == null || line.isEmpty()) break
48-
appendLine(line)
48+
append(line)
4949
}
5050
}
5151

runtime/nativeMain/src/kotlinx/benchmark/native/NativeExecutor.kt

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import kotlinx.benchmark.*
44
import kotlin.native.internal.GC
55
import kotlin.system.*
66

7-
class NativeExecutor(name: String, args: Array<out String>) : SuiteExecutor(name, args) {
7+
class NativeExecutor(
8+
name: String,
9+
args: Array<out String>
10+
) : SuiteExecutor(name, args[0], { NativeIntelliJBenchmarkProgress(args[2]) }) {
11+
12+
private val action = args[1]
13+
private val additionalArguments = args.drop(3)
814

915
data class BenchmarkRun(val benchmarkName: String, val config: BenchmarkConfiguration,
1016
val parameters: Map<String, String>)
@@ -23,7 +29,7 @@ class NativeExecutor(name: String, args: Array<out String>) : SuiteExecutor(name
2329
appendLine("configuration: $config")
2430
appendLine("parameters: $params")
2531
}
26-
val fileName = "${additionalArguments[1]}/${suite.name}_${it.name}_$parametersId.txt"
32+
val fileName = "${additionalArguments[0]}/${suite.name}_${it.name}_$parametersId.txt"
2733
writeFile(fileName, benchmarkRunConfig)
2834
parametersId++
2935
}
@@ -34,15 +40,15 @@ class NativeExecutor(name: String, args: Array<out String>) : SuiteExecutor(name
3440
?: throw NoSuchElementException("Benchmark $name wasn't found.")
3541

3642
private fun runBenchmarkIteration(benchmarks: List<BenchmarkDescriptor<Any?>>) {
37-
val (_, configFileName, iteration, cycles, resultsFile) = additionalArguments
43+
val (configFileName, iteration, cycles, resultsFile) = additionalArguments
3844
val benchmarkRun = configFileName.parseBenchmarkConfig()
3945
val benchmark = benchmarks.getBenchmark(benchmarkRun.benchmarkName)
4046
val samples = run(benchmark, benchmarkRun, iteration.toInt(), cycles.toInt())
4147
writeFile(resultsFile, samples?.let{ it[0].toString() } ?: "null")
4248
}
4349

4450
private fun runBenchmarkWarmup(benchmarks: List<BenchmarkDescriptor<Any?>>) {
45-
val (_, configFileName, iteration, resultsFile) = additionalArguments
51+
val (configFileName, iteration, resultsFile) = additionalArguments
4652
val benchmarkRun = configFileName.parseBenchmarkConfig()
4753
val benchmark = benchmarks.getBenchmark(benchmarkRun.benchmarkName)
4854
val id = id(benchmark.name, benchmarkRun.parameters)
@@ -69,7 +75,7 @@ class NativeExecutor(name: String, args: Array<out String>) : SuiteExecutor(name
6975
}
7076

7177
private fun runBenchmark(benchmarks: List<BenchmarkDescriptor<Any?>>) {
72-
val (_, configFileName, resultsFile) = additionalArguments
78+
val (configFileName, resultsFile) = additionalArguments
7379
val benchmarkRun = configFileName.parseBenchmarkConfig()
7480
val benchmark = benchmarks.getBenchmark(benchmarkRun.benchmarkName)
7581
val id = id(benchmark.name, benchmarkRun.parameters)
@@ -82,7 +88,7 @@ class NativeExecutor(name: String, args: Array<out String>) : SuiteExecutor(name
8288
}
8389

8490
private fun endExternalBenchmarksRun(benchmarks: List<BenchmarkDescriptor<Any?>>) {
85-
val (_, configFileName, samplesFile) = additionalArguments
91+
val (configFileName, samplesFile) = additionalArguments
8692
val samples = samplesFile.readFile().split(", ").map { it.toDouble() }.toDoubleArray()
8793
val benchmarkRun = configFileName.parseBenchmarkConfig()
8894
val benchmark = benchmarks.getBenchmark(benchmarkRun.benchmarkName)
@@ -153,8 +159,7 @@ class NativeExecutor(name: String, args: Array<out String>) : SuiteExecutor(name
153159
}
154160

155161
private fun storeResults(benchmarks: List<BenchmarkDescriptor<Any?>>, complete: () -> Unit) {
156-
val (_, resultsFileName) = additionalArguments
157-
resultsFileName.readFile().split("\n").filter { it.isNotEmpty() }.forEach {
162+
additionalArguments[0].readFile().lines().forEach {
158163
val (configFileName, samplesList) = it.split(": ")
159164
val samples = samplesList.split(", ").map { it.toDouble() }.toDoubleArray()
160165
val benchmarkRun = configFileName.parseBenchmarkConfig()
@@ -172,26 +177,14 @@ class NativeExecutor(name: String, args: Array<out String>) : SuiteExecutor(name
172177
start: () -> Unit,
173178
complete: () -> Unit
174179
) {
175-
val knownActions = mapOf(
176-
"--list" to 2,
177-
"--store-results" to 2,
178-
"--end-run" to 3,
179-
"--internal" to 3,
180-
"--iteration" to 5,
181-
"--warmup" to 4
182-
)
183-
184-
val action = additionalArguments.first()
185-
if (action !in knownActions.keys)
186-
throw NoSuchElementException("Action $action isn't found in the list of possible actions ${knownActions.keys}.")
187-
require(knownActions[action] == additionalArguments.size)
188180
when (action) {
189181
"--list" -> outputBenchmarks(runnerConfiguration, benchmarks, start)
190182
"--store-results" -> storeResults(benchmarks, complete)
191183
"--internal" -> runBenchmark(benchmarks)
192184
"--iteration" -> runBenchmarkIteration(benchmarks)
193185
"--warmup" -> runBenchmarkWarmup(benchmarks)
194186
"--end-run" -> endExternalBenchmarksRun(benchmarks)
187+
else -> throw IllegalArgumentException("Unknown action: $action.")
195188
}
196189
}
197190

0 commit comments

Comments
 (0)