Node 26 and enable Scala.js stress tests#178
Conversation
Update to Node 26 that updated to 14.6.202.33, that fixed V8 issue discussed in lampepfl#165.
`1L to total` produce `NumericRange[Long]` instead of an Int `Range`. `NumericRange[Long]` iterates through boxed values, and that makes async-heavy loop even slower.
|
Looks like there's an OOM error 🤔 |
|
Oh, |
Previously, a `WasmAsyncSupport.scheduleBoundary` created
2-nested `js.async` where outer `js.async` does nothing.
`WasmAsyncSupport.scheduleBoundary` ran schelduled
bodies by `execute(() => boundary(body))` inherited from
the implementation from `AsyncSupport`.
`JSAsyncScheduler` implements `execute` using `js.async`:
```scala
object JsAsyncScheduler extends Scheduler:
def execute(body: Runnable) = js.async(body.run())
```
and, on the other hand, boundary also call `js.async`
```scala
override def boundary[T](body: Label[T] ?=> T): T =
val label = WasmLabel[T]()
js.async:
val r = body(using label)
label.resolve(r) // see [[WasmLabel]]
js.await(label.promise)
```
As a result, each `Future` body was started as something like:
```scala
js.async {
val label = WasmLabel[T]()
js.async {
val r = body(using label)
label.resolve(r)
}
js.await(label.promise)
}
```
The outer async block almost do nothing: it only waits until the
inner body first suspends or completes, and then exists.
In stress tests that create thousands of `Future`s, and this
async boundary creates many Promise/JSPI continuations and JS-Wasm
boundary, which makes significant slow-down and memory consumption.
**fix**
This commit fixes the problem by removing the unnecessary outer
`js.async` block. Now `scheduleBoundary` is implemented like:
```scala
val label = WasmLabel[Unit]()
s.execute: () =>
body(using label)
label.resolve(())
```
so the `Future` would be
```scala
val label = WasmLabel[Unit]()
js.async {
body(using label)
label.resolve(())
}
```
Now, stress test consumes around 900MB of memory (previously it takes
around 3.5GB~).
|
I found that |
| * this assumes that the root context is **already** under `js.async`. | ||
| */ | ||
| trait WasmJSPISuspend(using AsyncToken) extends SuspendSupport: | ||
| trait WasmJSPISuspend(using AsyncToken) extends AsyncSupport: |
There was a problem hiding this comment.
I guess it makes sense, because JSPI anyway supports Async wether you like it or not ?
There was a problem hiding this comment.
I think it makes sense yeah! No reason to split this in the case of JSPI
|
Now, c220e96 is negligible on my local environment, maybe we can revert that. |
I think we can keep it, it makes sense to use ints for these loops anyway |
|
Wow, now test-js finishes in 1m30s, which previously took 3m30s~4m 🎉 |
Update to Node 26 that updated to 14.6.202.33, that fixed V8 issue discussed in #165.
edit: now StressTest doesn't fail with stack overflow, but it is still extremely slow on V8 🤔 -> It looks like loop over Long generates
NumericRange[Long]that takes so long. Using Integer instead, and now tests finishes approximately 6min. maybe worth investigating on scala-js side.