Comparar commits
5 Commits
asar
...
bo-fnr-speedup
| Autor | SHA1 | Data | |
|---|---|---|---|
| e785f9c0db | |||
| 34a16dda07 | |||
| 98eb229c1c | |||
| d56229ae05 | |||
| db1ecb6249 |
@@ -1,5 +1,7 @@
|
||||
require './benchmark-helper'
|
||||
{$, _, WorkspaceView} = require 'atom'
|
||||
{$, _, WorkspaceView, Range, Point} = require 'atom'
|
||||
{Emitter} = require 'emissary'
|
||||
TextBuffer = require 'text-buffer'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
describe "editorView.", ->
|
||||
@@ -29,6 +31,46 @@ describe "editorView.", ->
|
||||
benchmark "keydown-event-with-no-binding", 10, ->
|
||||
keymap.handleKeyEvent(event)
|
||||
|
||||
describe "markers.", ->
|
||||
[editor, buffer, lineCount, editorPromise] = []
|
||||
class EmitterTest
|
||||
Emitter.includeInto(EmitterTest)
|
||||
emitSomething: ->
|
||||
@emit('something')
|
||||
|
||||
beforeEach ->
|
||||
editorPromise = atom.project.open('medium.coffee').then (ed) => editor = ed
|
||||
waitsForPromise -> editorPromise
|
||||
runs ->
|
||||
lineCount = editor.buffer.getLineCount()
|
||||
buffer = new TextBuffer(text:editor.getText())
|
||||
|
||||
fpbenchmark "marker-creation-from-editor", ->
|
||||
for row in [0...lineCount]
|
||||
if length = editor.buffer.lineLengthForRow(row)
|
||||
editor.markBufferRange(new Range(new Point(row, 0), new Point(row, 1)))
|
||||
return
|
||||
|
||||
fpbenchmark "marker-creation-from-text-buffer", ->
|
||||
for row in [0...lineCount]
|
||||
if length = editor.buffer.lineLengthForRow(row)
|
||||
buffer.markRange(new Range(new Point(row, 0), new Point(row, 1)))
|
||||
return
|
||||
|
||||
fpbenchmark "object-creation-and-emit", ->
|
||||
for row in [0...lineCount]
|
||||
if length = editor.buffer.lineLengthForRow(row)
|
||||
et = new EmitterTest()
|
||||
et.on 'something', ->
|
||||
et.emitSomething()
|
||||
return
|
||||
|
||||
fpbenchmark "object-creation", ->
|
||||
for row in [0...lineCount]
|
||||
if length = editor.buffer.lineLengthForRow(row)
|
||||
new EmitterTest()
|
||||
return
|
||||
|
||||
describe "opening-buffers.", ->
|
||||
benchmark "300-line-file.", ->
|
||||
buffer = project.bufferForPathSync('medium.coffee')
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
# Find and replace speedup investigation
|
||||
|
||||
Find and replace is slow when there are a lot of matches on the page.
|
||||
|
||||
## Test junk
|
||||
|
||||
I'm testing with `editor-view.coffee` and searching for a single space. There are 9871 spaces in `editor-view.coffee`. I added a command to find and replace that creates a profile.
|
||||
|
||||
```coffee
|
||||
@subscriber.subscribeToCommand atom.workspaceView, 'find-and-replace:find-space-profile', =>
|
||||
@findView.findEditor.setText ''
|
||||
@findView.updateModel pattern: ''
|
||||
|
||||
console.profile('find spaces')
|
||||
console.time('find spaces')
|
||||
@findView.findEditor.setText ' '
|
||||
@findView.updateModel pattern: ' '
|
||||
console.timeEnd('find spaces')
|
||||
console.profileEnd('find spaces')
|
||||
```
|
||||
|
||||
## Baseline
|
||||
|
||||
As the code is today it takes ~1500ms to do this. Much of the time spent in `SpanSkipList::totalTo`. But there is a whole lot else costing time.
|
||||
|
||||
It didnt fluctuate much, ~1490ms - 1590ms.
|
||||
|
||||

|
||||
|
||||
## Experiment with removing totalTo
|
||||
|
||||
I wondered, was the skip list helping us?
|
||||
|
||||
~1280ms; 1260ms - 1360ms
|
||||
|
||||
```coffee
|
||||
# dumb algorithm
|
||||
positionForCharacterIndex: (offset) ->
|
||||
offset = Math.max(0, offset)
|
||||
offset = Math.min(@getText().length, offset)
|
||||
|
||||
row = 0
|
||||
column = 0
|
||||
for line in @lines
|
||||
if line.length < offset
|
||||
offset -= line.length + @lineEndingForRow(row).length
|
||||
row++
|
||||
else
|
||||
column = offset
|
||||
break
|
||||
|
||||
if row > @getLastRow()
|
||||
@getEndPosition()
|
||||
else
|
||||
new Point(row, column)
|
||||
```
|
||||
|
||||

|
||||
|
||||
It's consistently a litte faster than `totalTo`, but also reduces the GC pressure.
|
||||
|
||||
## Remove the places it emits slow
|
||||
|
||||
Emitter was recently updated to optimize the single arg case. And all of the decotation events were using 2 params. Reduced them to 1, and git a little bit.
|
||||
|
||||
It is consistently in the mid-low 1200ms range, and `emitSlow` is no longer being called.
|
||||
|
||||

