
The Glimmer VM: Boots Fast and Stays Fast - lowonkarma
http://yehudakatz.com/2017/04/05/the-glimmer-vm-boots-fast-and-stays-fast/
======
MatthewPhillips
I love technical articles about how libraries are built. This is really great!

I don't follow the last part of the article though. Specifically this part:

> We accomplish this by (under the hood) allowing every bit of data that goes
> into a template to separate the process of computing its current value from
> the process of determining whether the value might have changed.

When you say "bit of data", what exactly do you mean? I assume you mean some
field that is bound to a template. Like a property on glimmer component. Using
KVO this would be a compute of some kind, that triggers an event when its
value changes.

Perhaps you're doing something totally different, but I don't see how?
Breaking it down, we have an object like:

    
    
       {
         "foo": "bar"
       }
    

I think you're saying that you don't observe this object (using some kind of
obj.set('foo', 'baz') convention) but rather determine that the value changed
by some other means. If that is so, what is the other means?

~~~
MatthewPhillips
Thinking about it a bit more I _think_ might know what is going on here. In a
traditional KVO view layer (I've worked on a couple of these myself, so my
thoughts here are biased) when a property changes it triggers an event. The
view layer is listening to this event and so it knows to update the DOM to
reflect the changed value.

What this means is that the view layer only works with evented KVO objects.
What Glimmer has achieved is the ability to work with other types of data
management systems. Knowing that is key to understanding (for me, at least).

So given that, what must be going on is that the view layer gets the value of
a property with some interface. That interface implementation is provided by
the view model so it differs. For plain objects it might just be `return
obj.foo`. This will make getting a value really fast. No notification systems
are required.

For updating the view layer there must be some generic way of telling it "hey,
the obj.foo property might have changed (or maybe not, shrug), figure it out
for yourself". Then using the update VM it is able to figure if it really did
change, and only update the subtrees if it did.

Or something, this my interpretation. Sounds smart!

~~~
tomdale
Yep, this is pretty spot on!

At a high level, the Glimmer VM is built on top of two primitives:

1\. References (as you describe, an interface implementation to provide the
underlying value)

2\. Tags, an interface that communicates "is it possible this value has
changed?"

Every reference has an associated tag, so once you have a reference, at any
time you can ask: Is it possible this thing has changed? If so, what is it's
value?

In KVO systems, every time a dynamic value is used in the template, that view
usually adds an observer on to the root object. This adds a fair bit of
overhead to both rendering and teardown. And if multiple properties change,
you have to figure out the optimal re-rendering strategy. (E.g., if a value
inside an `if` changes, you probably don't want to re-render it if the
conditional _also_ changed from truthy to falsy!)

Today in Ember, the view layer no longer sets up observers. Instead, during
rendering, we create a tag for each property. When you mutate a property
(this.set('firstName', 'Matthew') for example), two things happen:

1\. That tag is marked as dirty.

2\. A revalidation of the entire tree is scheduled.

The revalidation process starts from the top of the render tree and walks
down, asking every reference/tag "is it possible you've changed?" Because this
is just an integer comparison, it's very very fast on modern JavaScript VMs,
even if you have lots of data on screen.

The tag is like a Bloom filter, though. It means a change MAY have happened,
not that one necessarily did. If the tag is dirty, we do a last chance
identity check for primitive values. Only if it has actually changed do we
update the DOM.

One nice thing that falls out of this is that the application developer can
change as much component state as they'd like at once, and we can avoid doing
any expensive computation to figure out the optimal place to start re-
rendering (the `if` case I mentioned above). By revalidating the tree from the
top down and keeping constant time factors low, you get that optimization "for
free."

The other nice thing is that you can express all sorts of cool semantics on
top. For example, if you have immutable data you can attach a tag that always
says "I'm never modified." If you don't want to do any bookkeeping at all, you
can attach a tag that says "Always recheck me to see if I've changed." Best of
all, as Yehuda mentioned, you can mix and match these semantics in your
components. It also allows the _data_ to drive the change semantics, not the
component, which you often don't want to have care about how model data might
change.

If this is interesting to you, there is some WIP documentation in the Glimmer
VM repository that talks more about the philosophy behind references and tags:

[https://github.com/glimmerjs/glimmer-
vm/blob/master/guides/0...](https://github.com/glimmerjs/glimmer-
vm/blob/master/guides/04-references.md)

[https://github.com/glimmerjs/glimmer-
vm/blob/master/guides/0...](https://github.com/glimmerjs/glimmer-
vm/blob/master/guides/05-validators.md)

~~~
MatthewPhillips
Thanks, this explanation is very helpful. One last question, when you say:

> Because this is just an integer comparison, it's very very fast on modern
> JavaScript VMs, even if you have lots of data on screen.

I don't follow, what is an integer comparison? Don't you have to compare the
reference's value to the previous value? Meaning you need to get that
reference's value. That reference could be somewhat expensive. Imagine if it
is a property getter that calls into a few more functions.

This seems like the disadvantage of not using KVO, you don't know a property's
value without asking for it. with KVO the value is cached and only changes
when the underlying dependency tree changes. Of course vdom works without
this, so it can't be that bad.

But I'm guessing you've found a clever way to avoid having to call out to get
the reference's value to compare, so what is it? :-)

And I'll definitely read me in those docs, thanks!

EDIT: Probably should have read the docs first. A global counter id! Very
clever... I wonder if you could get away with just a lastModified though. The
view layer could keep track of the last time it updated and then just compare
lastModified, if something has changed since the last time _it_ updated then
it must have changed too.

Going to think some more about this, thanks for the puzzle!

------
chubot
What IS the glimmer VM? I didn't see any links on the page. Is this it?

[https://github.com/glimmerjs/glimmer-
vm](https://github.com/glimmerjs/glimmer-vm)

It is JavaScript code or native code?

~~~
steveklabnik
That is it. It's TypeScript.

~~~
chubot
OK, my confusion is why it's called a "VM":

 _Glimmer is a flexible, low-level rendering pipeline for building a "live"
DOM from Handlebars templates that can subsequently be updated cheaply when
data changes._

This sounds like something written on top of a JavaScript VM, not a VM itself.
Is it implemented with VM-like techniques? What does the instruction set look
like?

~~~
steveklabnik
> This sounds like something written on top of a JavaScript VM, not a VM
> itself.

You can write VMs in languages that are implemented with a VM.

> Is it implemented with VM-like techniques?

More than that, it is literally a virtual machine.

> What does the instruction set look like?

IIRC, it's a stack-based VM. Here's the opcodes, I believe:
[https://github.com/glimmerjs/glimmer-
vm/blob/master/packages...](https://github.com/glimmerjs/glimmer-
vm/blob/master/packages/%40glimmer/wire-format/lib/opcodes.ts)

(I have mostly a high-level understanding on this, after talking to lots of
people and watching presentations; I don't hack on Glimmer myself.)

~~~
abecedarius
Thanks. Up to this point I too was scratching my head wondering what the fuss
was about -- if this was a JavaScript-in-Typescript VM, or what. It's a custom
virtual machine for a domain-specific language for filling templates, right?

~~~
steveklabnik
Yes, this is a good succinct way to put it. It compiles handlebars templates
to its VM, and then as the machine executes, it updates the DOM appropriately.
You can think of the Glimmer VM as a VM interface to the DOM rather than the
direct DOM interface, and handlebars templates as programs that compile to
that VM.

------
mwcampbell
What is the advantage of implementing your own bytecode VM rather than
generating JavaScript code and using eval()? It seems to me that the latter
would be more efficient, since you wouldn't have a VM within a VM.

~~~
ibdknox
In this particular case, I believe there are 3 reasons:

1) Size - turning each op into a couple of bytes means that the size of your
template is _significantly_ smaller. If each instruction is 4 bytes, I could
get ~20 instructions in the space of just one function with a setAttribute
call:

    
    
        function t(e) {e.setAttribute("id", "bar")}
    

Size is much more important on the web than it is in other places especially
as the next billion people start using the mobile web.

2) Parsing speed - given the size of the JS that templates produce, you start
running into JS parsing performance. Just by volume you're going to eat an
insane amount of time not just downloading the JS but then trying to turn it
into something executable. A correctly implemented bytecode VM could easily
beat the cost of parsing.

