Skip to content

Implement Wasm WASI target and fork mode for WasmJs with custom engines#361

Open
igoriakovlev wants to merge 8 commits intomasterfrom
yakovlev/wasm_fork_support
Open

Implement Wasm WASI target and fork mode for WasmJs with custom engines#361
igoriakovlev wants to merge 8 commits intomasterfrom
yakovlev/wasm_fork_support

Conversation

@igoriakovlev
Copy link
Copy Markdown
Contributor

@igoriakovlev igoriakovlev commented Mar 31, 2026

Here we have several major changes:
First of all I have removed d8 support from gradle plugin (it was needed for internal use only)
Second I have added Wasm WASI target (runned with nodejs)
Third implement wasmFork = perBenchmark. User can use it as in other targets, like in Native.
Fourth - support WasmJs custom engines - it supposed to be used internally.
Fifth - as we now have now custom engines I remove internal d8 engine support from the Runtime.

(fixed 287)

@igoriakovlev igoriakovlev requested a review from fzhinkin April 1, 2026 13:07
@igoriakovlev igoriakovlev force-pushed the yakovlev/wasm_fork_support branch 2 times, most recently from 8576f98 to c07a932 Compare April 1, 2026 15:01
}

else -> throw IllegalArgumentException("Invalid advanced option name: '$param'. Accepted options: \"nativeFork\", \"nativeGCAfterIteration\", \"jvmForks\", \"jsUseBridge\".")
"wasmFork" -> require(value is String) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please validate the value of the option like for the nativeFork

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (compilationTarget.isNodejsConfigured) {
val execTask = createNodeJsExec(config, target, compilation, taskName)
tasks.getByName(config.prefixName(RUN_BENCHMARKS_TASKNAME)).dependsOn(execTask)
val execTask = createNodeJsExec(config, target, binary.compilation, executableFile, taskName)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the dependency on the link task:

execTask.configure { it.dependsOn(binary.linkTask) }

It's one of those warning our tests were printing in CI

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (compilationTarget.isNodejsConfigured) {
val execTask = createNodeJsExec(config, target, compilation, taskName)
tasks.getByName(config.prefixName(RUN_BENCHMARKS_TASKNAME)).dependsOn(execTask)
val execTask = createNodeJsExec(config, target, binary.compilation, executableFile, taskName)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the dependency on the link task:

execTask.configure { it.dependsOn(binary.linkTask) }

It's one of those warning our tests were printing in CI

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

import kotlin.text.replaceFirstChar

@KotlinxBenchmarkPluginInternalApi
data class CustomEngine(val name: String, val enginePath: Provider<RegularFile>, val engineArguments: Provider<String>)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really internal? If so, it should not be exposed via BenchmarkConfiguration.

Also, please make it a regular class, not a data class.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternative option: keep the class private, add a function customJsEngine(...) that will pack all the arguments to advanced. Also, it seems crucial to explicitly specify that the engine is the Js/WasmJs/WasmWasi engine. Maybe, the runtime or VM would be a better name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is internal because not intended to used by the user. Could you please to
reveal an idea about this function customJsEngine? Where it should be placed, with which visibility? Any opt-ins? Etc. Thank you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

import kotlin.text.replaceFirstChar

@KotlinxBenchmarkPluginInternalApi
data class CustomEngine(val name: String, val enginePath: Provider<RegularFile>, val engineArguments: Provider<String>)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why engineArguments are string, and not a list of strings?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well. It is not very handy to have a list when you deal with Providers. Should it be a provider if list? or list of providers? or list of providers of strings? :)
For us (in wasm) to have this function the best way to express our needs is just a provider of string, But if you have a better idea from library point of view i would love to implement it.

Copy link
Copy Markdown
Contributor Author

@igoriakovlev igoriakovlev Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will make it with Provider

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@igoriakovlev igoriakovlev force-pushed the yakovlev/wasm_fork_support branch from 6dc4298 to 3413d05 Compare April 9, 2026 15:56
@igoriakovlev igoriakovlev force-pushed the yakovlev/wasm_fork_support branch 3 times, most recently from e7ef665 to bb3b759 Compare April 9, 2026 17:54
@igoriakovlev igoriakovlev force-pushed the yakovlev/wasm_fork_support branch from bb3b759 to 675d01d Compare April 10, 2026 14:38
Copy link
Copy Markdown
Collaborator

@fzhinkin fzhinkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few questions popped up during the last review round, but otherwise I'm OK with current implementation given that it is effectively non-public.

import kotlinx.benchmark.internal.KotlinxBenchmarkRuntimeInternalApi

/**
* Runs a complete benchmark suite by iterating over all configured benchmarks and their parameter combinations.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it runs the whole suite, then what's the difference with SuiteExecutor? Should they be a single class / interface? Is there any relation between them?

* - [runBenchmark] should return raw sample values in nanoseconds, or `null` if the benchmark produced no result
* - [saveBenchmarkResults] converts raw samples, records the formatted result, and notifies [reporter]
*/
@KotlinxBenchmarkRuntimeInternalApi
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make the interface internal instead? After you introduced KotilnxBenchmarkRuntimeExperimentalApi annotation, can everything that is still annotated with KotlinxBenchmarkRuntimeInternalApi be converted into internal?

* - [saveBenchmarkResults] converts raw samples, records the formatted result, and notifies [reporter]
*/
@KotlinxBenchmarkRuntimeInternalApi
interface RunAllBenchmarksExtension {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where the Extension comes from? Here, and in CommonBenchmarkExtension. Why the latter can not remain CommonBenchmarkExecutor and this interface be SuiteExecutor?

package kotlinx.benchmark

@KotlinxBenchmarkRuntimeExperimentalApi
abstract class Measurer {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some doc describing how Measurer, BenchmarkEngineSupport and overrideEngineSupport are related, why are they needed and how to implement them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wasm WASI target support

2 participants