From b57857cc0b423d8661644c0117494aa5eef7c4e8 Mon Sep 17 00:00:00 2001 From: Youngteac Hong Date: Wed, 24 Jul 2024 19:59:31 +0900 Subject: [PATCH] Enhance tree conversion for better performance --- src/api/converter.ts | 14 ++++------ test/bench/tree.bench.ts | 60 ++++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/src/api/converter.ts b/src/api/converter.ts index eaad25540..b1ba92408 100644 --- a/src/api/converter.ts +++ b/src/api/converter.ts @@ -1064,16 +1064,12 @@ function fromTreeNodes( } const root = nodes[nodes.length - 1]; + const depthTable = new Map(); + depthTable.set(pbTreeNodes[nodes.length - 1].depth, nodes[nodes.length - 1]); for (let i = nodes.length - 2; i >= 0; i--) { - let parent: CRDTTreeNode; - for (let j = i + 1; j < nodes.length; j++) { - if (pbTreeNodes[i].depth - 1 === pbTreeNodes[j].depth) { - parent = nodes[j]; - break; - } - } - + const parent = depthTable.get(pbTreeNodes[i].depth - 1); parent!.prepend(nodes[i]); + depthTable.set(pbTreeNodes[i].depth, nodes[i]); } root.updateDescendantsSize(); @@ -1593,6 +1589,8 @@ export const converter = { toChangePack, fromChangePack, fromChanges, + toTreeNodes, + fromTreeNodes, objectToBytes, bytesToObject, bytesToSnapshot, diff --git a/test/bench/tree.bench.ts b/test/bench/tree.bench.ts index db91e3bae..cec67a367 100644 --- a/test/bench/tree.bench.ts +++ b/test/bench/tree.bench.ts @@ -1,8 +1,8 @@ -import { Document, Tree } from '@yorkie-js-sdk/src/yorkie'; +import { converter, Document, Tree, TreeNode } from '@yorkie-js-sdk/src/yorkie'; import { MaxTimeTicket } from '@yorkie-js-sdk/src/document/time/ticket'; import { describe, bench, assert } from 'vitest'; -const benchmarkTree = (size: number) => { +const benchmarkTreeEdit = (size: number) => { const doc = new Document<{ tree: Tree }>('test-doc'); doc.update((root) => { @@ -16,6 +16,7 @@ const benchmarkTree = (size: number) => { } }); }; + const benchmarkTreeDeleteAll = (size: number) => { const doc = new Document<{ tree: Tree }>('test-doc'); @@ -35,6 +36,7 @@ const benchmarkTreeDeleteAll = (size: number) => { }, 'delete them'); assert.equal(doc.getRoot().tree.toXML(), `

`); }; + const benchmarkTreeSplitGC = (size: number) => { const doc = new Document<{ tree: Tree }>('test-doc'); @@ -58,6 +60,7 @@ const benchmarkTreeSplitGC = (size: number) => { const empty = 0; assert.equal(empty, doc.getGarbageLen()); }; + const benchmarkTreeEditGC = (size: number) => { const doc = new Document<{ tree: Tree }>('test-doc'); @@ -85,27 +88,48 @@ const benchmarkTreeEditGC = (size: number) => { assert.equal(empty, doc.getGarbageLen()); }; -describe('tree', () => { - bench('tree 100', () => { - benchmarkTree(100); +const benchmarkTreeConvert = (size: number) => { + const doc = new Document<{ tree: Tree }>('test-doc'); + doc.update((root) => { + const children: Array = []; + for (let i = 1; i <= size; i++) { + children.push({ type: 'text', value: 'a' }); + } + + root.tree = new Tree({ + type: 'doc', + children: [{ type: 'p', children: children }], + }); }); - bench('tree 1000', () => { - benchmarkTree(1000); + const root = doc.getRoot().tree.getIndexTree().getRoot(); + const pbTreeNodes = converter.toTreeNodes(root); + converter.fromTreeNodes(pbTreeNodes); +}; + +describe('tree.edit', () => { + bench('tree.edit 100', () => { + benchmarkTreeEdit(100); + }); + + bench('tree.edit 1000', () => { + benchmarkTreeEdit(1000); }); - bench('tree 100', () => { - benchmarkTree(100); + bench('tree.edit 100', () => { + benchmarkTreeEdit(100); }); - bench('tree 1000', () => { - benchmarkTree(1000); + bench('tree.edit 1000', () => { + benchmarkTreeEdit(1000); }); bench('tree delete all 1000', () => { benchmarkTreeDeleteAll(1000); }); +}); +describe('tree GC', () => { bench('tree split GC 100', () => { benchmarkTreeSplitGC(100); }); @@ -122,3 +146,17 @@ describe('tree', () => { benchmarkTreeEditGC(1000); }); }); + +describe('tree convert', () => { + bench('tree convert from/to Protobuf 10000', () => { + benchmarkTreeConvert(10000); + }); + + bench('tree convert from/to Protobuf 20000', () => { + benchmarkTreeConvert(20000); + }); + + bench('tree convert from/to Protobuf 30000', () => { + benchmarkTreeConvert(30000); + }); +});