From 86ee5d18931cae2b364a808c1286bffe7d94487f Mon Sep 17 00:00:00 2001 From: Blagoja Evkoski Date: Sat, 8 Dec 2018 15:33:07 +0100 Subject: [PATCH] perf(core): Optimize data.kindOf data.isOfKind (#124) --- .../src/data/__tests__/element-benchmarks.js | 69 +++++++++++++++++++ packages/core/src/data/__tests__/element.js | 4 +- packages/core/src/data/element.js | 24 +++---- packages/core/src/registry/Registry.js | 8 ++- .../core/src/registry/__tests__/Registry.js | 18 ++++- 5 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 packages/core/src/data/__tests__/element-benchmarks.js diff --git a/packages/core/src/data/__tests__/element-benchmarks.js b/packages/core/src/data/__tests__/element-benchmarks.js new file mode 100644 index 0000000..2600ddf --- /dev/null +++ b/packages/core/src/data/__tests__/element-benchmarks.js @@ -0,0 +1,69 @@ +/** + * @jest-environment node + */ + +import { Suite } from 'benchmark' +import I from 'immutable' + +import { canonical, isOfKind, kindOf } from '../' + +describe('element benchmarks', () => { + const detective1 = I.fromJS({ + kind: 'detective', + name: 'Sherlock Holmes', + }) + const detective2 = I.fromJS({ + kind: ['detective', 'the'], + name: 'Sherlock Holmes', + }) + const detective3 = I.fromJS({ + kind: ['detective', 'the', 'smartest'], + name: 'Sherlock Holmes', + }) + const detective4 = I.fromJS({ + kind: ['detective', 'the', 'smartest', 'ever'], + name: 'Sherlock Holmes', + }) + + test.skip('canonical', () => { + new Suite() + .add('canonical memoized', () => { + canonical('detective') + canonical(['detective', 'the']) + canonical(['detective', 'the', 'smartest']) + canonical(['detective', 'the', 'smartest', 'ever']) + }) + .on('cycle', function(event) { + console.log(event.target.toString()) + }) + .run() + }) + + test.skip('isOfKind', () => { + new Suite() + .add('isOfKind memoized', () => { + isOfKind('detective', detective3) + isOfKind(['detective', 'the'], detective3) + isOfKind(['detective', 'the', 'smartest'], detective3) + isOfKind(['detective', 'the', 'smartest', 'ever'], detective3) + }) + .on('cycle', function(event) { + console.log(event.target.toString()) + }) + .run() + }) + + test.skip('kindOf', () => { + new Suite() + .add('kindOf memoized', () => { + kindOf(detective1) + kindOf(detective2) + kindOf(detective3) + kindOf(detective4) + }) + .on('cycle', function(event) { + console.log(event.target.toString()) + }) + .run() + }) +}) diff --git a/packages/core/src/data/__tests__/element.js b/packages/core/src/data/__tests__/element.js index 03c2cca..b1f7183 100644 --- a/packages/core/src/data/__tests__/element.js +++ b/packages/core/src/data/__tests__/element.js @@ -142,8 +142,8 @@ describe('element', function() { }) it('properly normalizes element kinds', function() { - expect(canonical(null)).toEqual(null) - expect(canonical(true)).toEqual(null) + expect(canonical(null) == null).toBe(true) + expect(canonical(true) == null).toBe(true) expect(canonical('component')).toEqual(List.of('component')) expect(canonical(['component', 'test'])).toEqual( List.of('component', 'test') diff --git a/packages/core/src/data/element.js b/packages/core/src/data/element.js index 23da80a..db6c326 100644 --- a/packages/core/src/data/element.js +++ b/packages/core/src/data/element.js @@ -2,8 +2,9 @@ import * as R from 'ramda' import invariant from 'invariant' -import deprecated from '../log/deprecated' import { List, Seq, is, Iterable } from 'immutable' +import memoize from '../registry/memoize' +import deprecated from '../log/deprecated' /** * Checks if a given object is of the provided kind. @@ -48,7 +49,7 @@ export function isElementRef(obj) { * @returns {*} */ export const isExactlyOfKind = R.curry(function isExactlyOfKind(kind, element) { - if (element == null || kindOf(element) == null) { + if (element == null) { return false } @@ -114,17 +115,9 @@ export function ancestorKinds(ref) { return Seq(subKinds()) } -/** - * @param ref an element reference - * @returns the canonical version for the reference kind - */ -export function canonical(ref) { - return normalize(ref) -} - -function normalize(kind) { +function _normalize(kind) { if (typeof kind === 'string') { - return normalize([kind]) + return List.of(kind) } if (Array.isArray(kind)) { @@ -142,6 +135,13 @@ function normalize(kind) { return null } +const normalize = memoize(_normalize) + +/** + * @returns the canonical version for the reference kind + */ +export const canonical = normalize + export function pathsToChildElements(element) { return childPositions(element).flatMap(childrenPath => { const children = element.get(childrenPath) diff --git a/packages/core/src/registry/Registry.js b/packages/core/src/registry/Registry.js index 57f1465..48a4904 100644 --- a/packages/core/src/registry/Registry.js +++ b/packages/core/src/registry/Registry.js @@ -4,8 +4,14 @@ import { List } from 'immutable' import Trie from './impl/Trie' export function adaptKey(key) { + // prettier-ignore if (key instanceof List) { - // cast + if (key.size === 0) return [] + if (key.size === 1) return [key.get(0)] + if (key.size === 2) return [key.get(0), key.get(1)] + if (key.size === 3) return [key.get(0), key.get(1), key.get(2)] + if (key.size === 4) return [key.get(0), key.get(1), key.get(2), key.get(3)] + if (key.size === 5) return [key.get(0), key.get(1), key.get(2), key.get(3), key.get(4)] return key.toArray() } return key diff --git a/packages/core/src/registry/__tests__/Registry.js b/packages/core/src/registry/__tests__/Registry.js index bbffc8e..da13b54 100644 --- a/packages/core/src/registry/__tests__/Registry.js +++ b/packages/core/src/registry/__tests__/Registry.js @@ -2,7 +2,23 @@ import { List } from 'immutable' -import Registry from '../Registry' +import Registry, { adaptKey } from '../Registry' + +describe('adaptKey', () => { + it('properly adapts keys', () => { + expect(adaptKey(null)).toEqual(null) + expect(adaptKey([])).toEqual([]) + expect(adaptKey(List([]))).toEqual([]) + expect(adaptKey(List.of('one', 'two', 'three'))).toEqual([ + 'one', + 'two', + 'three', + ]) + expect( + adaptKey(List.of('one', 'two', 'three', 'four', 'five', 'six', 'seven')) + ).toEqual(['one', 'two', 'three', 'four', 'five', 'six', 'seven']) + }) +}) describe('Registry', () => { const registry = new Registry()