Monorepo for Tangled
tangled.org
1use std::hint::black_box;
2
3use bobbin_types::edges::Record;
4use criterion::{Criterion, criterion_group, criterion_main};
5use jacquard_common::DefaultStr;
6use jacquard_common::types::nsid::Nsid;
7use serde::Deserialize;
8use serde_json::value::RawValue;
9
10#[derive(Deserialize)]
11struct ValueFrame {
12 record: ValueRecordFrame,
13}
14
15#[derive(Deserialize)]
16struct ValueRecordFrame {
17 collection: Nsid<DefaultStr>,
18 #[serde(default)]
19 record: Option<serde_json::Value>,
20}
21
22#[derive(Deserialize)]
23struct BorrowedRawFrame<'a> {
24 #[serde(borrow)]
25 record: BorrowedRawRecordFrame<'a>,
26}
27
28#[derive(Deserialize)]
29struct BorrowedRawRecordFrame<'a> {
30 collection: Nsid<DefaultStr>,
31 #[serde(borrow, default)]
32 record: Option<&'a RawValue>,
33}
34
35#[derive(Deserialize)]
36struct OwnedRawFrame {
37 record: OwnedRawRecordFrame,
38}
39
40#[derive(Deserialize)]
41struct OwnedRawRecordFrame {
42 collection: Nsid<DefaultStr>,
43 #[serde(default)]
44 record: Option<Box<RawValue>>,
45}
46
47fn current_path(text: &str) -> Record {
48 let frame: ValueFrame = serde_json::from_str(text).expect("frame");
49 let value = frame.record.record.expect("upsert body");
50 let bytes = serde_json::to_vec(&value).expect("re-serialize Value");
51 Record::from_json_bytes(&frame.record.collection, &bytes).expect("decode record")
52}
53
54fn raw_value_borrowed(text: &str) -> Record {
55 let frame: BorrowedRawFrame<'_> = serde_json::from_str(text).expect("frame");
56 let raw = frame.record.record.expect("upsert body");
57 Record::from_json_bytes(&frame.record.collection, raw.get().as_bytes()).expect("decode record")
58}
59
60fn raw_value_owned(text: &str) -> Record {
61 let frame: OwnedRawFrame = serde_json::from_str(text).expect("frame");
62 let raw = frame.record.record.expect("upsert body");
63 Record::from_json_bytes(&frame.record.collection, raw.get().as_bytes()).expect("decode record")
64}
65
66fn floor_record_only(collection: &Nsid<DefaultStr>, record_bytes: &[u8]) -> Record {
67 Record::from_json_bytes(collection, record_bytes).expect("decode record")
68}
69
70fn star_text() -> &'static str {
71 r#"{
72 "id": 1,
73 "type": "record",
74 "record": {
75 "live": false,
76 "did": "did:plc:olaren",
77 "rev": "3lq2zk5wqsh2k",
78 "collection": "sh.tangled.feed.star",
79 "rkey": "abcabcabcabcz",
80 "action": "create",
81 "record": {
82 "$type": "sh.tangled.feed.star",
83 "createdAt": "2026-05-01T00:00:00Z",
84 "subject": {
85 "$type": "sh.tangled.feed.star#repo",
86 "did": "did:plc:limpet"
87 }
88 }
89 }
90 }"#
91}
92
93fn repo_text() -> &'static str {
94 r#"{
95 "id": 2,
96 "type": "record",
97 "record": {
98 "live": false,
99 "did": "did:plc:nel",
100 "rev": "3lq2zk5wqsh2l",
101 "collection": "sh.tangled.repo",
102 "rkey": "3lq2zk5wq0001",
103 "action": "create",
104 "record": {
105 "$type": "sh.tangled.repo",
106 "createdAt": "2026-05-01T00:00:00Z",
107 "knot": "knot.witchcraft.systems",
108 "name": "limpet",
109 "description": "demonstration repository for the bench corpus",
110 "owner": "did:plc:nel",
111 "repoDid": "did:plc:limpet",
112 "labels": [
113 "at://did:plc:periwinkle/sh.tangled.label.definition/3lq2zk5wq0010",
114 "at://did:plc:periwinkle/sh.tangled.label.definition/3lq2zk5wq0011"
115 ]
116 }
117 }
118 }"#
119}
120
121fn issue_text() -> &'static str {
122 r#"{
123 "id": 3,
124 "type": "record",
125 "record": {
126 "live": false,
127 "did": "did:plc:teq",
128 "rev": "3lq2zk5wqsh2m",
129 "collection": "sh.tangled.repo.issue",
130 "rkey": "3lq2zk5wq0100",
131 "action": "create",
132 "record": {
133 "$type": "sh.tangled.repo.issue",
134 "createdAt": "2026-05-01T00:00:00Z",
135 "title": "ingest: single-pass JSON decode follow-up corpus entry",
136 "body": "Long body to give the bench a realistic decode cost. Repeats: blahhhhh meow meow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
137 "repo": "did:plc:limpet",
138 "mentions": [
139 "did:plc:nel",
140 "did:plc:olaren",
141 "did:plc:bailey"
142 ],
143 "references": [
144 "at://did:plc:limpet/sh.tangled.repo.issue/3lq2zk5wq0099",
145 "at://did:plc:limpet/sh.tangled.repo.pull/3lq2zk5wq0098"
146 ]
147 }
148 }
149 }"#
150}
151
152fn pull_comment_text() -> &'static str {
153 r#"{
154 "id": 4,
155 "type": "record",
156 "record": {
157 "live": false,
158 "did": "did:plc:lyna",
159 "rev": "3lq2zk5wqsh2n",
160 "collection": "sh.tangled.feed.comment",
161 "rkey": "3lq2zk5wq0200",
162 "action": "create",
163 "record": {
164 "$type": "sh.tangled.feed.comment",
165 "createdAt": "2026-05-01T00:00:00Z",
166 "body": {
167 "$type": "sh.tangled.markup.markdown",
168 "text": "lgtm i thinks!!!! but please verify the cursor invariant under buffered(N) before landing. :3"
169 },
170 "subject": {
171 "uri": "at://did:plc:limpet/sh.tangled.repo.pull/3lq2zk5wq0098",
172 "cid": "bafkqaaa"
173 },
174 "pullRoundIdx": 0
175 }
176 }
177 }"#
178}
179
180fn follow_text() -> &'static str {
181 r#"{
182 "id": 5,
183 "type": "record",
184 "record": {
185 "live": false,
186 "did": "did:plc:bailey",
187 "rev": "3lq2zk5wqsh2o",
188 "collection": "sh.tangled.graph.follow",
189 "rkey": "3lq2zk5wq0300",
190 "action": "create",
191 "record": {
192 "$type": "sh.tangled.graph.follow",
193 "createdAt": "2026-05-01T00:00:00Z",
194 "subject": "did:plc:nel"
195 }
196 }
197 }"#
198}
199
200fn extract_record_slice(text: &str) -> (Nsid<DefaultStr>, Vec<u8>) {
201 let frame: ValueFrame = serde_json::from_str(text).expect("frame");
202 let value = frame.record.record.expect("upsert body");
203 let bytes = serde_json::to_vec(&value).expect("re-serialize Value");
204 (frame.record.collection, bytes)
205}
206
207fn bench_decode(c: &mut Criterion) {
208 let corpus: &[(&str, &str)] = &[
209 ("star", star_text()),
210 ("repo", repo_text()),
211 ("issue", issue_text()),
212 ("pull_comment", pull_comment_text()),
213 ("follow", follow_text()),
214 ];
215 for (name, text) in corpus {
216 let mut group = c.benchmark_group(format!("decode/{name}"));
217 let (collection, record_bytes) = extract_record_slice(text);
218
219 group.bench_function("current_path", |b| {
220 b.iter(|| {
221 let r = current_path(black_box(text));
222 black_box(r);
223 });
224 });
225
226 group.bench_function("raw_value_borrowed", |b| {
227 b.iter(|| {
228 let r = raw_value_borrowed(black_box(text));
229 black_box(r);
230 });
231 });
232
233 group.bench_function("raw_value_owned", |b| {
234 b.iter(|| {
235 let r = raw_value_owned(black_box(text));
236 black_box(r);
237 });
238 });
239
240 group.bench_function("floor_record_only", |b| {
241 let bytes = record_bytes.as_slice();
242 b.iter(|| {
243 let r = floor_record_only(black_box(&collection), black_box(bytes));
244 black_box(r);
245 });
246 });
247
248 group.finish();
249 }
250}
251
252criterion_group!(benches, bench_decode);
253criterion_main!(benches);