3) Scheduling - If you just produce raw JS code, you don't have much room to
dictate how it executes. Since glimmer's goal is to never miss a frame,
they're going to have to take control of the work that gets executed to make
sure that they always pause at a frame boundary. That's a much more
straightforward thing to do in a VM, where pausing work is just a matter of
yielding the interpreter loop. This gives you complete control over how you
schedule the work from the ground up. Have some huge dom tree to render? Split
it across 10 frames without doing a bunch of control inversion.

In terms of cost, I haven't looked at their implementation, but I assume these
guys did their homework. You can implement interpreters that execute
instructions in a few nanoseconds without too much effort. If you _really_ put
in the effort, you can do it subnano, but that's outside of the scope of
handwritten JS. Even if the overhead was 20x a normal call, the cost of the
operations this interpreter is running makes that a rounding error. The DOM is
slow and the other benefits almost assuredly outweigh whatever tiny cost
they're paying at the per instruction level.

There are lots of other potential benefits as well: opportunities for
specialized optimizations over the bytecode (you could basically do your own
domain specific jit), ease of implementing the base VM for different targets,
and so on. There are relatively few times when writing your own interpreter
probably makes sense, but it seems like this architecture would give them a
ton of headroom to do some great stuff down the line.

~~~
curveship
For 1) and 2), some frameworks solve the size issue by creating functions for
the basic DOM ops, which then get minimized to single character names in
production. So in your example, there might be a setNodeAttribute(node, name,
val) function, such that the final code isn't `e.setAttribute("id", "bar")`
but just `a(e,"id","bar")`, where `a` is the minimized name of
setNodeAttribute.

There's really not much noise in that expression, just the "(,,)", so maybe 4
chars that an opcode could save.

As for 3), is that really possible? If you profile most modern frameworks,
they're already fast enough that most of the time is spent in rendering, not
in javascript DOM manipulation. So even if you cut short your js before 16ms
(60fps), you have no idea how long the browser is going to take to render your
changes. Plus, the browser will be doing extra work, since it needs to render
all the frames in which you've only done part of your updates.

~~~
ibdknox
In terms of #3, if you're yielding until the next requestAnimationFrame, the
browser is telling you when you have the opportunity to do more work. Is there
something that isn't covered by that?

> they're already fast enough that most of the time is spent in rendering, not
> in javascript DOM manipulation

That hasn't been my experience, but it's been awhile since I've benchmarked
any of the frameworks in common use. Change tracking, diffing, and then the
dom calls have all been the bulk of the work in large updates. Assuming you're
doing those in a dom fragment I'm not sure how "rendering time" (I'm taking
that to mean compositing and painting?) could be the bottleneck in that
scenario.

~~~
curveship
BTW if you're curious, browser DOM operations have advanced now to the point
that doing work in dom fragments is often slower than just doing it right in
the main tree. See, for instance, one of the recent optimizations to the
vanillajs implementation of js-framework-benchmark:
[https://github.com/krausest/js-framework-
benchmark/commit/2e...](https://github.com/krausest/js-framework-
benchmark/commit/2e013314093eddef2cdf28751d6c11ab3bc5c3c7)

------
mercer
Apologies ahead of time if this is a stupid question. I am pretty much the
walking stereotype of a web developer with very little experience with
anything below javascript/ruby/python/php.

