Skip to content

Commit d43a3bf

Browse files
committed
[PERF] history: reduce history memory usage
This commit reduces the amount of memory used to keep track of history changes. Currently, for each change, we keep the full path of keys from the root plugin to the deepest data structure which is actually updated. `[BordersPlugin, 'borders', 'sheet1', 0, 1, 'horizontal']` This is useless, it is enough to keep a reference to the updated object and the updated key. Note that `after` was never used. On the large formula data set, with 20k rows, here are the amounts of memory used (after GC has run) adding a column left of A: - before 613Mb - after 487Mb adding borders on all cells, all sides: - before 451Mb - after 384Mb Undoing a revision is also faster since we no longer needs to iterate over all keys of the path. Adding a column before A, then undo and reload. Replaying both commands goes from `~1700ms` to `~1200ms` closes #4230 Task: 3940604 Signed-off-by: Pierre Rousseau (pro) <[email protected]>
1 parent c969fba commit d43a3bf

File tree

3 files changed

+15
-21
lines changed

3 files changed

+15
-21
lines changed

src/history/factory.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { transformAll } from "../collaborative/ot/ot";
22
import { Revision } from "../collaborative/revisions";
33
import { inverseCommand } from "../helpers/inverse_commands";
4-
import { createEmptyStructure } from "../helpers/state_manager_helpers";
54
import { StateObserver } from "../state_observer";
65
import { CoreCommand, HistoryChange, UID } from "../types";
76
import { SelectiveHistory } from "./selective_history";
@@ -56,28 +55,21 @@ function revertChanges(revisions: readonly Revision[]) {
5655
for (const revision of revisions.slice().reverse()) {
5756
for (let i = revision.changes.length - 1; i >= 0; i--) {
5857
const change = revision.changes[i];
59-
applyChange(change, "before");
58+
applyChange(change);
6059
}
6160
}
6261
}
6362

6463
/**
6564
* Apply the changes of the given HistoryChange to the state
6665
*/
67-
function applyChange(change: HistoryChange, target: "before" | "after") {
68-
let val = change.path[0];
69-
const key = change.path.at(-1);
70-
for (let pathIndex = 1; pathIndex < change.path.slice(0, -1).length; pathIndex++) {
71-
const p = change.path[pathIndex];
72-
if (val[p] === undefined) {
73-
const nextPath = change.path[pathIndex + 1];
74-
val[p] = createEmptyStructure(nextPath);
75-
}
76-
val = val[p];
77-
}
78-
if (change[target] === undefined) {
79-
delete val[key];
66+
function applyChange(change: HistoryChange) {
67+
const target = change.target;
68+
const key = change.key;
69+
const before = change.before;
70+
if (before === undefined) {
71+
delete target[key];
8072
} else {
81-
val[key] = change[target];
73+
target[key] = before;
8274
}
8375
}

src/state_observer.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { createEmptyStructure } from "./helpers/state_manager_helpers";
22
import { CoreCommand, HistoryChange } from "./types";
33

4+
type HistoryPath = [any, ...(number | string)[]];
5+
46
export class StateObserver {
57
private changes: HistoryChange[] = [];
68
private commands: CoreCommand[] = [];
@@ -20,7 +22,7 @@ export class StateObserver {
2022
this.commands.push(command);
2123
}
2224

23-
addChange(...args: [...HistoryChange["path"], any]) {
25+
addChange(...args: [...HistoryPath, any]) {
2426
const val: any = args.pop();
2527
const root = args[0];
2628
let value = root as any;
@@ -38,9 +40,9 @@ export class StateObserver {
3840
return;
3941
}
4042
this.changes.push({
41-
path: args,
43+
key,
44+
target: value,
4245
before: value[key],
43-
after: val,
4446
});
4547
if (val === undefined) {
4648
delete value[key];

src/types/history.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ export interface CreateRevisionOptions {
99
}
1010

1111
export interface HistoryChange {
12-
path: [any, ...(number | string)[]];
12+
key: string;
13+
target: any;
1314
before: any;
14-
after: any;
1515
}
1616

1717
export interface WorkbookHistory<Plugin> {

0 commit comments

Comments
 (0)