← Blog

Measuring Superlinea's performance

Speed is one aspect of Superlinea's development that I consider super important (pun intended). A drawing app not only needs a good set of features and a good design, it also has to be fast. Because of that, during the development process I'm putting a lot of effort into optimizing it as much as possible. In this post, I'd like to present the results from the automated benchmarks I run to track the app's performance.

How I ran the benchmarks

One important caveat about the numbers below: I ran the benchmarks on one of the latest MacBook Pros on the market. Obviously, I don't expect everybody to have this exact machine, so while I continue building the app, I plan to gather more data from other machines.

The measurements below were taken with a production build of the editor in Superlinea 0.0.35, running in an isolated native macOS WebKit window with the sidebars visible. The machine I used is a MacBook Pro with an Apple M5 Pro chip and 48 GB of memory, running macOS Golden Gate (27.0.1). The window is 1,600 × 1,000 points at 2× scale (3,200 × 2,000 pixels), using the Apple GPU.

I measured opening large drawings, panning and zooming, bulk selection and editing, and path operations like Smooth & Simplify and booleans. The desktop shell, native file dialogs, disk access, and autosave are outside the measurements for now.

Big drawings

For this first benchmark, I used two repeatable drawings. The first is a grid of colored rectangles. The second mixes rectangles, gradient-filled ellipses, and stroked curves, arranged in nested groups.

The editor loaded and displayed both test drawings at 100,000 shapes. The mixed drawing opened in a median of 1,535 ms. That's the largest size in this test, not a hard limit on what Superlinea can open.

The opening times cover everything from reading the drawing to showing it on screen, ready to edit. They don't include launching the app or loading the file from disk. Each number is the typical (median) result of seven runs.

Shapes Open rectangles Open mixed Pan p95 Zoom p95
1,000 33 ms 50 ms 18 ms 18 ms
5,000 83 ms 100 ms 18 ms 17 ms
10,000 118 ms 167 ms 18 ms 18 ms
25,000 284 ms 381 ms 19 ms 18 ms
50,000 557 ms 775 ms 18 ms 20 ms
100,000 1,216 ms 1,535 ms 22 ms 18 ms

The last two columns show how smooth it feels to move around the mixed drawing. A typical screen draws a new frame every 16.7 ms, so anything close to that feels fluid. The numbers are the slow end, known as p95: 95% of frames arrived at least that fast.

At 100,000 shapes, the mixed document also contains 3,000 groups. Panning at 200% zoom had a p95 frame interval of 21 ms. Preparing its SVG took 266 ms, producing 11.2 MiB of editable SVG data.

Selecting and editing

A drawing also needs to respond quickly when the user changes it. For these tests I used the rectangle drawing, selected every shape for the bulk operations, and restored the original document before each destructive edit.

This table shows median processing times from seven repetitions after two warm-ups. These times measure how long the editor takes to do the work and update its panels, not the moment the result appears on screen.

Action 1,000 shapes 5,000 shapes 10,000 shapes
Click one shape 6 ms 6 ms 4 ms
Marquee everything 16 ms 45 ms 78 ms
Select all 14 ms 42 ms 73 ms
Select matching fill 11 ms 20 ms 32 ms
Move all 16 ms 38 ms 73 ms
Duplicate all 29 ms 129 ms 306 ms
Group all 23 ms 112 ms 300 ms
Ungroup all 23 ms 74 ms 139 ms
Delete all 3 ms 8 ms 14 ms
Undo deletion 19 ms 63 ms 117 ms
Redo deletion 3 ms 7 ms 14 ms
Prepare SVG 4 ms 17 ms 23 ms

The opening tests wait for two frames (two screen refreshes) after each run, so nothing there can measure below about 33 ms. I left that wait out here, which shows the real differences between small operations. Clicks and marquee selections go through the real selection tool, but they're triggered by a script rather than a physical mouse. “Prepare SVG” creates the file contents without writing them to disk.

For continuous dragging with all 10,000 rectangles selected and snapping enabled, the median frame interval was 22 ms and p95 was 25 ms. This uses the actual selection tool with scripted movement; it measures the editing workload rather than a physical mouse gesture.

Before these optimizations, the same drag had a p95 frame interval of 77 ms, compared with 25 ms now. The editor reuses paths when shapes are translated; when the whole drawing moves together, it can also shift cached artwork during the drag and refine it after release.

Dragging this many selected objects is still slower than the 16.7 ms per frame needed for a perfectly smooth 60 frames per second. It's a heavier task than navigating the drawing.

Curves and overlapping shapes

For this benchmark, I tested paths and overlapping shapes. The tables in this section include the two-frame wait again, which means that no operation can be faster than ~33 ms.

Smooth & Simplify

I tested the Smooth & Simplify tool at its default 50% settings on a wavy ring drawn with 100, 1,000, and 5,000 anchors. The timings include opening the preview and applying it.

Input Result Elapsed time
100 anchors 29 anchors 33 ms
1,000 anchors 29 anchors 68 ms
5,000 anchors 64 anchors 250 ms

All three rings are the same shape, drawn with more or fewer anchors. The densest one ends up with more anchors than the others: 64 instead of 29. For the technically curious, the simplifier merges at most 32 segments at a time to limit its work, so a denser path leaves more segments behind before smoothing. It's a limitation I want to improve.

Boolean operations

I tested live boolean operations on grids of overlapping circles. The timings include creating the combined shape, which stays editable, and drawing the result. Other drawings can be faster or slower, depending on how their shapes overlap.

Operation Input Elapsed time
Unite 10 circles 33 ms
Exclude 10 circles 33 ms
Unite 100 circles 33 ms
Exclude 100 circles 34 ms
Unite 500 circles 34 ms
Exclude 500 circles 150 ms

This test also helped improve the editor.

For these 500 overlapping circles, Exclude fell from 12.3 seconds to 150 ms, comparing median elapsed times with the same two-frame wait. Both runs use the same fixtures, native WebKit host, and machine.

Before, the editor merged shapes one at a time, so every step dealt with a bigger, messier shape. Now, for operations such as Unite and Exclude, it merges them in pairs and then merges the results, which keeps each step small.

What's next?

Thanks to this benchmark I found and fixed a boolean operation that took over 12 seconds, and made dragging large selections about three times smoother. There's still work to do: dragging 10,000 shapes doesn't reach 60 fps yet, and duplicating or grouping that many elements takes about 300 ms.

Next I'll run the same tests on other machines and start measuring the parts I left out: the desktop shell, disk access, and autosave. By the way, if you are one of the beta testers and have a drawing that makes Superlinea struggle, send it my way.

Recent posts