Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: "20"
cache: "npm"

- run: npm ci

- run: npm test
- run: npm run build
- run: npm run benchmarks
28 changes: 14 additions & 14 deletions benchmark_results.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,29 @@ Note: This is not a fair comparison to list/text CRDTs. The executions benchmark
Send insertAfter and delete operations over a reliable link (e.g. WebSocket) - ElementId only.
Updates and saved states use JSON encoding, with optional GZIP for saved states.

- Sender time (ms): 2229
- Sender time (ms): 1440
- Avg update size (bytes): 147.3
- Receiver time (ms): 2214
- Save time (ms): 14
- Receiver time (ms): 1545
- Save time (ms): 8
- Save size (bytes): 1177551
- Load time (ms): 28
- Save time GZIP'd (ms): 83
- Save size GZIP'd (bytes): 65897
- Load time GZIP'd (ms): 53
- Load time (ms): 14
- Save time GZIP'd (ms): 43
- Save size GZIP'd (bytes): 65895
- Load time GZIP'd (ms): 27
- Mem used estimate (MB): 2.7

## Insert-After, Custom Encoding

Send insertAfter and delete operations over a reliable link (e.g. WebSocket) - ElementId only.
Updates use a custom string encoding; saved states use JSON with optional GZIP.

- Sender time (ms): 1943
- Sender time (ms): 1201
- Avg update size (bytes): 45.6
- Receiver time (ms): 3237
- Save time (ms): 13
- Receiver time (ms): 2548
- Save time (ms): 7
- Save size (bytes): 1177551
- Load time (ms): 19
- Save time GZIP'd (ms): 57
- Save size GZIP'd (bytes): 65889
- Load time GZIP'd (ms): 49
- Load time (ms): 15
- Save time GZIP'd (ms): 41
- Save size GZIP'd (bytes): 65895
- Load time GZIP'd (ms): 26
- Mem used estimate (MB): 2.7
18 changes: 5 additions & 13 deletions benchmarks/insert_after_custom.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert } from "chai";
import { v4 as uuidv4 } from "uuid";
import { ElementId, IdList, SavedIdList } from "../src";
import { ElementId, ElementIdGenerator, IdList, SavedIdList } from "../src";
import {
avg,
getMemUsed,
Expand All @@ -10,7 +10,7 @@ import {
sleep,
} from "./internal/util";

const { edits, finalText } = realTextTraceEdits();
const { edits } = realTextTraceEdits();

type Update = string;

Expand All @@ -32,6 +32,8 @@ export async function insertAfterCustom() {
return replicaId + replicaCounter++;
}

const idGenerator = new ElementIdGenerator(nextBunchId);

// Perform the whole trace, sending all updates.
const updates: string[] = [];
let startTime = process.hrtime.bigint();
Expand All @@ -40,17 +42,7 @@ export async function insertAfterCustom() {
let update: Update;
if (edit[2] !== undefined) {
const before = edit[0] === 0 ? null : sender.at(edit[0] - 1);
let id: ElementId;
// Try to extend before's bunch, so that it will be compressed.
if (
before !== null &&
sender.maxCounter(before.bunchId) === before.counter
) {
id = { bunchId: before.bunchId, counter: before.counter + 1 };
} else {
// id = { bunchId: uuidv4(), counter: 0 };
id = { bunchId: nextBunchId(), counter: 0 };
}
const id = idGenerator.generateAfter(before);

sender = sender.insertAfter(before, id);

Expand Down
18 changes: 5 additions & 13 deletions benchmarks/insert_after_json.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert } from "chai";
import { v4 as uuidv4 } from "uuid";
import { ElementId, IdList, SavedIdList } from "../src";
import { ElementId, ElementIdGenerator, IdList, SavedIdList } from "../src";
import {
avg,
getMemUsed,
Expand All @@ -10,7 +10,7 @@ import {
sleep,
} from "./internal/util";

const { edits, finalText } = realTextTraceEdits();
const { edits } = realTextTraceEdits();

type Update =
| {
Expand Down Expand Up @@ -38,6 +38,8 @@ export async function insertAfterJson() {
return replicaId + replicaCounter++;
}

const idGenerator = new ElementIdGenerator(nextBunchId);

// Perform the whole trace, sending all updates.
const updates: string[] = [];
let startTime = process.hrtime.bigint();
Expand All @@ -46,17 +48,7 @@ export async function insertAfterJson() {
let updateObj: Update;
if (edit[2] !== undefined) {
const before = edit[0] === 0 ? null : sender.at(edit[0] - 1);
let id: ElementId;
// Try to extend before's bunch, so that it will be compressed.
if (
before !== null &&
sender.maxCounter(before.bunchId) === before.counter
) {
id = { bunchId: before.bunchId, counter: before.counter + 1 };
} else {
// id = { bunchId: uuidv4(), counter: 0 };
id = { bunchId: nextBunchId(), counter: 0 };
}
const id = idGenerator.generateAfter(before);

sender = sender.insertAfter(before, id);

Expand Down