From 4386a68b3c1585a15d805e4a992e798c58f9d2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 1 Apr 2020 14:29:55 +0200 Subject: [PATCH 01/11] Use spread operator for concatenating paths in Position constructor. --- src/model/position.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/position.js b/src/model/position.js index 6bc5f2206..62396e200 100644 --- a/src/model/position.js +++ b/src/model/position.js @@ -82,7 +82,7 @@ export default class Position { } // Normalize the root and path (if element was passed). - path = root.getPath().concat( path ); + path = [ ...root.getPath(), ...path ]; root = root.root; /** From a5e9280b4ec3e9da17c08cb425ce30131754a060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Wed, 1 Apr 2020 14:49:18 +0200 Subject: [PATCH 02/11] Use for loops to concatenate paths in model/Position. --- src/model/position.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/model/position.js b/src/model/position.js index 62396e200..c0b193045 100644 --- a/src/model/position.js +++ b/src/model/position.js @@ -82,7 +82,8 @@ export default class Position { } // Normalize the root and path (if element was passed). - path = [ ...root.getPath(), ...path ]; + const newPath = concatPaths( root.getPath(), path ); + root = root.root; /** @@ -124,7 +125,7 @@ export default class Position { * @readonly * @member {Array.} module:engine/model/position~Position#path */ - this.path = path; + this.path = newPath; /** * Position stickiness. See {@link module:engine/model/position~PositionStickiness}. @@ -840,7 +841,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 = concatPaths( combined.path, this.path.slice( i + 1 ) ); return combined; } @@ -1057,3 +1058,17 @@ export default class Position { * * @typedef {String} module:engine/model/position~PositionStickiness */ + +function concatPaths( rootPath, path ) { + const newPath = []; + + 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; +} From abf6d1f6db69ab372b03ec430743642eabad5c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 2 Apr 2020 10:25:50 +0200 Subject: [PATCH 03/11] Add docs to helper concatenatePaths() helper method. --- src/model/position.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/model/position.js b/src/model/position.js index c0b193045..672c7f247 100644 --- a/src/model/position.js +++ b/src/model/position.js @@ -81,9 +81,8 @@ export default class Position { ); } - // Normalize the root and path (if element was passed). - const newPath = concatPaths( root.getPath(), path ); - + // Normalize the root and path when element (not root) is passed. + path = concatenatePaths( root.getPath(), path ); root = root.root; /** @@ -125,7 +124,7 @@ export default class Position { * @readonly * @member {Array.} module:engine/model/position~Position#path */ - this.path = newPath; + this.path = path; /** * Position stickiness. See {@link module:engine/model/position~PositionStickiness}. @@ -841,7 +840,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 = concatPaths( combined.path, this.path.slice( i + 1 ) ); + combined.path = concatenatePaths( combined.path, this.path.slice( i + 1 ) ); return combined; } @@ -1059,7 +1058,12 @@ export default class Position { * @typedef {String} module:engine/model/position~PositionStickiness */ -function concatPaths( rootPath, path ) { +// 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 = []; for ( let i = 0; i < rootPath.length; i++ ) { From 75a53ec48e0e6feae9476d53c8f492abf66c3d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 3 Apr 2020 08:15:50 +0200 Subject: [PATCH 04/11] Do not use library method in Position#offset. This is a performance enhancement. Offset is calculated many times in model operations. Getters should be As Fast As Possible thus calling external method that do too much is not desired here. --- src/model/position.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/model/position.js b/src/model/position.js index 672c7f247..a7c5f4500 100644 --- a/src/model/position.js +++ b/src/model/position.js @@ -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'; @@ -141,7 +140,7 @@ export default class Position { * @type {Number} */ get offset() { - return last( this.path ); + return this.path[ this.path.length - 1 ]; } /** From e49c55cf037632b9f4387500a905760f25dfa8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 6 Apr 2020 17:18:34 +0200 Subject: [PATCH 05/11] Add simple manual test for Position's path concatenation. --- tests/manual/performance/position.html | 7 ++ tests/manual/performance/position.js | 120 +++++++++++++++++++++++++ tests/manual/performance/position.md | 5 ++ 3 files changed, 132 insertions(+) create mode 100644 tests/manual/performance/position.html create mode 100644 tests/manual/performance/position.js create mode 100644 tests/manual/performance/position.md diff --git a/tests/manual/performance/position.html b/tests/manual/performance/position.html new file mode 100644 index 000000000..681a39d64 --- /dev/null +++ b/tests/manual/performance/position.html @@ -0,0 +1,7 @@ +
+ +
+ +
+ +
diff --git a/tests/manual/performance/position.js b/tests/manual/performance/position.js new file mode 100644 index 000000000..39b06dada --- /dev/null +++ b/tests/manual/performance/position.js @@ -0,0 +1,120 @@ +/** + * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +/* globals document, setTimeout */ + +document.getElementById( 'run' ).addEventListener( 'click', () => { + log( 'Running tests...' ); + + setTimeout( async () => { + await runTest( 'concat', concat ); + await runTest( 'spread operator', spread ); + await runTest( 'for-loop', forLoop ); + await runTest( 'ultra-loop', ultraForLoop ); + + log( 'done' ); + } ); +} ); + +const output = document.getElementById( 'output' ); + +function log( line ) { + const paragraphElement = document.createElement( 'p' ); + paragraphElement.innerText = line; + output.appendChild( paragraphElement ); +} + +const roots = [ + [ 1, 2, 3, 4 ], + [ 1 ], + [ 1, 2 ], + [ 1 ], + [ 7 ], + [ 8 ], + [ 7 ], + [ 4, 2 ], + [ 0, 2 ], + [ 1, 2, 3, 4 ] +]; + +const paths = [ + [ 0 ], + [ 6 ], + + [ 2 ], + [ 1 ], + [ 0, 1 ], + [ 0, 1 ], + [ 0, 1 ], + [ 0, 1, 3 ], + [ 0, 2, 0 ] +]; + +function runTest( name, callback ) { + return new Promise( resolve => { + const start = new Date(); + + for ( let i = 0; i < 500000; i++ ) { + callback(); + } + + const end = new Date(); + + log( ` > ${ name } took ${ end - start }ms` ); + + setTimeout( () => { + resolve(); + }, 50 ); + } ); +} + +function concat() { + for ( const rootPath of roots ) { + for ( const path of paths ) { + rootPath.concat( path ); + } + } +} + +function spread() { + for ( const rootPath of roots ) { + for ( const path of paths ) { + [ ...rootPath, ...path ]; + } + } +} + +function forLoop() { + for ( const rootPath of roots ) { + for ( const path of paths ) { + const newPath = []; + + for ( let i = 0; i < rootPath.length; i++ ) { + newPath.push( rootPath[ i ] ); + } + + for ( let i = 0; i < path.length; i++ ) { + newPath.push( path[ i ] ); + } + } + } +} + +function ultraForLoop() { + for ( const rootPath of roots ) { + for ( const path of paths ) { + const fullLength = rootPath.length + path.length; + const newPath = new Array( fullLength ); + + for ( let i = 0; i < rootPath.length; i++ ) { + newPath[ i ] = rootPath[ i ]; + } + + for ( let i = 0; i < path.length; i++ ) { + newPath[ rootPath.length + i ] = path[ i ]; + } + } + } +} diff --git a/tests/manual/performance/position.md b/tests/manual/performance/position.md new file mode 100644 index 000000000..7cd0c549a --- /dev/null +++ b/tests/manual/performance/position.md @@ -0,0 +1,5 @@ +# Performance: pasting data + +1. Begin performance recording in devtools. +1. Click a button for test. +1. Stop performance recording. From eae2b4850e832e845bab7a48bce4c1085faf3001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Apr 2020 08:13:22 +0200 Subject: [PATCH 06/11] Use position class to test performance. --- tests/manual/performance/position.js | 133 ++++++++++++++------------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/tests/manual/performance/position.js b/tests/manual/performance/position.js index 39b06dada..dcdfa4b02 100644 --- a/tests/manual/performance/position.js +++ b/tests/manual/performance/position.js @@ -9,10 +9,10 @@ document.getElementById( 'run' ).addEventListener( 'click', () => { log( 'Running tests...' ); setTimeout( async () => { - await runTest( 'concat', concat ); - await runTest( 'spread operator', spread ); - await runTest( 'for-loop', forLoop ); - await runTest( 'ultra-loop', ultraForLoop ); + await runTest( 'concat', testConcat ); + await runTest( 'spread operator', testSpread ); + await runTest( 'for-loop', testForLoop ); + await runTest( 'ultra-loop', testUltraForLoop ); log( 'done' ); } ); @@ -26,38 +26,22 @@ function log( line ) { output.appendChild( paragraphElement ); } -const roots = [ - [ 1, 2, 3, 4 ], - [ 1 ], - [ 1, 2 ], - [ 1 ], - [ 7 ], - [ 8 ], - [ 7 ], - [ 4, 2 ], - [ 0, 2 ], - [ 1, 2, 3, 4 ] -]; - -const paths = [ - [ 0 ], - [ 6 ], - - [ 2 ], - [ 1 ], - [ 0, 1 ], - [ 0, 1 ], - [ 0, 1 ], - [ 0, 1, 3 ], - [ 0, 2, 0 ] -]; - function runTest( name, callback ) { return new Promise( resolve => { const start = new Date(); - for ( let i = 0; i < 500000; i++ ) { - callback(); + const repetitions = 10000000; + const z = Array( repetitions ); + + const root = { + root: 'foo', + path: [ 0 ] + }; + const path = [ 0, 2 ]; + + for ( let i = 0; i < repetitions; i++ ) { + const newPath = callback( root, path ); + z[ i ] = [ newPath.length ]; } const end = new Date(); @@ -70,51 +54,70 @@ function runTest( name, callback ) { } ); } -function concat() { - for ( const rootPath of roots ) { - for ( const path of paths ) { - rootPath.concat( path ); +class Position { + constructor( root, path, stickiness = 'left', pathMergeMethod ) { + if ( !( path instanceof Array ) || path.length === 0 ) { + throw new Error( 'model-position-path-incorrect-format' ); } + + path = pathMergeMethod( root.path, path ); + root = root.root; + + this.root = root; + this.path = path; + this.stickiness = stickiness; } } -function spread() { - for ( const rootPath of roots ) { - for ( const path of paths ) { - [ ...rootPath, ...path ]; - } - } +function testConcat( root, path ) { + return new Position( root, path, 'right', concat ); +} + +function testSpread( root, path ) { + return new Position( root, path, 'right', spread ); } -function forLoop() { - for ( const rootPath of roots ) { - for ( const path of paths ) { - const newPath = []; +function testForLoop( root, path ) { + return new Position( root, path, 'right', forLoop ); +} - for ( let i = 0; i < rootPath.length; i++ ) { - newPath.push( rootPath[ i ] ); - } +function testUltraForLoop( root, path ) { + return new Position( root, path, 'right', ultraForLoop ); +} - for ( let i = 0; i < path.length; i++ ) { - newPath.push( path[ i ] ); - } - } +function concat( rootPath, path ) { + return rootPath.concat( path ); +} + +function spread( rootPath, path ) { + return [ ...rootPath, ...path ]; +} + +function forLoop( rootPath, path ) { + const newPath = []; + + 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; } -function ultraForLoop() { - for ( const rootPath of roots ) { - for ( const path of paths ) { - const fullLength = rootPath.length + path.length; - const newPath = new Array( fullLength ); +function ultraForLoop( rootPath, path ) { + const fullLength = rootPath.length + path.length; + const newPath = new Array( fullLength ); - for ( let i = 0; i < rootPath.length; i++ ) { - newPath[ i ] = rootPath[ i ]; - } + for ( let i = 0; i < rootPath.length; i++ ) { + newPath[ i ] = rootPath[ i ]; + } - for ( let i = 0; i < path.length; i++ ) { - newPath[ rootPath.length + i ] = path[ i ]; - } - } + for ( let i = 0; i < path.length; i++ ) { + newPath[ rootPath.length + i ] = path[ i ]; } + + return newPath; } From f8e5c8f03ad800de6e9018035f1b82af44b2bb1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Apr 2020 09:34:49 +0200 Subject: [PATCH 07/11] Use separate position classes. --- tests/manual/performance/position.js | 67 +++++++++++++++++++++------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/tests/manual/performance/position.js b/tests/manual/performance/position.js index dcdfa4b02..a3584643b 100644 --- a/tests/manual/performance/position.js +++ b/tests/manual/performance/position.js @@ -54,13 +54,13 @@ function runTest( name, callback ) { } ); } -class Position { - constructor( root, path, stickiness = 'left', pathMergeMethod ) { +class PositionConcat { + constructor( root, path, stickiness = 'left' ) { if ( !( path instanceof Array ) || path.length === 0 ) { throw new Error( 'model-position-path-incorrect-format' ); } - path = pathMergeMethod( root.path, path ); + path = root.path.concat( path ); root = root.root; this.root = root; @@ -69,28 +69,65 @@ class Position { } } -function testConcat( root, path ) { - return new Position( root, path, 'right', concat ); +class PositionSpread { + constructor( root, path, stickiness = 'left' ) { + if ( !( path instanceof Array ) || path.length === 0 ) { + throw new Error( 'model-position-path-incorrect-format' ); + } + + path = [ ...root.path, ...path ]; + root = root.root; + + this.root = root; + this.path = path; + this.stickiness = stickiness; + } } -function testSpread( root, path ) { - return new Position( root, path, 'right', spread ); +class PositionForLoop { + constructor( root, path, stickiness = 'left' ) { + if ( !( path instanceof Array ) || path.length === 0 ) { + throw new Error( 'model-position-path-incorrect-format' ); + } + + path = forLoop( root.path, path ); + root = root.root; + + this.root = root; + this.path = path; + this.stickiness = stickiness; + } } -function testForLoop( root, path ) { - return new Position( root, path, 'right', forLoop ); +class PositionUltraForLoop { + constructor( root, path, stickiness = 'left' ) { + if ( !( path instanceof Array ) || path.length === 0 ) { + throw new Error( 'model-position-path-incorrect-format' ); + } + + path = ultraForLoop( root.path, path ); + root = root.root; + + this.root = root; + this.path = path; + this.stickiness = stickiness; + } } -function testUltraForLoop( root, path ) { - return new Position( root, path, 'right', ultraForLoop ); +function testConcat( root, path ) { + return new PositionConcat( root, path ); } -function concat( rootPath, path ) { - return rootPath.concat( path ); +function testSpread( root, path ) { + return new PositionSpread( root, path ); } -function spread( rootPath, path ) { - return [ ...rootPath, ...path ]; +function testForLoop( root, path ) { + return new PositionForLoop( root, path ); +} + +function testUltraForLoop( root, path ) { + return new PositionUltraForLoop( root, path ); } function forLoop( rootPath, path ) { From 5708ba27a1b50c559c534bc72b08da46f66d90de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Apr 2020 09:56:17 +0200 Subject: [PATCH 08/11] Fix test performance. --- tests/manual/performance/position.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/manual/performance/position.js b/tests/manual/performance/position.js index a3584643b..115a1201e 100644 --- a/tests/manual/performance/position.js +++ b/tests/manual/performance/position.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -/* globals document, setTimeout */ +/* globals window, document, setTimeout */ document.getElementById( 'run' ).addEventListener( 'click', () => { log( 'Running tests...' ); @@ -18,6 +18,8 @@ document.getElementById( 'run' ).addEventListener( 'click', () => { } ); } ); +window.cache = []; + const output = document.getElementById( 'output' ); function log( line ) { @@ -31,7 +33,6 @@ function runTest( name, callback ) { const start = new Date(); const repetitions = 10000000; - const z = Array( repetitions ); const root = { root: 'foo', @@ -41,7 +42,7 @@ function runTest( name, callback ) { for ( let i = 0; i < repetitions; i++ ) { const newPath = callback( root, path ); - z[ i ] = [ newPath.length ]; + window.cache.push( newPath.length ); } const end = new Date(); From dada66d829c5ba5643d2069c950809bc652b2d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Thu, 9 Apr 2020 10:02:49 +0200 Subject: [PATCH 09/11] Add note about times in tests. --- tests/manual/performance/position.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/manual/performance/position.md b/tests/manual/performance/position.md index 7cd0c549a..5aa6f83c9 100644 --- a/tests/manual/performance/position.md +++ b/tests/manual/performance/position.md @@ -3,3 +3,5 @@ 1. Begin performance recording in devtools. 1. Click a button for test. 1. Stop performance recording. + +Note that times reported in web page are total time for a test function (including other operations). In order to verify which methods is fastest always look in on the "total time" of each Position constructor stubs. From 3c7ea6390159470cdd9cc03b7aa8551c20fcee89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Mon, 13 Apr 2020 14:21:22 +0200 Subject: [PATCH 10/11] The root is passed most commonly and it has an empty path, so we can optimize for this scenario. --- src/model/position.js | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/model/position.js b/src/model/position.js index f5bb60d13..da3e0a013 100644 --- a/src/model/position.js +++ b/src/model/position.js @@ -80,8 +80,12 @@ export default class Position { } // Normalize the root and path when element (not root) is passed. - path = concatenatePaths( root.getPath(), path ); - root = root.root; + if ( root.is( 'rootElement' ) ) { + path = path.slice(); + } else { + path = [ ...root.getPath(), ...path ]; + root = root.root; + } /** * Root of the position path. @@ -856,7 +860,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 = concatenatePaths( combined.path, this.path.slice( i + 1 ) ); + combined.path = [ ...combined.path, ...this.path.slice( i + 1 ) ]; return combined; } @@ -1074,25 +1078,6 @@ 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 = []; - - 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; -} - // Helper function used to inline text node access by using a cached parent. // Reduces the access to the Position#parent property 3 times (in total, when taken into account what #nodeAfter and #nodeBefore do). // See #6579. From e63694a0330c7bbe02501dbe968554d842e2133d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Tue, 14 Apr 2020 12:00:24 +0200 Subject: [PATCH 11/11] Removed a test that's unnecessary now. --- tests/manual/performance/position.html | 7 -- tests/manual/performance/position.js | 161 ------------------------- tests/manual/performance/position.md | 7 -- 3 files changed, 175 deletions(-) delete mode 100644 tests/manual/performance/position.html delete mode 100644 tests/manual/performance/position.js delete mode 100644 tests/manual/performance/position.md diff --git a/tests/manual/performance/position.html b/tests/manual/performance/position.html deleted file mode 100644 index 681a39d64..000000000 --- a/tests/manual/performance/position.html +++ /dev/null @@ -1,7 +0,0 @@ -
- -
- -
- -
diff --git a/tests/manual/performance/position.js b/tests/manual/performance/position.js deleted file mode 100644 index 115a1201e..000000000 --- a/tests/manual/performance/position.js +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license - */ - -/* globals window, document, setTimeout */ - -document.getElementById( 'run' ).addEventListener( 'click', () => { - log( 'Running tests...' ); - - setTimeout( async () => { - await runTest( 'concat', testConcat ); - await runTest( 'spread operator', testSpread ); - await runTest( 'for-loop', testForLoop ); - await runTest( 'ultra-loop', testUltraForLoop ); - - log( 'done' ); - } ); -} ); - -window.cache = []; - -const output = document.getElementById( 'output' ); - -function log( line ) { - const paragraphElement = document.createElement( 'p' ); - paragraphElement.innerText = line; - output.appendChild( paragraphElement ); -} - -function runTest( name, callback ) { - return new Promise( resolve => { - const start = new Date(); - - const repetitions = 10000000; - - const root = { - root: 'foo', - path: [ 0 ] - }; - const path = [ 0, 2 ]; - - for ( let i = 0; i < repetitions; i++ ) { - const newPath = callback( root, path ); - window.cache.push( newPath.length ); - } - - const end = new Date(); - - log( ` > ${ name } took ${ end - start }ms` ); - - setTimeout( () => { - resolve(); - }, 50 ); - } ); -} - -class PositionConcat { - constructor( root, path, stickiness = 'left' ) { - if ( !( path instanceof Array ) || path.length === 0 ) { - throw new Error( 'model-position-path-incorrect-format' ); - } - - path = root.path.concat( path ); - root = root.root; - - this.root = root; - this.path = path; - this.stickiness = stickiness; - } -} - -class PositionSpread { - constructor( root, path, stickiness = 'left' ) { - if ( !( path instanceof Array ) || path.length === 0 ) { - throw new Error( 'model-position-path-incorrect-format' ); - } - - path = [ ...root.path, ...path ]; - root = root.root; - - this.root = root; - this.path = path; - this.stickiness = stickiness; - } -} - -class PositionForLoop { - constructor( root, path, stickiness = 'left' ) { - if ( !( path instanceof Array ) || path.length === 0 ) { - throw new Error( 'model-position-path-incorrect-format' ); - } - - path = forLoop( root.path, path ); - root = root.root; - - this.root = root; - this.path = path; - this.stickiness = stickiness; - } -} - -class PositionUltraForLoop { - constructor( root, path, stickiness = 'left' ) { - if ( !( path instanceof Array ) || path.length === 0 ) { - throw new Error( 'model-position-path-incorrect-format' ); - } - - path = ultraForLoop( root.path, path ); - root = root.root; - - this.root = root; - this.path = path; - this.stickiness = stickiness; - } -} - -function testConcat( root, path ) { - return new PositionConcat( root, path ); -} - -function testSpread( root, path ) { - return new PositionSpread( root, path ); -} - -function testForLoop( root, path ) { - return new PositionForLoop( root, path ); -} - -function testUltraForLoop( root, path ) { - return new PositionUltraForLoop( root, path ); -} - -function forLoop( rootPath, path ) { - const newPath = []; - - 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; -} - -function ultraForLoop( rootPath, path ) { - const fullLength = rootPath.length + path.length; - const newPath = new Array( fullLength ); - - for ( let i = 0; i < rootPath.length; i++ ) { - newPath[ i ] = rootPath[ i ]; - } - - for ( let i = 0; i < path.length; i++ ) { - newPath[ rootPath.length + i ] = path[ i ]; - } - - return newPath; -} diff --git a/tests/manual/performance/position.md b/tests/manual/performance/position.md deleted file mode 100644 index 5aa6f83c9..000000000 --- a/tests/manual/performance/position.md +++ /dev/null @@ -1,7 +0,0 @@ -# Performance: pasting data - -1. Begin performance recording in devtools. -1. Click a button for test. -1. Stop performance recording. - -Note that times reported in web page are total time for a test function (including other operations). In order to verify which methods is fastest always look in on the "total time" of each Position constructor stubs.