|
||||
|
||||
## Attempt to optimize the emitter.
|
||||
|
||||
Is the emitter doing too much? Making too many temp objects?
|
||||
|
||||
## No decorations
|
||||
|
||||
How much faster is it with out decorations? A little bit less GC pressure.
|
||||
|
||||
820 - 900
|
||||
|
||||

|
||||
|
||||
## No markers
|
||||
|
||||
How much faster is it with out markers and no decorations?
|
||||
|
||||
210ms - 240ms
|
||||
|
||||
Whoa. Now, it's all about finding the position in char range. The GC isnt even in the picture.
|
||||
|
||||

|
||||
|
||||
## Optimizing Marker creation
|
||||
|
||||
So our largest chunk of time is spent creating markers. How slow is marker creation compared to regular object creation + emit
|
||||
|
||||

|
||||
|
||||
It's a lot slower.
|
||||
|
||||
This The profile for marker-creation:
|
||||
|
||||

|
||||
|
||||
The profile looked like we were spending some time in `Range.toObject`. Is there a difference between `new Range(new Point(row, 0), new Point(row, 1))` and `[[row, 0], [row, 1]]`? Yeah, and it's pretty big
|
||||
|
||||

|
||||
|
||||
Could optimize it to not use `args...`? __YES__
|
||||
|
||||
```coffee
|
||||
@fromObject: (object, copy) ->
|
||||
if Array.isArray(object)
|
||||
new this(...)
|
||||
```
|
||||
|
||||
to
|
||||
|
||||
```coffee
|
||||
@fromObject: (object, copy) ->
|
||||
if Array.isArray(object)
|
||||
[pointA, pointB] = object
|
||||
new this(pointA, pointB)
|
||||
```
|
||||
|
||||
And now they are equal performance!
|
||||
|
||||

|
||||
|
||||
### Marker creation in Atom vs TextBuffer
|
||||
|
||||
Creation is fast on the text-buffer side, and ~3x+ slower on the atom side. What are we doing?
|
||||
|
||||

|
||||
|
||||
Looks like we're spending a lot more time (~4x!) in the garbage collector and a whole lot more time emitting events.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### Subscribing is slow
|
||||
|
||||
Commenting out the changed and destroyed handlers in `DisplayBufferMarker` cut the time in _half_.
|
||||
|
||||
```coffee
|
||||
# @subscribe @bufferMarker, 'destroyed', => @destroyed()
|
||||
# @subscribe @bufferMarker, 'changed', (event) => @notifyObservers(event)
|
||||
```
|
||||
|
||||

|
||||
|
||||
Even using `on` rather than subscribe is faster:
|
||||
|
||||

|
||||
@@ -786,13 +786,13 @@ class DisplayBuffer extends Model
|
||||
# in the change handler. Bookmarks does this.
|
||||
if decorations?
|
||||
for decoration in decorations
|
||||
@emit 'decoration-changed', marker, decoration, event
|
||||
@emit 'decoration-changed', decoration
|
||||
|
||||
decoration = new Decoration(marker, this, decorationParams)
|
||||
@decorationsByMarkerId[marker.id] ?= []
|
||||
@decorationsByMarkerId[marker.id].push(decoration)
|
||||
@decorationsById[decoration.id] = decoration
|
||||
@emit 'decoration-added', marker, decoration
|
||||
@emit 'decoration-added', decoration
|
||||
decoration
|
||||
|
||||
removeDecoration: (decoration) ->
|
||||
@@ -803,13 +803,13 @@ class DisplayBuffer extends Model
|
||||
if index > -1
|
||||
decorations.splice(index, 1)
|
||||
delete @decorationsById[decoration.id]
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@emit 'decoration-removed', decoration
|
||||
@removedAllMarkerDecorations(marker) if decorations.length is 0
|
||||
|
||||
removeAllDecorationsForMarker: (marker) ->
|
||||
decorations = @decorationsByMarkerId[marker.id].slice()
|
||||
for decoration in decorations
|
||||
@emit 'decoration-removed', marker, decoration
|
||||
@emit 'decoration-removed', decoration
|
||||
@removedAllMarkerDecorations(marker)
|
||||
|
||||
removedAllMarkerDecorations: (marker) ->
|
||||
|
||||
+4
-4
@@ -227,10 +227,10 @@ class Editor extends Model
|
||||
@subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
|
||||
@subscribe @displayBuffer, 'tokenized', => @handleTokenization()
|
||||
@subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
|
||||
@subscribe @displayBuffer, "decoration-added", (args...) => @emit 'decoration-added', args...
|
||||
@subscribe @displayBuffer, "decoration-removed", (args...) => @emit 'decoration-removed', args...
|
||||
@subscribe @displayBuffer, "decoration-changed", (args...) => @emit 'decoration-changed', args...
|
||||
@subscribe @displayBuffer, "decoration-updated", (args...) => @emit 'decoration-updated', args...
|
||||
@subscribe @displayBuffer, "decoration-added", (decoration) => @emit 'decoration-added', decoration
|
||||
@subscribe @displayBuffer, "decoration-removed", (decoration) => @emit 'decoration-removed', decoration
|
||||
@subscribe @displayBuffer, "decoration-changed", (decoration) => @emit 'decoration-changed', decoration
|
||||
@subscribe @displayBuffer, "decoration-updated", (decoration) => @emit 'decoration-updated', decoration
|
||||
@subscribe @displayBuffer, "character-widths-changed", (changeCount) => @emit 'character-widths-changed', changeCount
|
||||
|
||||
getViewClass: ->
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário