Fixes Strategies#

Differents stategies can be identified for the fixes. Whatever the fix, we want to ensure:

  • Searching in the notetbook functionality is still working like before.

  • No negative side-effect should appear.

Strip Output#

We could strip the cell output if too the ouput is too large, but this has two limits:

  1. This does not work for graphical outputs.

  2. This does not provide any fix for the code editor which is the largest identified performance offender so far.

Block Div#

We should not use flex display CSS layout but rather use a simple block.

Virtualized Notebook#

Cocalc has been using react-virtualized. We should look at this to understand how this could help Virtualization complexity and potential side-effects (search…) have to be taken into account.

Intersection Observer#

We can think to more generic fixes like using Intersection Observer API.

The strategy for a notebook would be:

  • Use an intersection observer to only render cells (input + output) on screen on page load.

  • As there are free cycles, render the rest of the notebook.

  • Ideally do this from the closest cell to the furthest.

  • If any cell is in view, it loads.

  • All non-loaded cells will need a cheap div that takes approx the same amount of space and has clear loading text.

  • Add UI elements to indicate a notebook is still loading so that users don’t search and not find something on the page.

  • Do not load any notebook that is not visible (i.e. when you open a workspace and have notebooks that are not visible, they should not be rendered).

Tutorials

React

An preliminary step is to wrap Notebook into React (see this PR Try Notebok React component).

Then we could use React Intersection Obsever libraries.

React Virtualized / Windowing#

An preliminary step is to wrap Notebook into React (see this PR Try Notebok React component).

Then we could use React virtualisation libraries.

Shadow DOM#

Shadow DOM allows encapsulation being able to keep the markup structure, style, and behavior hidden and separate from other code on the page so that different parts do not clash, and the code can be kept nice and clean.

Initial Shadow DOM usage is implemented in [Move CodeMirror HTML tree and related CSS to shadow DOM](https://github.com/jupyterlab/jupyterlab/pull/8584 . We compare 1f15fcb vs f7b7ee7 commits.

- f7b7ee7d271bd1233a5b95c9fd9dfb2d9509bbe6
  - Move CodeMirror HTML tree and related CSS to shadow DOM
  - Sun Jul 26 05:33:10 2020 -0500ƒ
- 1f15fcbc577517f1f320252bbe0a7b5a48f32996
  - Merge pull request #8642 from saulshanabrook/2.2-changelog
  - Fri Jul 24 16:14:11 2020 -0400

For the 100 N output each of a div and the one output with N div, we find that Shadow DOM made switching notebooks slightly faster in Chrome and slightly slower in Firefox.

For the Nx5 with 2 line of code and 2 outputs per cell, we have some small improvements in notebook switching. For Firefox open times do see nice improvement in general. On Chrome it seems also improve for smaller notebooks but it degrades with larger ones, and actually becomes worse (with a lot of variability).

Tune CodeMirror Configuration#

We should look how to configure or even update CodeMirror code base to mitigate the numerous Force layout.

Enhance CodeMirror Code Base#

On this comment: Also editing the codemirror source to avoid measurements (by manually returning what the cached values ended up being) at https://github.com/codemirror/CodeMirror/blob/83b9f82f411274407755f80f403a48448faf81d0/src/measurement/position_measurement.js#L586 and https://github.com/codemirror/CodeMirror/blob/83b9f82f411274407755f80f403a48448faf81d0/src/measurement/position_measurement.js#L606 seemed to help a bit. The idea here is that since a single codemirror seems okay, but many codemirrors does not (even when the total number of lines is the same), perhaps we can use measurements from the codemirror to shortcut measurements in all the others, which seem to be causing lots of browser layout time.

Read also the discussion on CodeMirror/#/5873.

Content Visibility#

We have tried content visibility supported in Chrome 85+ in this branch.

Without Shadow DOM, we have clear improvement.

Meanwhile, with Shadow DOM, we have no improvement.

See also Display Locking library for the related Display Locking specification.

Fast DOM#

FastDOM is a library that eliminates layout thrashing by batching DOM measurement and mutation tasks.

Web Workers#

Web Workers makes it possible to run a script operation in a background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.

Service Workers#

Use requestAnimationFrame (non concluding)#

Wrapped the cell creation into a requestAnimationFrame call.

requestAnimationFrame(() => {
  const cellDB = this._factory.modelDB!;
  const cellType = cellDB.createValue(id + '.type');
  let cell: ICellModel;
  switch (cellType.get()) {
    case 'code':
    cell = this._factory.createCodeCell({ id: id });
    break;
    case 'markdown':
    cell = this._factory.createMarkdownCell({ id: id });
    break;
    default:
    cell = this._factory.createRawCell({ id: id });
    break;
  }
  this._cellMap.set(id, cell);
});

This produces a different profile pattern (2 heavy sections separated by an inactive one). The Forced layout due to codemirror are still there.

Update Editor on Show#

A candidate fix would be to further look at updateEditorOnShow implemented in jupyterlab/jupyterlab#5700, but is is already set to false….

Use pushAll cells insted of push (non concluding)#

We have updated the celllist#pushAll code block but it has not brought better performance.

Current attempts have not brought enhancements.

React Concurrency#

We can inject more React into the UI components and see if it makes life easier to get better performance using core React performance features.

Reuse contentFactory#

Reuse contentFactory in Notebook Model. We may try to benchmark with this patch. At first user try, this does not give sensible change.

diff --git a/packages/notebook/src/model.ts b/packages/notebook/src/model.ts
index 2efeee7b3..4716ce9f7 100644
--- a/packages/notebook/src/model.ts
+++ b/packages/notebook/src/model.ts
@@ -514,7 +514,7 @@ export namespace NotebookModel {
      *   `codeCellContentFactory` will be used.
      */
     createCodeCell(options: CodeCellModel.IOptions): ICodeCellModel {
-      if (options.contentFactory) {
+      if (!options.contentFactory) {
         options.contentFactory = this.codeCellContentFactory;
       }
       if (this.modelDB) {

Use scrollbarStyle: ‘null’ in Editor Config#

On this comment: I did some quick experiments, based on some quick profiling results (it seems that the vast majority of time is in browser layout). For example, adding scrollbarStyle: ‘null’ to the bare editor config in editor.ts.

Transfer Content in Chunks#

Transfer Content in Chunks for Incremental Loading on https://github.com/jupyter/jupyter_server/issues/308

Server Memory cache#

TBD

Improving Network Performance#

Web Render#

From this comment: Webrender for Firefox 79 (for many linux and macos devices, see https://wiki.mozilla.org/Platform/GFX/WebRender_Where can be turned on via a pref. See also https://www.techrepublic.com/article/how-to-enable-firefox-webrender-for-faster-page-rendering. Note that windows firefox has had webrender turned on by default in certain cases for a while now.