Considering that Glimmer goes quite far in optimizing stuff, at which point
would it perhaps make more sense to just emulate HTML elements on a pixel-
level?

Or am I vastly underestimating how complex the standard UI elements really
are? Or overestimating how much effort went into Glimmer? And if so, as
perhaps a more constructive question: are there any areas of 'web' where
someone feels a shortcut _should_ be taken?

(On the latter, I'm inclined to feel that WYSIWYG textareas and CSS above
class-scope are ripe for 'shortcuts', but perhaps that's opening some cans of
whole other worms...)

~~~
okket
Whole websites were done in Flash, not long ago. It was not a good idea.

~~~
dbbk
It feels like a bad dream now, but yeah this really did happen. It was awful.

------
dlcmh
Based on some of the comments about what a VM actually is, I think this 2-part
series might help those who are wondering (me included) about the
technicalities of building one's own VM:

\- (Part 1) [https://www.codeproject.com/Articles/43176/How-to-create-
you...](https://www.codeproject.com/Articles/43176/How-to-create-your-own-
virtual-machine) \- (Part 2) [https://www.codeproject.com/Articles/61924/How-
to-create-you...](https://www.codeproject.com/Articles/61924/How-to-create-
your-own-virtual-machine-Part)

------
bluepnume
I'm a big fan of what I'm seeing in Glimmer so far. I had fun adding support
for xcomoponent yesterday, making Glimmer components work as cross-domain
components.

[https://medium.com/@bluepnume/introducing-support-for-
cross-...](https://medium.com/@bluepnume/introducing-support-for-cross-domain-
glimmer-components-with-xcomponent-21287c9f91f1)

I'd love it if it were a little more flexible to get started with though.
Rather than fire up ember-cli, I'd love to just be able to:

1\. Load glimmer.js from a CDN

2\. Extend glimmer.Component

3\. Render my component into an element

I know ember-cli solves this problem for a lot of users, but I'd love for the
number of steps to get a "hello world" working to be as simple as the above. I
think that's one of the reasons React has been so successful: it's stupid-
simple to get started with, and it draws you in from there.

~~~
okket
This is simply not possible with what Glimmer tries to achieve. (Did you even
read the article?)

~~~
bluepnume
It's not possible to have a slower development build which does
template->bytecode conversion on page load?

The point I'm trying to make it, there's already a huge amount of churn around
the vast amount build tooling needed to write any javascript these days. Part
of making a framework with a low barrier to entry is having the build tooling
stay out of your way until you're ready to deploy.

I get that's not necessarily Glimmer/Ember's philosophy, I just think it's a
shame that using the framework from day 1 requires buying into all of the
technology choices around ember-cli.

~~~
okket
Minimising time to first render/interaction is on the list what is important
for Glimmer. I don't see how this is compatible with moving the conversion
engine to the client.

(Not to mention that the app size zealots will eat you alive)

~~~
bluepnume
Yeah, that's essential in production. Totally agree. I'm mainly talking about
the development experience for first-time users, or for people who just want
to throw Glimmer onto one of their apps on localhost and play around with
glimmer.Component. The thing that needs to be fast in those scenarios is the
developer experience, not the app.

~~~
okket
If you want to play around, have a look at Ember Twiddle:

