Optimize DisplayLayer's memory usage by storing transformations in a Patch #185

Merged
merged 160 commits into from Dec 15, 2016

Conversation

Projects
None yet
3 participants
@maxbrunsfeld
Contributor

maxbrunsfeld commented Dec 14, 2016

The Problem

A major issue with Atom's current handling of large files is that the DisplayLayer uses so much memory that v8 can run out of heap space, causing a hard crash.

The reason that the DisplayLayer uses so much memory is that it stores all of the transformations that are applied to the text when it is displayed. These transformations include soft-wraps, folds, hard-tab expansion, atomic soft-tab clipping, indent guides, and invisible-character substitution. They are stored in a data structure called the DisplayIndex, which uses a binary tree to allow content to be accessed efficiently in terms of both buffer or screen coordinates.

The Optimization

Translating between arbitrary buffer and screen coordinates is an important responsibility of the DisplayLayer, but many of the transformations listed above do not affect position translation. The only transformations that do affect position translation are soft-wraps, folds, and hard-tabs.

This PR takes advantage of that fact by avoiding storing any of the other transformations, and computing them only as needed, when rendering lines of text. This means that the data stored by the DisplayLayer is now sparse with respect to screen lines; nothing needs to be stored for screen lines without folds, soft-wraps or hard tabs.

We now store the DisplayLayer's soft-wraps and folds in a Patch - a data structure that we already use elsewhere in this library, for representing groups of changes in the buffer's History, and for aggregating changes to pass to onDidChangeText callbacks.

As part of this effort, we have rewritten the Patch in C++ and with a more compact representation, in order to further reduce Atom's memory consumption when editing large files.

nathansobo and others added some commits Nov 4, 2016

Start on a new DisplayLayer implement in terms of Patch to save memory
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Implement getText for hard tabs
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Start work on soft wraps
Signed-off-by: Nathan Sobo <nathan@github.com>
Handle soft wraps in getScreenLines
Signed-off-by: Nathan Sobo <nathan@github.com>
Add skipSoftWrapIndentation option that overrides backward clipDirection
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Assign characterWidth to 0 at end of buffer line
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Disallow non-positive soft wrap column parameters
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Cover edge cases of soft wrapping
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Update full screen lines and buffer lines in spatial index
If a screen line has a fold, always update the entire line, even if the
requested buffer row range starts or ends in the middle of the screen
line. If a requested buffer row contains soft wraps, always update all
screen lines that belong to that buffer row.

Signed-off-by: Antonio Scandurra <as-cii@github.com>
Implement DisplayLayer.destroyFoldsIntersectingBufferRange
Signed-off-by: Antonio Scandurra <as-cii@github.com>
Add DisplayLayer.destroyAllFolds
Signed-off-by: Antonio Scandurra <as-cii@github.com>
Temporarily use JS patch implementation in History and TextBuffer
We’ll use the native version when we add the necessary features.
Don’t consult buffer when finding following boundaries
Signed-off-by: Antonio Scandurra <as-cii@github.com>
Add DisplayLayer.foldsIntersectingBufferRange
Signed-off-by: Antonio Scandurra <as-cii@github.com>
Start handling changes to the buffer in new DisplayLayer implementation
Signed-off-by: Antonio Scandurra <as-cii@github.com>
Clip to the start or end of atomic soft tabs
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Render indent guides on non-empty lines
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Render indent guides for blank lines
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Don't clip atomic tabs if `fold` is false in translateBufferPosition
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Clip atomic soft tabs in translateScreenPosition
Also, change the private helper method to return a column delta rather
than a new point.

Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Simplify getAtomicSoftTabColumnDelta
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Handle paired characters
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>
Handle folds when translating buffer positions
Signed-off-by: Nathan Sobo <nathan@github.com>
Constrain end row argument to getScreenLines
Signed-off-by: Nathan Sobo <nathan@github.com>
Add DisplayMarkerLayer APIs to new DisplayLayer
Signed-off-by: Max Brunsfeld <maxbrunsfeld@github.com>

nathansobo and others added some commits Dec 13, 2016

Test display layer's partially-indexed state in randomized tests
Signed-off-by: Nathan Sobo <nathan@github.com>
Remove extra spatial indexing in buildScreenLines
Signed-off-by: Nathan Sobo <nathan@github.com>
Fix retrieval of partially-cached runs of soft-wrapped screen lines
Signed-off-by: Nathan Sobo <nathan@github.com>
Validate getScreenLines with random row ranges
Signed-off-by: Nathan Sobo <nathan@github.com>
Always cache all screen lines for a given buffer line
Signed-off-by: Nathan Sobo <nathan@github.com>
Remove logic for handling partially cached buffer lines
Signed-off-by: Nathan Sobo <nathan@github.com>
Avoid infinite loop in findBoundaryFollowingScreenRow
Signed-off-by: Nathan Sobo <nathan@github.com>
In buildScreenLines, always start at a boundary screen row
Signed-off-by: Nathan Sobo <nathan@github.com>
Fix off-by-one error when handling decoration invalidation events
Signed-off-by: Nathan Sobo <nathan@github.com>
Surround EOLs and indent guides with containing decoration tags
Signed-off-by: Nathan Sobo <nathan@github.com>

@maxbrunsfeld maxbrunsfeld merged commit d86d959 into master Dec 15, 2016

0 of 3 checks passed

continuous-integration/appveyor/branch Waiting for AppVeyor build to complete
Details
continuous-integration/appveyor/pr Waiting for AppVeyor build to complete
Details
continuous-integration/travis-ci/pr The Travis CI build is in progress
Details

@maxbrunsfeld maxbrunsfeld deleted the mb-use-less-memory-for-display-layer branch Dec 15, 2016

@kylehotchkiss

This comment has been minimized.

Show comment
Hide comment
@kylehotchkiss

kylehotchkiss Feb 8, 2017

Came here via the release notes - does this improve file open time when syntax highlighting is applied as well or does that happen at another layer of the application?

Came here via the release notes - does this improve file open time when syntax highlighting is applied as well or does that happen at another layer of the application?

@maxbrunsfeld

This comment has been minimized.

Show comment
Hide comment
@maxbrunsfeld

maxbrunsfeld Feb 8, 2017

Contributor

The main improvement is a dramatically lower memory consumption, so that you can open large files without crashing. File open time should improve across the board as well. This change is not specifically about syntax highlighting though.

Contributor

maxbrunsfeld commented Feb 8, 2017

The main improvement is a dramatically lower memory consumption, so that you can open large files without crashing. File open time should improve across the board as well. This change is not specifically about syntax highlighting though.

@kylehotchkiss

This comment has been minimized.

Show comment
Hide comment
@kylehotchkiss

kylehotchkiss Feb 8, 2017

@maxbrunsfeld just gave it a try on a 40mb / 1mil line JSON file and it works great. Thank you for your hard work!!!

@maxbrunsfeld just gave it a try on a 40mb / 1mil line JSON file and it works great. Thank you for your hard work!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment