From 997ccc6d13962e5f1a7a10f7f4b51dea301595ae Mon Sep 17 00:00:00 2001 From: "Adrien Minne (adrm)" Date: Thu, 13 Apr 2023 09:39:13 +0000 Subject: [PATCH] [IMP] model: store handler arrays Currently the model create a new array every time `this.handlers` or `this.coreHandlers` are accessed. That means that we create new arrays every time we dispatch a command, leading to quite a bit of garbage collection. When deleting the first column of a 26 x 1000 sheet filled with numbers, initializing the arrays saves ~100ms in garbage collection. Task: 3272878 Part-of: odoo/o-spreadsheet#2340 --- src/model.ts | 19 +++++++++++++------ tests/xlsx_export.test.ts | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/model.ts b/src/model.ts index d5913e1ba1..9bfe51eee4 100644 --- a/src/model.ts +++ b/src/model.ts @@ -143,6 +143,9 @@ export class Model extends EventBus implements CommandDispatcher { uuidGenerator: UuidGenerator; + private readonly handlers: CommandHandler[] = []; + private readonly coreHandlers: CommandHandler[] = []; + constructor( data: any = {}, config: Partial = {}, @@ -194,6 +197,9 @@ export class Model extends EventBus implements CommandDispatcher { // Initiate stream processor this.selection = new SelectionStreamProcessor(this.getters); + this.coreHandlers.push(this.range); + this.handlers.push(this.range); + // registering plugins for (let Plugin of corePluginRegistry.getAll()) { this.setupCorePlugin(Plugin, workbookData); @@ -204,6 +210,8 @@ export class Model extends EventBus implements CommandDispatcher { } this.uuidGenerator.setIsFastStrategy(false); + this.handlers.push(this.history); + // starting plugins this.dispatch("START"); // Model should be the last permanent subscriber in the list since he should render @@ -227,10 +235,6 @@ export class Model extends EventBus implements CommandDispatcher { markRaw(this); } - get handlers(): CommandHandler[] { - return [this.range, ...this.corePlugins, ...this.uiPlugins, this.history]; - } - joinSession() { this.session.join(this.config.client); } @@ -251,6 +255,7 @@ export class Model extends EventBus implements CommandDispatcher { this.getters[name] = plugin[name].bind(plugin); } this.uiPlugins.push(plugin); + this.handlers.push(plugin); const layers = Plugin.layers.map((l) => [plugin, l] as [UIPlugin, LAYERS]); this.renderers.push(...layers); this.renderers.sort((p1, p2) => p1[1] - p2[1]); @@ -282,6 +287,8 @@ export class Model extends EventBus implements CommandDispatcher { } plugin.import(data); this.corePlugins.push(plugin); + this.coreHandlers.push(plugin); + this.handlers.push(plugin); } private onRemoteRevisionReceived({ commands }: { commands: CoreCommand[] }) { @@ -305,7 +312,7 @@ export class Model extends EventBus implements CommandDispatcher { return; } this.isReplayingCommand = true; - this.dispatchToHandlers([this.range, ...this.corePlugins], command); + this.dispatchToHandlers(this.coreHandlers, command); this.isReplayingCommand = false; } ), @@ -437,7 +444,7 @@ export class Model extends EventBus implements CommandDispatcher { const command: Command = { type, ...payload }; const previousStatus = this.status; this.status = Status.RunningCore; - const handlers = this.isReplayingCommand ? [this.range, ...this.corePlugins] : this.handlers; + const handlers = this.isReplayingCommand ? this.coreHandlers : this.handlers; this.dispatchToHandlers(handlers, command); this.status = previousStatus; return DispatchResult.Success; diff --git a/tests/xlsx_export.test.ts b/tests/xlsx_export.test.ts index 66a506f15d..fce1385423 100644 --- a/tests/xlsx_export.test.ts +++ b/tests/xlsx_export.test.ts @@ -18,7 +18,7 @@ import { exportPrettifiedXlsx, toRangesData } from "./test_helpers/helpers"; function getExportedExcelData(model: Model): ExcelWorkbookData { model.dispatch("EVALUATE_CELLS"); let data = createEmptyExcelWorkbookData(); - for (let handler of model.handlers) { + for (let handler of model["handlers"]) { if (handler instanceof BasePlugin) { handler.exportForExcel(data); }