Experiment: Avoid JSArrayConstr for Varargs to optimize the Wasm backend#5148
Closed
tanishiking wants to merge 1 commit intoscala-js:mainfrom
Closed
Experiment: Avoid JSArrayConstr for Varargs to optimize the Wasm backend#5148tanishiking wants to merge 1 commit intoscala-js:mainfrom
JSArrayConstr for Varargs to optimize the Wasm backend#5148tanishiking wants to merge 1 commit intoscala-js:mainfrom
Conversation
… Backend
Currently in Scala.js, varargs call like `List(1, 2, 3)`, it is translated into the IR form `js.WrappedArray(JSArrayConstr(...))`. That requires JS interop for constructing the array and accessing its elements.
Since Wasm-to-JS calls are expensive, this is undesirable for performance.
This commit experiments avoiding `JSArrayConstr` for varargs.
Instead, varargs are transformed into something like `new WrappedArray$ofInt(ArrayValue(1, 2, 3))` (or `new ArraySeq$ofInt(...)` on 2.13) to explore potential Wasm-specific optimizations.
Note1: While reducing JS interop can improve performance on the Wasm backend, the same does not apply the JS backend.
We'd need to re-optimize back to `JSArrayConstr`-based code during the Optimizer for the JS backend.
Note 2: How about `WrappedArray.make` instead of directly instantiating specialized `WrappedArray`?
I found that runtime type checks in `make` appear to be very slow, and in some micro-benchmarks, using `make` performed slightly worse than the original `JSArrayConstr`-based code.
---
Unfortunately, the performance improvements were negligible.
For example, in the following code:
```scala
def main(args: Array[String]): Unit = {
val startTime = System.nanoTime()
val xs = Seq(1, 2, ..., 20)
xs.foreach(x => assert(x > 0))
val endTime = System.nanoTime()
println(s"elapsed: ${endTime - startTime} ns")
}
```
Both versions (with and without `JSArrayConstr`) reported similar timings of ~540000–580000 ns.
Benchmarks run using [`sjrd/scalajs-benchmarks/wasm`](https://github.com/sjrd/scalajs-benchmarks/tree/wasm) also did not show any significant performance differences.
| Benchmark | before | after | Ratio (after / before) |
|-------------|--------------|--------------|----------------------|
| sha512 | 12403.95816 | 12737.42497 | 1.0269 |
| sha512int | 12259.02363 | 13313.69655 | 1.0860 |
| queens | 2.954778067 | 2.920396237 | 0.9873 |
| list | 60.56316878 | 60.52163829 | 0.9993 |
| richards | 87.49714448 | 87.77807725 | 1.0032 |
| cd | 32866.35461 | 31672.2486 | 0.9637 |
| gcbench | 104672.8678 | 121351.2553 | 1.1588 |
| tracerFloat | 870.5680015 | 876.4962162 | 1.0068 |
| tracer | 784.3968099 | 783.2297365 | 0.9985 |
| sudoku | 3634.165046 | 3609.813857 | 0.9933 |
| nbody | 23722.03084 | 24192.39211 | 1.0198 |
| permute | 262.9023071 | 265.949228 | 1.0116 |
| deltaBlue | 525.4683864 | 511.8722724 | 0.9742 |
| kmeans | 206339.5187 | 202615.0022 | 0.9820 |
| brainfuck | 2352.518883 | 2357.064959 | 1.0019 |
| json | 288.1723513 | 280.1001847 | 0.9720 |
| bounce | 33.12443674 | 33.01821262 | 0.9978 |
There may still be room for further optimization in the non-`JSArrayConstr` implementation.
JSArrayConstr for Varargs to optimize the Wasm backendJSArrayConstr for Varargs to optimize the Wasm backend
JSArrayConstr for Varargs to optimize the Wasm backendJSArrayConstr for Varargs to optimize the Wasm backend
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is more like an experiment report for discuss based on the results of this change.
Currently in Scala.js, varargs call like
List(1, 2, 3), it is translated into the IR formjs.WrappedArray(JSArrayConstr(...)). That requires JS interop for constructing the array and accessing its elements. Since Wasm-to-JS calls are expensive, this is undesirable for performance.This commit experiments avoiding
JSArrayConstrfor varargs. Instead, varargs are transformed into something likenew WrappedArray$ofInt(ArrayValue(1, 2, 3))(ornew ArraySeq$ofInt(...)on 2.13) to explore potential Wasm-specific optimizations.Note1: While reducing JS interop can improve performance on the Wasm backend, the same does not apply the JS backend. We'd need to re-optimize back to
JSArrayConstr-based code during the Optimizer for the JS backend.Note 2: How about
WrappedArray.makeinstead of directly instantiating specializedWrappedArray? I found that runtime type checks inmakeappear to be very slow, and in some micro-benchmarks, usingmakeperformed slightly worse than the originalJSArrayConstr-based code.Unfortunately, the performance improvements were negligible.
For example, in the following code:
Both versions (with and without
JSArrayConstr) reported similar timings of ~540000–580000 ns.Benchmarks run using
sjrd/scalajs-benchmarks/wasmalso did not show any significant performance differences.(It might be because there's not so much varargs in the benchmark)
There may still be room for further optimization in the non-
JSArrayConstrimplementation.