rebornix / notebook-test

1 stars 2 forks source link

Benchmarks #1

Open rebornix opened 4 years ago

rebornix commented 4 years ago

To get a better understanding the overall performance of opening/loading a notebook with different implementations, we will do some benchmark with different types of Notebook files, the matrix is

There are a couple of factors that affect the overall performance. To make sure we get the right numbers, we need to track the benchmarks in following aspects

Results

All tests are run multiple times to get fair results. Benchmark profiles are measured by Chrome Dev Tools Performance tool. Before each benchmark, garbage is always collected. For more details of each benchmark's water flow, please read the comments below.

Platform File Size Initialization Cells Rendering Total
Python Regular 3.88s 1.84s 5.75s
Medium 4s 6.28s 11s
Large 4.1s 74s 80s
ADS Regular 250ms 443ms 0.8s
Medium 210ms 5s 6.43s
Large 600ms 108s 120s

Interesting findings:

rebornix commented 4 years ago

--Python--

Regular

image

Medium

image

Large

image
rebornix commented 4 years ago

--Azure Data Studio--

Running Code sample

image

Medium

image

Large

image

rebornix commented 4 years ago

Where the time is spent

The scripting/layout time ratio is around 20/80. It indicates that the scripts trigger forced reflow layout very often. If we dig into the water flow of ADS benchmarks we can find some proves

Firstly, editor and markdown content creation. This phase takes 70s for opening the large notebook.

image

In above frame, a monaco editor control is created and as it doesn't provide the initial dimension, it attempts to measure the container DOM node size, which takes 30ms. The layout time accounts for 3/4 of the editor creation time.

And then a renderMarkdown call triggers another forced reflow, which takes another 30ms. The scripting time renderMarkdown is only 2ms.

For the large file, we have 545 code blocks and 1292 text cells, the time spent in forced reflow is at least 545*30 + 1292*30 = 55,000 ms.

Secondly, the second phase is all editor updating and relayout.

image

The resize observer we put on every monaco editor container detects some size change and triggers a recompilation of the editor options, which lead to a relayout finally. In the water flow, what we see is a 2ms editor option update followed by 50ms browser forced reflow.

With 545 editors, it's about 545*50 = 27,250ms.

rebornix commented 4 years ago

The performance of opening a notebook file can be improved by optimizing editor creation and layout, especially for the initial creation. To get a better understanding of how this can be improved, here are some attempts I've done

Layout editor with calculated dimensions

The size of a monaco editor is determined by line count of the text model. Since we know the height of a view line, we can get the height of the editor by calculation. As we pass in the calculated dimensions when running editor.layout({ width, height }), the file opening time is reduced to 30s.

image

It's better than before, but still not acceptable as open the same file in text editor takes hundred of ms. Scrolling is terrible as well since there are too many DOM nodes.

Virtualization

After adopting List View, the file loading performance is improved significantly as the List View is virtualized. It loads several view ports into the DOM and remove unused ones when necessary.

For loading a file with 200 code blocks and 1000 markdown cells, it takes 350ms ... actually it only loads the first 20 of them.

image

The scrolling is much better than loading whole content into view, but it's still not good enough. Each onScroll handling takes around 200 - 600ms depends on how many code cells are loaded in future viewports. For comparison, you can open Interactive Playground and scroll in it, you won't feel any lag at all.

Some ideas of improving scrolling

Simple editor, caching, translate3d

The scrolling is below 200ms on average. The lag is still noticeable sometimes.

image

List item dynamic height

We use dynamic height option in the List View at the beginning so it will calculate the list item height every time when a new list item needs to be added to the tree. However, this step is costly as it will add the node to a temporary tree and then remove it from the tree. In below water flow, you can see that monaco editor is created to calculate its height and then be removed.

image

    private probeDynamicHeight(index: number): number {
        const item = this.items[index];
                ...
        row.domNode!.style.height = '';
        this.rowsContainer.appendChild(row.domNode!);

                ...
                this.rowsContainer.removeChild(row.domNode!);
       }

However, the height of each editor is predictable: lineHeight * lineNumber so we can use dynamic height for markdown cells only. After the optimization, we reduce the list rendering near half.

image