Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
50b7feb
wip
buntec May 26, 2022
b02d86e
wip
buntec May 26, 2022
28f9e08
fix unit tests
buntec May 26, 2022
3fdc941
all tests pass
buntec May 26, 2022
a94b236
wip
buntec May 26, 2022
197df34
fixes bug
buntec May 27, 2022
828ce2b
use List instead of Array for child nodes
buntec May 27, 2022
ad1c835
all tests pass
buntec May 27, 2022
a192025
bug fix
buntec May 27, 2022
c070689
fix event listener handling
buntec May 27, 2022
a5c76bd
some cleanup
buntec May 27, 2022
25f1c25
improve `updateChildren`
buntec May 27, 2022
8ef782d
make thunk render functions take `Any`
buntec May 27, 2022
4344bfb
add new unit test
buntec May 27, 2022
5aae324
improve `updateChildren`; add very simple micro benchmark comparison …
buntec May 27, 2022
0208cad
minor change in benchmark
buntec May 27, 2022
f5def44
clean up modules; inline `DomApi` methods
buntec May 27, 2022
b831b44
improve `updateChildren` to guarantee patching of keyed nodes
buntec May 28, 2022
01a666b
add another randomized test
buntec May 28, 2022
07d1972
run `githubWorkflowGenerate`
buntec May 28, 2022
f114581
add missing headers
buntec May 28, 2022
5f66616
bump base version to 0.2
buntec May 28, 2022
0bdc6f0
clean up; add comments to `updateChildren`; remove unused props
buntec May 28, 2022
f7ce50c
clean up
buntec May 28, 2022
4d8d312
fix compiler warning
buntec May 28, 2022
c88bd6e
add another micro benchmark
buntec May 29, 2022
b5021aa
clean up
buntec May 29, 2022
7033612
add scalacheck-based test; reusing same sequence of keys is *not* wor…
buntec May 29, 2022
6611d18
fix key handling in scalacheck test case generation - and boom it fails!
buntec May 29, 2022
761d649
fixes bug in `updateChildren`
buntec May 29, 2022
e8720eb
fix Scala 3 compilation error
buntec May 29, 2022
0d87944
expand scalacheck-based tests
buntec Jun 5, 2022
14e009b
increase timeout for scalacheck-based tests
buntec Jun 5, 2022
7954e24
remove redundant `key` field
buntec Jun 6, 2022
72574ae
introduce async boundaries and macrotask EC to avoid rendering timeou…
buntec Jun 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p examples/target target .js/target snabbdom/target .jvm/target .native/target project/target
run: mkdir -p benchmarks/target examples/target target .js/target snabbdom/target .jvm/target .native/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar examples/target target .js/target snabbdom/target .jvm/target .native/target project/target
run: tar cf targets.tar benchmarks/target examples/target target .js/target snabbdom/target .jvm/target .native/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down
11 changes: 11 additions & 0 deletions benchmarks/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head></head>
<body>
<script type="text/javascript" src="target/scala-2.13/scala-js-snabbdom-benchmarks-opt/main.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js" integrity="sha512-fsn00AJc9Kmi16dMwiRuAs/oCrK5tFYwILcP8tBxMZsp7zqKnOloeHZ3TzeG1WB+LCt0D1rV50tMbUYYmjlNGg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="module" src="index.js"></script>
<p>See console</p>
<div id="container"></div>
</body>
</html>
85 changes: 85 additions & 0 deletions benchmarks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { init, attributesModule, classModule, propsModule, styleModule, eventListenersModule, datasetModule, h } from 'https://cdn.jsdelivr.net/npm/snabbdom/+esm'

const patch = init([
attributesModule,
classModule,
propsModule,
styleModule,
eventListenersModule,
datasetModule
]);

function benchmark1(root) {

let vnode0 = h("div", {})
let vnode1 = h("div", {}, [h("span", "1"), h("span", "2"), h("span", "3")]);
let vnode2 = h("div", {}, [h("span", "2"), h("span", "3")]);
let vnode3 = h("div", {}, [h("span", "3")]);
let vnode4 = h("div", {}, [h("span", "2"), h("span", "3")]);
let vnode5 = h("div", {}, [h("span", "1"), h("span", "2"), h("span", "3")]);
let vnode6 = h("div", {}, [h("span", "0"), h("span", "1"), h("span", "2"), h("span", "3")]);
let vnode7 = h("div", {}, [h("span", "0"), h("span", "1"), h("span", "2"), h("span", "3"), h("span", "4")]);

patch(root, vnode0);
patch(vnode0, vnode1);
patch(vnode1, vnode2);
patch(vnode2, vnode3);
patch(vnode3, vnode4);
patch(vnode4, vnode5);
patch(vnode5, vnode6);
patch(vnode6, vnode7);

}

