Skip to content

Commit

Permalink
Merge pull request #369 from streamich/json-crdt-refactor
Browse files Browse the repository at this point in the history
JSON CRDT refactors
streamich authored Nov 5, 2023
2 parents 6a0245a + 35b5ede commit d070db0
Showing 80 changed files with 1,016 additions and 1,101 deletions.
4 changes: 2 additions & 2 deletions src/json-crdt-patch/codec/binary/Decoder.ts
Original file line number Diff line number Diff line change
@@ -45,8 +45,8 @@ export class Decoder extends CborDecoder<CrdtReader> {

protected decodeId(): ITimestampStruct {
const reader = this.reader;
const [isRelativeTime, x] = reader.b1vu56();
return isRelativeTime ? new Timestamp(this.patchSid!, x) : new Timestamp(x, reader.vu57());
const [isSessionDifferent, x] = reader.b1vu56();
return isSessionDifferent ? new Timestamp(reader.vu57(), x) : new Timestamp(this.patchSid!, x);
}

protected decodeTss(): ITimespanStruct {
6 changes: 3 additions & 3 deletions src/json-crdt-patch/codec/binary/Encoder.ts
Original file line number Diff line number Diff line change
@@ -52,10 +52,10 @@ export class Encoder extends CborEncoder<CrdtWriter> {
const time = id.time;
const writer = this.writer;
if (sessionId === this.patchSid) {
writer.b1vu56(1, time);
writer.b1vu56(0, time);
} else {
writer.b1vu56(0, sessionId);
writer.vu57(time);
writer.b1vu56(1, time);
writer.vu57(sessionId);
}
}

24 changes: 12 additions & 12 deletions src/json-crdt/README.md
Original file line number Diff line number Diff line change
@@ -13,35 +13,35 @@ and random positions of a 10,000-character newly created string.

```
node benchmarks/json-crdt/strings-short.js
json-crdt StringRga type x 228,880 ops/sec ±0.07% (99 runs sampled)
json-crdt StrNode type x 228,880 ops/sec ±0.07% (99 runs sampled)
json-crdt x 187,700 ops/sec ±0.17% (98 runs sampled)
Y.js x 18,288 ops/sec ±0.19% (100 runs sampled)
Automerge x 23.64 ops/sec ±1.54% (44 runs sampled)
Fastest is json-crdt StringRga type
Fastest is json-crdt StrNode type
```

Inserting one 10-char string and deleting one 10-char string range at random
positions from a 10,000-char string, which was beforehand already edited 5,000 times.

```
node benchmarks/json-crdt/strings-long.js
json-crdt StringRga type x 54,250 ops/sec ±20.68% (23 runs sampled)
json-crdt StrNode type x 54,250 ops/sec ±20.68% (23 runs sampled)
json-crdt x 59,628 ops/sec ±14.39% (32 runs sampled)
Y.js x 16,358 ops/sec ±10.79% (45 runs sampled)
Automerge x 1,777 ops/sec ±5.97% (76 runs sampled)
Fastest is json-crdt,json-crdt StringRga type
Fastest is json-crdt,json-crdt StrNode type
```

Editing a string at 10 repeating positions with +/- 6 characters random variance
from those positions.

```
node benchmarks/json-crdt/strings-repeat-insert-positions.js
json-crdt StringRga type x 8,313 ops/sec ±1.52% (93 runs sampled)
json-crdt StrNode type x 8,313 ops/sec ±1.52% (93 runs sampled)
json-crdt x 6,292 ops/sec ±2.22% (77 runs sampled)
Y.js x 3,104 ops/sec ±1.40% (78 runs sampled)
Automerge x 246 ops/sec ±1.04% (85 runs sampled)
Fastest is json-crdt StringRga type
Fastest is json-crdt StrNode type
```

### Real-world comparisons
@@ -114,22 +114,22 @@ String length: 104852 , Chunk count: 12487
JSON CRDT: 134.596ms
String length: 104852 , Chunk count: 12487
---------------------------------------------
JSON CRDT StringRga: 85.254ms
JSON CRDT StrNode: 85.254ms
String length: 104852 , Chunk count: 12387
---------------------------------------------
JSON CRDT StringRga: 86.487ms
JSON CRDT StrNode: 86.487ms
String length: 104852 , Chunk count: 12387
---------------------------------------------
JSON CRDT StringRga: 73.346ms
JSON CRDT StrNode: 73.346ms
String length: 104852 , Chunk count: 12387
---------------------------------------------
JSON CRDT StringRga: 74.109ms
JSON CRDT StrNode: 74.109ms
String length: 104852 , Chunk count: 12387
---------------------------------------------
JSON CRDT StringRga: 73.593ms
JSON CRDT StrNode: 73.593ms
String length: 104852 , Chunk count: 12387
---------------------------------------------
JSON CRDT StringRga: 74.138ms
JSON CRDT StrNode: 74.138ms
String length: 104852 , Chunk count: 12387
---------------------------------------------
diamond-types-node: 58.114ms
2 changes: 1 addition & 1 deletion src/json-crdt/__bench__/bench.traces.crdt-libs.ts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ runTraceMatrix({
'automerge-paper',
],
editors: [
'StringRga (json-joy)',
'StrNode (json-joy)',
'json-joy',
// 'Y.js',
// 'Y.rs',
2 changes: 1 addition & 1 deletion src/json-crdt/__bench__/bench.traces.non-crdt-libs.ts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ runTraceMatrix({
'automerge-paper',
],
editors: [
'StringRga (json-joy)',
'StrNode (json-joy)',
'diamond-types-node',
'rope.js',
'V8 strings',
22 changes: 11 additions & 11 deletions src/json-crdt/__bench__/profiler/automerge-paper.js
Original file line number Diff line number Diff line change
@@ -4,18 +4,18 @@
*/

const {traces} = require('../../data/editing-traces');
const {StringRga} = require('../../../es2020/json-crdt/types/rga-string/StringRga');
const {StrNode} = require('../../../es2020/json-crdt/types/str/StrNode');
const {ts} = require('../../../es2020/json-crdt-patch/clock/logical');

const patches = traces.get('automerge-paper').txns.map((txn) => txn.patches[0]);
const length = patches.length;
console.log('Document operations:', length, patches);

const runStringRga = () => {
const runStrNode = () => {
console.log('---------------------------------------------');
console.time('JSON CRDT StringRga');
console.time('JSON CRDT StrNode');
let time = 0;
const rga = new StringRga(ts(1, time++));
const rga = new StrNode(ts(1, time++));
for (let i = 0; i < length; i++) {
const [pos, del, c] = patches[i];
if (del) {
@@ -27,14 +27,14 @@ const runStringRga = () => {
}
rga.view();
// console.log(rga.view());
console.timeEnd('JSON CRDT StringRga');
console.timeEnd('JSON CRDT StrNode');
console.log('String length:', rga.length(), ', Chunk count:', rga.size());
// console.log(rga.toString());
};

runStringRga();
runStringRga();
runStringRga();
runStringRga();
runStringRga();
runStringRga();
runStrNode();
runStrNode();
runStrNode();
runStrNode();
runStrNode();
runStrNode();
12 changes: 6 additions & 6 deletions src/json-crdt/__bench__/strings-long.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Benchmark = require('benchmark');
const {Model} = require('../../es2020/json-crdt');
const {StringRga} = require('../../es2020/json-crdt/types/rga-string/StringRga');
const {StrNode} = require('../../es2020/json-crdt/types/str/StrNode');
const Y = require('yjs');
const Automerge = require('automerge');
const {randomU32} = require('hyperdyperid/lib/randomU32');
@@ -14,10 +14,10 @@ const len2 = str2.length;

const operations = 1;

const type = new StringRga(ts(1, 1));
const type = new StrNode(ts(1, 1));
type.ins(ts(1, 1), ts(1, 2), str1);
let time = str1.length + 100;
const editStringRga = () => {
const editStrNode = () => {
for (let i = 0; i < operations; i++) {
const pos1 = randomU32(0, len1 - len2);
const pos2 = randomU32(0, len1 - len2);
@@ -70,7 +70,7 @@ const editAutomerge = () => {
};

for (let i = 0; i < 5000; i++) {
editStringRga();
editStrNode();
editJsonCrdt();
editYjs();
editAutomerge();
@@ -79,8 +79,8 @@ for (let i = 0; i < 5000; i++) {
const suite = new Benchmark.Suite();

suite
.add('json-crdt StringRga type', function () {
editStringRga();
.add('json-crdt StrNode type', function () {
editStrNode();
})
.add('json-crdt', function () {
editJsonCrdt();
12 changes: 6 additions & 6 deletions src/json-crdt/__bench__/strings-repeat-insert-positions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Benchmark = require('benchmark');
const {Model} = require('../../es2020/json-crdt');
const {StringRga} = require('../../es2020/json-crdt/types/rga-string/StringRga');
const {StrNode} = require('../../es2020/json-crdt/types/str/StrNode');
const Y = require('yjs');
const Automerge = require('automerge');
const {randomU32} = require('hyperdyperid/lib/randomU32');
@@ -15,10 +15,10 @@ const str2 = '1234567890';
const len1 = str1.length;
const len2 = str2.length;

const type = new StringRga(ts(1, 1));
const type = new StrNode(ts(1, 1));
type.ins(ts(1, 1), ts(1, 2), str1);
let time = str1.length + 100;
const editStringRga = () => {
const editStrNode = () => {
for (let i = 0; i < insertPositions.length; i++) {
const pos1 = insertPositions[i] + randomU32(0, variance);
const pos2 = randomU32(0, len1 - len2);
@@ -71,7 +71,7 @@ const editAutomerge = () => {
};

for (let i = 0; i < 1000; i++) {
editStringRga();
editStrNode();
editJsonCrdt();
editYjs();
editAutomerge();
@@ -80,8 +80,8 @@ for (let i = 0; i < 1000; i++) {
const suite = new Benchmark.Suite();

suite
.add('json-crdt StringRga type', function () {
editStringRga();
.add('json-crdt StrNode type', function () {
editStrNode();
})
.add('json-crdt', function () {
editJsonCrdt();
12 changes: 6 additions & 6 deletions src/json-crdt/__bench__/strings-short.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Benchmark = require('benchmark');
const {Model} = require('../../es2020/json-crdt');
const {StringRga} = require('../../es2020/json-crdt/types/rga-string/StringRga');
const {StrNode} = require('../../es2020/json-crdt/types/str/StrNode');
const Y = require('yjs');
const Automerge = require('automerge');
const {randomU32} = require('hyperdyperid/lib/randomU32');
@@ -14,8 +14,8 @@ const str2 = '1234567890';
const len1 = str1.length;
const len2 = str2.length;

const editStringRga = () => {
const type = new StringRga(ts(1, 1));
const editStrNode = () => {
const type = new StrNode(ts(1, 1));
type.ins(ts(1, 1), ts(1, 2), str1);
let time = str1.length + 100;
for (let i = 0; i < 10; i++) {
@@ -71,7 +71,7 @@ const editAutomerge = () => {
};

for (let i = 0; i < 10; i++) {
editStringRga();
editStrNode();
editJsonCrdt();
editYjs();
editAutomerge();
@@ -86,8 +86,8 @@ suite
// arr.splice(pos1, {i: null, r: null, p: null, content: {}, deleted: false, id: [1 ,2]}, {i: null, r: null, p: null, content: {}, deleted: false, id: [1 ,2]});
// arr.splice(pos2, 2);
// })
.add('json-crdt StringRga type', function () {
editStringRga();
.add('json-crdt StrNode type', function () {
editStrNode();
})
.add('json-crdt', function () {
editJsonCrdt();
8 changes: 4 additions & 4 deletions src/json-crdt/__bench__/util/editors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Timestamp} from '../../../json-crdt-patch/clock';
import {StringRga} from '../../types/rga-string/StringRga';
import {StrNode} from '../../nodes';
import {Model} from '../../model';
import {Doc} from 'diamond-types-node';
import * as Y from 'yjs';
@@ -11,11 +11,11 @@ const AutomergeUnstable = require('@automerge/automerge/dist/cjs/unstable');
const Rope = require('rope.js');

export const editors = {
'StringRga (json-joy)': {
name: 'StringRga (json-joy)',
'StrNode (json-joy)': {
name: 'StrNode (json-joy)',
factory: () => {
let time = 0;
const rga = new StringRga(new Timestamp(1, time++));
const rga = new StrNode(new Timestamp(1, time++));
return {
ins: (pos: number, insert: string) => {
rga.insAt(pos, new Timestamp(1, time), insert);
Original file line number Diff line number Diff line change
@@ -3,17 +3,17 @@
/**
* Run this demo with:
*
* npx nodemon -q -x ts-node src/json-crdt/__demos__/StringRga-hello-world.ts
* npx nodemon -q -x ts-node src/json-crdt/__demos__/StrNode-hello-world.ts
*/

import {ts} from '../../json-crdt-patch/clock';
import {StringRga} from '../types/rga-string/StringRga';
import {StrNode} from '../nodes';

const sid = 123; // Site ID
let time = 0; // "time"

const id = ts(sid, time++);
const str = new StringRga(id);
const str = new StrNode(id);

console.log(str.view());
console.log(str + '');
Original file line number Diff line number Diff line change
@@ -3,11 +3,11 @@
/**
* Run this demo with:
*
* npx nodemon -q -x ts-node src/json-crdt/__demos__/StringRga-json-joy.ts
* npx nodemon -q -x ts-node src/json-crdt/__demos__/StrNode-json-joy.ts
*/

import {ts} from '../../json-crdt-patch/clock';
import {StringRga} from '../types/rga-string/StringRga';
import {StrNode} from '../nodes';

const user1 = 123;
const user2 = 345;
@@ -16,31 +16,31 @@ const user3 = 789;
console.log();

// User 1 creates a new string
const str1 = new StringRga(ts(user1, 0));
const str1 = new StrNode(ts(user1, 0));
str1.insAt(0, ts(user1, 1), 'js');

// User 2 and 3 insert their changes at the same time "ts(user1, 2)"
str1.ins(ts(user1, 2), ts(user2, 3), ' joy');
str1.ins(ts(user1, 2), ts(user3, 3), 'on');

console.log(str1 + '');
// StringRga 123.0 { "json joy" }
// └─ StringChunk 789.3!2 len:8 { "on" }
// ← StringChunk 123.1!2 len:2 { "js" }
// → StringChunk 345.3!4 len:4 { " joy" }
// StrNode 123.0 { "json joy" }
// └─ StrChunk 789.3!2 len:8 { "on" }
// ← StrChunk 123.1!2 len:2 { "js" }
// → StrChunk 345.3!4 len:4 { " joy" }

console.log();

// User 1 creates a new string "js"
const str2 = new StringRga(ts(user1, 0));
const str2 = new StrNode(ts(user1, 0));
str2.insAt(0, ts(user1, 1), 'js');

// User 2 and 3 insert their changes at the same time "ts(user1, 2)"
str2.ins(ts(user1, 2), ts(user3, 3), 'on');
str2.ins(ts(user1, 2), ts(user2, 3), ' joy');

console.log(str2 + '');
// StringRga 123.0 { "json joy" }
// └─ StringChunk 345.3!4 len:8 { " joy" }
// ← StringChunk 789.3!2 len:4 { "on" }
// ← StringChunk 123.1!2 len:2 { "js" }
// StrNode 123.0 { "json joy" }
// └─ StrChunk 345.3!4 len:8 { " joy" }
// ← StrChunk 789.3!2 len:4 { "on" }
// ← StrChunk 123.1!2 len:2 { "js" }
Loading

0 comments on commit d070db0

Please sign in to comment.