-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathJsExecutor.kt
134 lines (117 loc) · 5.76 KB
/
JsExecutor.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package kotlinx.benchmark.js
import kotlinx.benchmark.*
import kotlin.js.Promise
class JsExecutor(name: String, @Suppress("UNUSED_PARAMETER") dummy_args: Array<out String>) :
SuiteExecutor(name, (process["argv"] as Array<String>).drop(5).toTypedArray()) {
private val benchmarkJs: dynamic = require("benchmark")
override fun run(
runnerConfiguration: RunnerConfiguration,
reporter: BenchmarkProgress,
benchmarks: List<BenchmarkDescriptor<Any?>>,
complete: () -> Unit
) {
val jsSuite: dynamic = benchmarkJs.Suite()
jsSuite.on("complete") {
complete()
}
benchmarks.forEach { benchmark ->
val suite = benchmark.suite
if (suite.hasInvocationFixture) {
throw UnsupportedOperationException("Fixture methods with `Invocation` level are not supported")
}
val config = BenchmarkConfiguration(runnerConfiguration, suite)
val jsDescriptor = benchmark as JsBenchmarkDescriptor
runWithParameters(suite.parameters, runnerConfiguration.params, suite.defaultParameters) { params ->
val id = id(benchmark.name, params)
@Suppress("UNCHECKED_CAST")
val function = benchmark.function
val instance = suite.factory() // TODO: should we create instance per bench or per suite?
suite.parametrize(instance, params)
val asynchronous = if (jsDescriptor.async) {
@Suppress("UNCHECKED_CAST")
val promiseFunction = function as Any?.() -> Promise<*>
jsSuite.add(benchmark.name) { deferred: Promise<Unit> ->
// Mind asDynamic: this is **not** a regular promise
instance.promiseFunction().then { (deferred.asDynamic()).resolve() }
}
true
} else {
jsSuite.add(benchmark.name) { instance.function() }
false
}
val jsBenchmark = jsSuite[jsSuite.length - 1] // take back last added benchmark and subscribe to events
// TODO: Configure properly
// initCount: The default number of times to execute a test on a benchmark’s first cycle
// minTime: The time needed to reduce the percent uncertainty of measurement to 1% (secs).
// maxTime: The maximum time a benchmark is allowed to run before finishing (secs).
jsBenchmark.options.initCount = config.warmups
jsBenchmark.options.minSamples = config.iterations
val iterationSeconds = config.iterationTime * config.iterationTimeUnit.toSecondsMultiplier()
jsBenchmark.options.minTime = iterationSeconds
jsBenchmark.options.maxTime = iterationSeconds
jsBenchmark.options.async = asynchronous
jsBenchmark.options.defer = asynchronous
jsBenchmark.on("start") { event ->
reporter.startBenchmark(executionName, id)
suite.trialSetup(instance)
suite.iterationSetup(instance)
}
var iteration = 0
jsBenchmark.on("cycle") { event ->
val target = event.target
val nanos = (target.times.period as Double) * BenchmarkTimeUnit.SECONDS.toMultiplier()
val sample = nanos.nanosToText(config.mode, config.outputTimeUnit)
// (${target.cycles} × ${target.count} calls) -- TODO: what's this?
reporter.output(
executionName,
id,
"Iteration #${iteration++}: $sample"
)
suite.iterationTearDown(instance)
suite.iterationSetup(instance)
}
jsBenchmark.on("complete") { event ->
suite.iterationTearDown(instance)
suite.trialTearDown(instance)
val stats = event.target.stats
val samples = stats.sample
.unsafeCast<DoubleArray>()
.map {
val nanos = it * BenchmarkTimeUnit.SECONDS.toMultiplier()
nanos.nanosToSample(config.mode, config.outputTimeUnit)
}
.toDoubleArray()
val result = ReportBenchmarksStatistics.createResult(benchmark, params, config, samples)
val message = with(result) {
" ~ ${score.sampleToText(
config.mode,
config.outputTimeUnit
)} ±${(error / score * 100).formatSignificant(2)}%"
}
val error = event.target.error
if (error == null) {
reporter.endBenchmark(
executionName,
id,
BenchmarkProgress.FinishStatus.Success,
message
)
result(result)
} else {
val stacktrace = error.stack
reporter.endBenchmarkException(
executionName,
id,
error.toString(),
stacktrace.toString()
)
}
}
Unit
}
}
jsSuite.run()
}
}
external fun require(module: String): dynamic
private val process = require("process")