question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

More efficient fragment creation

See original GitHub issue

At present, fragments are built up programmatically one at a time. For a template like this…

<section>
  <h1>Hello {name}!</h1>
  <p>{description}</p>
</section>

…we get this:

function create_fragment(ctx) {
  let section;
  let h1;
  let t0;
  let t1;
  let t2;
  let t3;
  let p;
  let t4;

  return {
    c() {
      section = element("section");
      h1 = element("h1");
      t0 = text("Hello ");
      t1 = text(ctx.name);
      t2 = text("!");
      t3 = space();
      p = element("p");
      t4 = text(ctx.description);
    },
    m(target, anchor) {
      insert(target, section, anchor);
      append(section, h1);
      append(h1, t0);
      append(h1, t1);
      append(h1, t2);
      append(section, t3);
      append(section, p);
      append(p, t4);
    },
    p(changed, ctx) {
      if (changed.name) set_data(t1, ctx.name);
      if (changed.description) set_data(t4, ctx.description);
    },
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(section);
    }
  };
}

One way we could potentially improve this is by using <template> and cloneNode to create the entire fragment in one go. This suggestion came from @martypdx, who co-authored Ractive (Svelte’s predecessor): https://gist.github.com/martypdx/96b0a0900d2769a982ba36a066c1e38a. Riffing on the ideas in that gist, imagine we had a helper like this…

const make_renderer = html => {
  const template = document.createElement('template');
  template.innerHTML = html;

  const text = template.content.querySelectorAll('sveltetext');
  for (let i = 0; i < text.length; i += 1) {
    text[i].replaceWith(document.createTextNode());
  }
  
  return () => {
    const fragment = template.content.cloneNode(true);
    return fragment.querySelectorAll('[bind]');
  };
};

…we could then generate this code instead:

+const render = make_renderer(
+  `<section bind><h1 bind>Hello <sveltetext/>!</h1> <p bind></p></section>`
+);
+
function create_fragment(ctx) {
  let section;
+  let t0;
+  let t1;
-  let h1;
-  let t0;
-  let t1;
-  let t2;
-  let t3;
-  let p;
-  let t4;

  return {
    c() {
+      [section, { childNodes: [, t0] }, { childNodes: [t1] }] = render();
+      t0 = text(ctx.name);
+      t1 = text(ctx.description);
-      section = element("section");
-      h1 = element("h1");
-      t0 = text("Hello ");
-      t1 = text(ctx.name);
-      t2 = text("!");
-      t3 = space();
-      p = element("p");
-      t4 = text(ctx.description);
    },
    m(target, anchor) {
      insert(target, section, anchor);
-      append(section, h1);
-      append(h1, t0);
-      append(h1, t1);
-      append(h1, t2);
-      append(section, t3);
-      append(section, p);
-      append(p, t4);
    },
    p(changed, ctx) {
+      if (changed.name) set_data(t0, ctx.name);
+      if (changed.description) set_data(t1, ctx.description);
-      if (changed.name) set_data(t1, ctx.name);
-      if (changed.description) set_data(t4, ctx.description);
    },
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(section);
    }
  };
}

This is less code (notwithstanding the cost of the helper), and also very possibly less work (though I haven’t benchmarked anything yet).

There’s also potential for further benefits in future — by putting markup in <template> elements that are on the page (generated during SSR), or in HTML modules if we ever get those. This would mean less JavaScript, and we can take advantage of the browser’s parser (which is fast!).

A few things to consider:

  • Need to be able to insert placeholders for (if/each/etc) blocks (though this could be the same mechanism as for text)
  • The bind attributes, used for selecting nodes we need references to, would stick around in the DOM. Not the worst, but not ideal. Also, maybe we need to pick a name that’s less likely to conflict with something that gets added to the DOM in future (maybe just svelte)
  • Haven’t really thought about how this’d work with hydration

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:52
  • Comments:29 (17 by maintainers)

github_iconTop GitHub Comments

58reactions
tomoamcommented, Sep 5, 2021

I was interested in this issue, so I created an experimental implementation using <template> and cloneNode.

https://github.com/tomoam/svelte/tree/efficient-fragment-creation

The implementation is only makeshift, but passes the tests. The tests have not been changed except for the test cases in the test/js directory and a very few test cases. The implementation of hydration and namespace is quite messy.

Here is the keyed result of running krausest/js-framework-benchmark with this implementation. svelte-experimental is the benchmark score using this implementation.

js-framework-benchmark keyed results

Note: The scores for partial update, select row, swap rows, and clear rows are not helpful. This is because in my environment (M1 MacBook Air, 16GB Memory), the scores for these four test cases are random and vary widely each time these are run, no matter which framework.

It is worth noting that the scores for create rows, replace all rows, create many rows, and append rows to large table are much improved.

If we are going to improve the performance of Svelte in the future, the use of <template> and cloneNode is worth considering.

30reactions
tomoamcommented, Sep 9, 2021

@ryansolid Thanks for the advice. I ran the benchmark in a different environment (Macbook Pro 2016, 32GB Memory) and got the following results. As you said, the score is in Inferno range.

Keyed results

keyed results

Startup Metrics

For Startup Metrics, the score of this implementation remained as good as the current Svelte.

Startup metrics

Memory allocation

For Memory allocation, there was a slight improvement.

Memory allocation
Read more comments on GitHub >

github_iconTop Results From Across the Web

Create a fragment - Android Developers
A fragment represents a modular portion of the user interface within an activity. A fragment has its own lifecycle, receives its own input...
Read more >
Are Activity faster to create than fragment? - Stack Overflow
I reckon Fragment can never be created faster than it's host Activity simply because it has to be attached to the Activity.
Read more >
Creating a Fragment | Android Developers
While fragments are reusable, modular UI components, each instance of a Fragment class must be associated with a parent FragmentActivity . You can...
Read more >
FRAGMENTS - Android Fundamentals - YouTube
In this video you will get to know the concept of fragments and how to actually use them in our app. · Let...
Read more >
1.1: Creating a Fragment with a UI · GitBook
However, the UI of an Activity may be more effective if it adds or removes the Fragment dynamically while the Activity is running....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found