[https://ember-twiddle.com/](https://ember-twiddle.com/)

(No Glimmer there yet, only full Ember, but you can get a feel for what it can
do, how it is like, test a things out...)

------
conradk
How does Vue.js compiled templates compare to Glimmer ?

------
bozonil
Is Glimmer VM actually a virtual machine?

The term suggests some kind of Turing complete language with sandboxing and
memory management.

Without these features "domain-specific language" would be more precise
description.

------
pier25
I wonder why the author ignored other big players in those benchmarks such as
Angular or Vue.

------
qaq
Someone needs to make a lightweight framework with MobX and Glimmer

------
analognoise
Isn't this just re-solving a decades old problem, with the (unnecessary)
constraints caused by modern web systems?

~~~
matthewmacleod
While being technically correct, your statement is pretty much pointless.

All development is 'just' re-solving an existing problem with better
performance, or 'just' extending an existing algorithm to be more resilient,
or 'just' implementing a legacy interface on a new platform.

There are lots of very obvious reasons that the web has the constraints that
it does and describing them as 'unnecessary' adds literally nothing useful to
that conversation.

~~~
coldtea
> _All development is 'just' re-solving an existing problem with better
> performance, or 'just' extending an existing algorithm to be more resilient,
> or 'just' implementing a legacy interface on a new platform._

Only this is "re-solving an existing problem with worse performance that we
had decades ago (on native), but slightly faster than the previous speeds we
achieved having it run with its feet tied".

(Where by "feet tied" we refer to the performance penalty imposed by having
everything run on the web stack).

~~~
pcwalton
Can you explain how "native" solved the problem of performing minimal MVC-
style updates to a UI "decades ago"?

Decades ago, we had the Win32 API, which didn't even try to solve this—apps
had to roll their own.

~~~
coldtea
> _Can you explain how "native" solved the problem of performing minimal MVC-
> style updates to a UI "decades ago"?_

That's not an actual problem people have. Nobody says "what I want is minimal
MVC-style updates to a UI". What they want is a fast UI.

So an actual problem is e.g. "having a slow UI" \-- and native GUIs solved it
"decades ago" by being able to do stuff faster and with less memory and power
compared to the web stack.

That said, there were several frameworks that allowed that, especially since
MVC didn't magically appear with the web -- the concept originated with
Smalltalk. And even "dumb" native GUI frameworks were much closer to the metal
than the DOM, and knew how to repaint e.g. only the area of a widget that
changed.

~~~
pcwalton
> And even "dumb" native GUI frameworks were much closer to the metal than the
> DOM

GDI wasn't "closer to the metal" than the CSS painting model was. It was
reviled for _not_ being close to the metal, in fact (which is why WinG and
later DirectX were so important). On Windows NT you had to take a context
switch to kernel mode to issue painting commands—how can _that_ be "close to
the metal"?

> and knew how to repaint e.g. only the area of a widget that changed

Browsers have been doing this for over a decade.

Actually, I think they should largely stop doing this, since in a double
buffered scenario (which has been the norm since the Vista era) partial
repaints become complicated, and GPUs are so good at blitting a window-sized
area that it's really a non-issue. What _are_ issues are state changes and
overdraw, which native frameworks are _exceptionally_ bad at minimizing (this
is largely why Skia-GL is underwhelming on Android) and the declarative CSS
model is good at supporting. Native frameworks such as Win32 and GTK are _held
back_ by having to support this obsolete model. None of the Win32 designers
could have imagined that Z-buffers and early fragment tests would become
universal.

A meta-note: Pretty much without fail whenever anybody has mentioned some
_specific_ thing that Web browsers supposedly fail to do that native can do
(the incorrect idea that browsers don't do partial repaints being just the
latest instance of it), browsers have been already doing that thing for years.
This indicates to me that most people who complain about "native" vs. "Web"
haven't really looked into how the Web works. I'm all for acknowledging the
Web's shortcomings, but let's concentrate on real issues. (In my view, legacy
browser design, _which is largely the direct result of trying to be "native"_,
is the biggest problem, not the Web.)

------
iamleppert
It's not correct to call this a VM. It's an optimized template
language/renderer.

I'd like to see an example of the same static gzipped HTML content delivered
to the browser and a template rendered with this.

I have doubts this is any more efficient than the browser's built-in gzip
decoder on a time basis nor do I think it beats gzip in terms of space
efficiency.

It's hard to take seriously those who claim to be optimizing things when they
don't have any measurements on what they are trying to improve vs. the new
method.

~~~
swsieber
Why not? They have byte code (sort of) that translates to actions...

I don't get the gzip comparison because this is about _updating views_ in an
SPA framework (Ember)

