-
Notifications
You must be signed in to change notification settings - Fork 40
I/6528: Faster paths concatenation #1835
Changes from 4 commits
4386a68
a5e9280
abf6d1f
75a53ec
e49c55c
eae2b48
f8e5c8f
5708ba2
184cb3d
dada66d
5485dbc
3c7ea63
e63694a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,6 @@ import TreeWalker from './treewalker'; | |
import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays'; | ||
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; | ||
import Text from './text'; | ||
import { last } from 'lodash-es'; | ||
|
||
// To check if component is loaded more than once. | ||
import '@ckeditor/ckeditor5-utils/src/version'; | ||
|
@@ -81,8 +80,8 @@ export default class Position { | |
); | ||
} | ||
|
||
// Normalize the root and path (if element was passed). | ||
path = root.getPath().concat( path ); | ||
// Normalize the root and path when element (not root) is passed. | ||
path = concatenatePaths( root.getPath(), path ); | ||
root = root.root; | ||
|
||
/** | ||
|
@@ -141,7 +140,7 @@ export default class Position { | |
* @type {Number} | ||
*/ | ||
get offset() { | ||
return last( this.path ); | ||
return this.path[ this.path.length - 1 ]; | ||
} | ||
|
||
/** | ||
|
@@ -840,7 +839,7 @@ export default class Position { | |
|
||
// Then, add the rest of the path. | ||
// If this position is at the same level as `from` position nothing will get added. | ||
combined.path = combined.path.concat( this.path.slice( i + 1 ) ); | ||
combined.path = concatenatePaths( combined.path, this.path.slice( i + 1 ) ); | ||
|
||
return combined; | ||
} | ||
|
@@ -1057,3 +1056,22 @@ export default class Position { | |
* | ||
* @typedef {String} module:engine/model/position~PositionStickiness | ||
*/ | ||
|
||
// This helper method concatenate two arrays more efficiently than Array.concat(). See ckeditor/ckeditor5#6528. | ||
// | ||
// The problem with Array.concat() is that it is an overloaded method that can concatenate various arguments, | ||
// like mixed data types with arrays (e.g. [ 0 ].concat( 1, 2, [3, 4])) thus it probably check each argument's types and more. | ||
// In Position's paths concatenation case there always be two arrays to merge so such general method is not needed. | ||
function concatenatePaths( rootPath, path ) { | ||
const newPath = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you check whether creating new array with the desired length (`new Array(len)`) isn't better? Creating an empty array and pushing items to it seems extremely counterintuitive. I understand that right now we can see that it's faster, but this is this type of optimization that may suddenly become slower. Also, on which browsers did you test it? Due to this being counterintuitive, I'd like to be 100% sure that it doesn't deoptimize this on one of the browsers. It's probably one of the places where having a manual test comparing two scenarios might be helpful in verifying this in the future too. Because it's not said that the situation won't change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'll check. The same thing was pointed out by @mlewand . I think that in this case, the change might be negligible as both arrays have length of max few values. This is potentially helpful when concatenating large arrays. The case that this might get slower will probably be a newer a case for Responding also to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Anyway, I'll check this with a manual test and post the results. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So 1 & 3 are basically the same so we can choose either. |
||
|
||
for ( let i = 0; i < rootPath.length; i++ ) { | ||
newPath.push( rootPath[ i ] ); | ||
} | ||
|
||
for ( let i = 0; i < path.length; i++ ) { | ||
newPath.push( path[ i ] ); | ||
} | ||
|
||
return newPath; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? I thought myself that
last()
is probably slow, but I didn't see any change in results.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a need here for using an external library which involves loading an external library for this. Also for other path operations, we do not use libs for simple tasks in
Position
elsewhere.The
last
helper does:and we should never have
Position
withoutthis.path
.Calling external method will be slower. It is arguable if this is an important gain. Here I get 195.7ms (1.1%) down to 2.3 ms (0.0%). In a total of ~20s (long semantic + undo). So it is arguable if this change is necessary - I'm for this change.
Current:
Current with reverting to
last
.TL;DR: Let's keep it - using
last()
makes no sense.