function benchmark2(root) {

let vnode0 = h("div", {})
let vnode1 = h("div", {}, [h("span", { key: "1" }, "1"), h("span", { key: "2" }, "2"), h("span", { key: "3" }, "3")]);
let vnode2 = h("div", {}, [h("span", { key: "2" }, "2a"), h("span", { key: "3" }, "3a")]);
let vnode3 = h("div", {}, [h("span", { key: "3" }, "3b")]);
let vnode4 = h("div", {}, [h("span", { key: "2" }, "2c"), h("span", { key: "3" }, "3c")]);
let vnode5 = h("div", {}, [h("span", { key: "1" }, "1d"), h("span", { key: "2" }, "2d"), h("span", { key: "3" }, "3d"), h("span", "b")]);
let vnode6 = h("div", {}, [h("span", { key: "1" }, "1e"), h("span", { key: "3" }, "3e"), h("span", { key: "2" }, "2e")]);
let vnode7 = h("div", {}, [h("span", "a"), h("span", "b"), h("span", { key: "1" }, "1f"), h("span", { key: "2f" }, "2"), h("span", { key: "3f" }, "3")]);
let vnode8 = h("div", {}, [h("span", { key: "1" }, "1g"), h("span", { key: "2" }, "2g"), h("span", { key: "3" }, "3g"), h("span", { key: "4" }, "4g")]);

patch(root, vnode0);
patch(vnode0, vnode1);
patch(vnode1, vnode2);
patch(vnode2, vnode3);
patch(vnode3, vnode4);
patch(vnode4, vnode5);
patch(vnode5, vnode6);
patch(vnode6, vnode7);
patch(vnode7, vnode8);

}

function run(name, fn, nRuns) {

const container = document.getElementById("container");
console.log(`running ${name}...`);
const t0 = performance.now()
for (let i = 0; i <= nRuns; i++) {
const root = document.createElement("div");
container.appendChild(root);
fn(root);
container.replaceChildren();
}
const t1 = performance.now()
console.log(`done: ${t1 - t0} ms`)

}

document.addEventListener("DOMContentLoaded", function() {

const n = 10000 // number of runs

run("snabbdom - benchmark1", benchmark1, n)
run("sjs-snabbdom - benchmark1", (root) => SnabbdomBenchmarks.benchmark1(root), n)
run("snabbdom - benchmark2", benchmark2, n)
run("sjs-snabbdom - benchmark2", (root) => SnabbdomBenchmarks.benchmark2(root), n)

}

)
139 changes: 139 additions & 0 deletions benchmarks/src/main/scala/snabbdom/benchmarks/Benchmarks.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright 2022 buntec
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package snabbdom.benchmarks

import org.scalajs.dom

import snabbdom._
import snabbdom.modules._

import scalajs.js.annotation._

@JSExportTopLevel("SnabbdomBenchmarks")
object Benchmarks {

val patch = init(
Seq(
Attributes.module,
Classes.module,
Props.module,
Styles.module,
EventListeners.module,
Dataset.module
)
)

@JSExport
def benchmark1(container: dom.Element): Unit = {

val vnode0p = patch(container, h("div"))

val vnode1 = h("div", List(h("span", "1"), h("span", "2"), h("span", "3")))
val vnode2 = h("div", List(h("span", "2"), h("span", "3")))
val vnode3 = h("div", List(h("span", "3")))
val vnode4 = h("div", List(h("span", "2"), h("span", "3")))
val vnode5 = h("div", List(h("span", "1"), h("span", "2"), h("span", "3")))
val vnode6 = h(
"div",
List(h("span", "0"), h("span", "1"), h("span", "2"), h("span", "3"))
)
val vnode7 = h(
"div",
List(
h("span", "0"),
h("span", "1"),
h("span", "2"),
h("span", "3"),
h("span", "4")
)
)

List(vnode1, vnode2, vnode3, vnode4, vnode5, vnode6, vnode7).foldLeft(
vnode0p
) { case (acc, vnode) => patch(acc, vnode) }

()

}

@JSExport
def benchmark2(container: dom.Element): Unit = {

def key(k: String) = VNodeData(key = Some(k))

val vnode0p = patch(container, h("div"))

val vnode1 = h(
"div",
List(
h("span", key("1"), "1"),
h("span", key("2"), "2"),
h("span", key("3"), "3")
)
)
val vnode2 =
h("div", List(h("span", key("2"), "2a"), h("span", key("3"), "3a")))
val vnode3 = h("div", List(h("span", key("3"), "3b")))
val vnode4 =
h("div", List(h("span", key("2"), "2c"), h("span", key("3"), "3c")))
val vnode5 = h(
"div",
List(
h("span", key("1"), "1d"),
h("span", key("2"), "2d"),
h("span", key("3"), "3d"),
h("span", "b")
)
)
val vnode6 = h(
"div",
List(
h("span", key("1"), "1e"),
h("span", key("3"), "3e"),
h("span", key("2"), "2e")
)
)
val vnode7 = h(
"div",
List(
h("span", "a"),
h("span", "b"),
h("span", key("1"), "1f"),
h("span", key("2"), "2f"),
h("span", key("3"), "3f")
)
)
val vnode8 = h(
"div",
List(
h("span", key("1"), "1g"),
h("span", key("2"), "2g"),
h("span", key("3"), "3g"),
h("span", key("4"), "4g")
)
)

List(vnode1, vnode2, vnode3, vnode4, vnode5, vnode6, vnode7, vnode8)
.foldLeft(
vnode0p
) { case (acc, vnode) => patch(acc, vnode) }

()

}

}
18 changes: 15 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ThisBuild / tlBaseVersion := "0.1"
ThisBuild / tlBaseVersion := "0.2"

val scala213 = "2.13.8"
ThisBuild / scalaVersion := scala213
Expand Down Expand Up @@ -38,8 +38,9 @@ ThisBuild / Test / jsEnv := {

lazy val scalajsDomVersion = "2.1.0"
lazy val munitVersion = "0.7.29"
lazy val scalacheckVersion = "1.16.0"

lazy val root = tlCrossRootProject.aggregate(snabbdom, examples)
lazy val root = tlCrossRootProject.aggregate(snabbdom, examples, benchmarks)

lazy val snabbdom = (project
.in(file("snabbdom")))
Expand All @@ -48,7 +49,9 @@ lazy val snabbdom = (project
name := "scala-js-snabbdom",
libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % scalajsDomVersion,
"org.scalameta" %%% "munit" % munitVersion % Test
"org.scalameta" %%% "munit" % munitVersion % Test,
"org.scalacheck" %%% "scalacheck" % scalacheckVersion % Test,
"org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0" % Test
)
)

Expand All @@ -69,3 +72,12 @@ lazy val examples = (project
}
)
.dependsOn(snabbdom)

lazy val benchmarks = (project
.in(file("benchmarks")))
.enablePlugins(ScalaJSPlugin, NoPublishPlugin)
.settings(
name := "scala-js-snabbdom-benchmarks",
scalaJSUseMainModuleInitializer := false
)
.dependsOn(snabbdom)
8 changes: 4 additions & 4 deletions examples/src/main/scala/snabbdom/examples/Example.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object Example {
val vnode = h(
"div",
VNodeData(on = Map("click" -> ((_: dom.Event) => println("foo")))),
Array[VNode](
List[VNode](
h(
"span",
VNodeData(style = Map("fontWeight" -> "bold")),
Expand All @@ -56,12 +56,12 @@ object Example {
)
)
// Patch into empty DOM element this modifies the DOM as a side effect
patch(container, vnode);
val vnodep = patch(container, vnode);

val newVnode = h(
"div#container.two.classes",
VNodeData(on = Map("click" -> ((_: dom.Event) => println("bar")))),
Array[VNode](
List[VNode](
h(
"span",
VNodeData(style =
Expand All @@ -79,7 +79,7 @@ object Example {
)

// Second `patch` invocation
patch(vnode, newVnode)
patch(vnodep, newVnode)

()

Expand Down
2 changes: 1 addition & 1 deletion snabbdom/src/main/scala/snabbdom/CreateHook.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ package snabbdom

trait CreateHook {

def apply(emptyVNode: VNode, vNode: VNode): Any
def apply(vNode: PatchedVNode): PatchedVNode

}
2 changes: 1 addition & 1 deletion snabbdom/src/main/scala/snabbdom/DestroyHook.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ package snabbdom

trait DestroyHook {

def apply(vNode: VNode): Any
def apply(vNode: PatchedVNode): Unit

}
Loading