[ANN] Makie v0.24

Makie v0.24 is finally out!
This release features a large refactor of the internals, replacing Observables with a ComputeGraph. This helped us achieve major performance improvements, better handling of multiple attribute updates, and improved correctness and maintainability.

This has been a huge effort due to Observables being so tightly integrated with everything. Note that Observables are not deprecated – we just avoid them in the internals going forward!

Read all about it in our blog post:
https://makie.org/website/blogposts/v0.24.0/

84 Likes

Thank you for the update @sdanisch. Very impressive numbers!

From the viewpoint of someone interested in custom recipes, is there anything we can do to benefit from these performance improvements? Is there a simple example where a custom recipe implemented in terms of Observables is rewritten in terms of ComputeGraph?

I really support the idea of moving away from Observables.

3 Likes

This is great! If I have written code that uses recipes and observables, will it continue to work?

1 Like

That depends on the way you’ve been using attributes. If you’ve only been creating observables and passing them directly as arguments to inner plotting functions, that continues to work (even though it doesn’t make use of the lazy graph features). But if you were doing something with Attributes that was treating them like a dict, that doesn’t work anymore. Like plot!(parent, args...; kwargs..., parent.attributes...) or attr = copy(Attributes(plot)); pop!(attr, :removed), and others that the blog post spells out.

1 Like

Thank you. I will read the full blog post! This seems like a big improvement compared to the observables machinery. But I am glad compatiability seems mostly intact.

One way is to compare recipes that have been converted inside of Makie, although they are not structured didactically.

For example, rangebars/errorbars in v0.23 vs v0.24.1. It’s not an exact correspondence but you can extract some patterns, and look at other recipes of course. Not all have been converted, yet, though.

Implementation of recipes with many corresponding attributes (that should share an array size, mostly) is much easier because you don’t have to think about order of observable triggers anymore. Instead, you know that the whole graph you construct will be updated in one go for a given frame.

2 Likes

Care was taken to leave as many “simple” use cases intact as possible. But from experience we also know that Makie suffers a lot from Hyrum’s law:

With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviours of your system will be depended on by somebody.

(No pun intended with the “observable” :wink: )

5 Likes

@jules we adopted the following code pattern in our recipes:

  1. Access attributes with plot[:attrname]
  2. Derive new observables with @lift
  3. Sometimes access the contents with attr[]
  4. Forward the new observables to the next recipe

I wonder if the following example would benefit from a rewrite:

If I have GLMakie (v0.11.5 for instance) as dependency of a package, what exactly should one expect from (or do to get) this update? I’m confused by the version numbers here.

GLMakie is its own package but due to the monorepo structure it’s not as easy to see the releases on github as for Makie.jl. You can check the project.toml directly to see what the current version is Makie.jl/GLMakie/Project.toml at master · MakieOrg/Makie.jl · GitHub

So currently its GLMakie 0.13.1 with Makie 0.24.1 so that would suggest 0.11 is from Makie 0.22 as the minor versions have been updating in lockstep for a while.

1 Like

1-4 should all still be supported, unless you explicitely type something as an observable.
The linked example doesn’t look like it would be helped a lot by moving to the new api, since it’s mainly 1:1 :wink:
It would help with having less going back and forth between observables and the compute graph.

1 Like

If you have any suggestions to improve the back and forth, please feel free to share. It is not clear to me what you mean by that.

if you follow the upgrade guide in the blogpost, you’ll create less observables on top of the compute graph :wink:

I’m curious about whether this refactor has some improvement on precompiling? Or somehow making it worse? (new ComputeGraph linking etc.)

Sadly it made it a bit worse.
We still hope to reclaim some regressions and maybe even improve over v0.23, since in theory it could work pretty well with precompilation and we haven’t put as much time into it yet as we did for Observables.

2 Likes

:smiling_face: Got it! Thanks for the effort.

1 Like

Always exciting to see Makie getting better-still!

But I’m afraid I’m still struggling to understand how to use the new the ComputeGraph system. The examples in the blog post are too incomplete and abstract for me. They have a variable called plot (which seems like a spicy choice given that it shadows an exported function) and it seems that plot.attributes is of special significance. But what is plot and where does it come from? Context would suggest it’s a ComputeGraph, but there are no examples of how to instantiate one.

My main use of Observables thus far has been to do things like update the datapoints in a scatter over time, adjust the axes of a figure, or change the contents of a Label. How can I connect those uses to the new system?

I found Observables | Makie (plus some blog posts) helpful back when I was first using Makie. That page now makes mention of the new ComputeGraph stuff, but could that be improved to a link to a full page that provides a similar level of detail?

EDIT: it looks like the Lorenz demo on Welcome to Makie! | Makie has been updated and provides a usable example. I guess I can probably piece it together from there.

2 Likes

We just call the objects created by recipes “plots” in our imprecise language so that’s why the variable in the example is called that. If you’re only plotting by passing observables to existing plotting functions, you don’t need to interact with the compute graphs inside at all. The one thing worth learning in that scenario is to use the new update! function to update multiple attributes of a plot object at once, so you can make sure array lengths never go out of sync, for example when GLMakie renders a frame in between two of your observable updates.

Other than that the other compute graph functionality is mostly for people writing recipes. It’s much easier to add new nodes to the graph than setting up a network of observables and getting that to sync correctly, at least for more complex recipes. plot.attributes is actually the graph, I complained about that, too, as it’s a little confusing that it’s not an Attributes dict anymore like it used to be but kept the name.

1 Like

I have not yet tried it, but wondering if this performance increase will help with low powered machines with poorly implemented OpenGL, like the Raspberry PI family of computers.