Skip to content

Commit e928efc

Browse files
optimization pass. processing 1kb json now 65k constraints
1 parent b9c3158 commit e928efc

File tree

8 files changed

+133
-133
lines changed

8 files changed

+133
-133
lines changed

out.txt

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

src/json_entry.nr

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,70 @@ impl JSONEntry {
3434
// 4 gates. oof
3535
}
3636

37+
unconstrained fn __extract_parent_index_from_field(f: Field) -> (Field, Field, Field) {
38+
let entry = JSONEntry::from_field(f);
39+
let low = entry.id;
40+
let mid = entry.parent_index;
41+
let hi = (f - low - mid * 0x10000) / 0x100000000;
42+
(low, mid, hi)
43+
}
44+
fn add_child_pointer_into_field(f: Field, child_pointer: Field) -> Field {
45+
f + child_pointer * 0x1000000000000
46+
}
47+
unconstrained fn __extract_entry_type_and_id_from_field(f: Field) -> (Field, Field, Field) {
48+
let entry = JSONEntry::from_field(f);
49+
let id = entry.id;
50+
let entry_type = entry.entry_type;
51+
let mid = (f - id - entry_type * 0x100000000000000000000000000000000000000) / 0x10000;
52+
(id, mid, entry_type)
53+
}
54+
unconstrained fn __extract_entry_type_id_and_parent_index_from_field(f: Field) -> (Field, Field, Field, Field) {
55+
let entry = JSONEntry::from_field(f);
56+
let id = entry.id;
57+
let entry_type = entry.entry_type;
58+
let parent_index = entry.parent_index;
59+
let mid = (f - id - parent_index * 0x10000 - entry_type * 0x100000000000000000000000000000000000000)
60+
/ 0x100000000;
61+
(id, parent_index, mid, entry_type)
62+
}
63+
64+
// 11.75 gates
65+
fn extract_entry_type_id_and_parent_index_from_field(f: Field) -> (Field, Field, Field) {
66+
let (id, parent_index, mid, entry_type) = JSONEntry::__extract_entry_type_id_and_parent_index_from_field(f);
67+
id.assert_max_bit_size(8); // 1.25
68+
parent_index.assert_max_bit_size(16); // 1.5
69+
entry_type.assert_max_bit_size(16); // 1.5
70+
mid.assert_max_bit_size(136); // 5.5
71+
72+
assert(
73+
id
74+
+ parent_index * 0x10000
75+
+ mid * 0x100000000
76+
+ entry_type * 0x100000000000000000000000000000000000000
77+
== f
78+
);
79+
80+
(id, parent_index, entry_type)
81+
}
82+
fn extract_entry_type_and_id_from_field(f: Field) -> (Field, Field) {
83+
let (id, mid, entry_type) = JSONEntry::__extract_entry_type_and_id_from_field(f);
84+
id.assert_max_bit_size(8); // 1.25
85+
entry_type.assert_max_bit_size(16); // 1.5
86+
mid.assert_max_bit_size(136); // 5.5
87+
88+
assert(id + mid * 0x10000 + entry_type * 0x100000000000000000000000000000000000000 == f);
89+
(id, entry_type)
90+
}
91+
fn extract_parent_index_from_field(f: Field) -> Field {
92+
let (low, parent_index, hi) = JSONEntry::__extract_parent_index_from_field(f);
93+
94+
low.assert_max_bit_size(16); // 1.75
95+
hi.assert_max_bit_size(128); // 5.5
96+
parent_index.assert_max_bit_size(16); // 1.75
97+
assert(low + parent_index * 0x10000 + hi * 0x100000000 == f); // 1
98+
// 10 gates?
99+
parent_index
100+
}
37101
fn from_field(f: Field) -> Self {
38102
let bytes: [u8; 20] = f.to_be_bytes(20).as_array(); // 10.5 gates
39103

src/keyhash.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ struct Hasher<let KeyFields: u16>
377377

378378
impl<let KeyFields: u16> Hasher<KeyFields> {
379379

380-
fn get_keyhash<let NumPackedFields: u16>(_: Self, packed_fields: [Field; NumPackedFields], body_index: u16, key_length: u16) -> Field {
380+
fn get_keyhash<let NumPackedFields: u16>(_: Self, packed_fields: [Field; NumPackedFields], body_index: Field, key_length: Field) -> Field {
381381
let key_fields: [Field; KeyFields] = slice_fields(packed_fields, body_index, key_length);
382382

383383
let hashed_full = dep::std::hash::poseidon2::Poseidon2::hash(key_fields, KeyFields as u32);

src/keymap.nr

Lines changed: 48 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use crate::json_entry::JSONEntry;
33
use crate::lt::lt_field_16_bit;
44
use crate::lt::lte_field_240_bit;
55
use crate::lt::assert_lte_240_bit;
6-
use crate::redux::BEGIN_OBJECT_TOKEN;
7-
use crate::redux::BEGIN_ARRAY_TOKEN;
6+
use crate::redux_tables::{TOKEN_BEGINS_OBJECT_OR_ARRAY, BEGIN_OBJECT_TOKEN, BEGIN_ARRAY_TOKEN};
87
use crate::keyhash::get_keyhash_chunky;
98
use crate::keyhash::get_keyhash;
109
use crate::keyhash::slice_200_bits_from_field;
@@ -15,8 +14,8 @@ use dep::noir_sort;
1514

1615
use dep::std::hash::poseidon2;
1716
struct KeyIndexData {
18-
json_index: u16,
19-
json_length: u16,
17+
json_index: Field,
18+
json_length: Field,
2019
parent_id: Field,
2120
array_index: Field,
2221
}
@@ -32,155 +31,89 @@ impl KeyIndexData {
3231
fn from_field(packed: Field) -> Self {
3332
let unpacked = packed.to_be_bytes(8);
3433
let array_index: Field = unpacked[1] as Field + unpacked[0] as Field * 0x100;
35-
let json_length: u16 = unpacked[3] as u16 + unpacked[2] as u16 * 0x100;
36-
let json_index: u16 = unpacked[5] as u16 + unpacked[4] as u16 * 0x100;
34+
let json_length: Field = unpacked[3] as Field + unpacked[2] as Field * 0x100;
35+
let json_index: Field = unpacked[5] as Field + unpacked[4] as Field * 0x100;
3736
let parent_id: Field = unpacked[7] as Field + unpacked[6] as Field * 0x100;
3837
KeyIndexData { json_index, json_length, parent_id, array_index }
3938
}
4039
}
4140

42-
fn fakehash<let N: u32>(input: [u8; N]) -> [u8; 32] {
43-
let mut r = 0;
44-
for i in 0..N {
45-
r *= 0x100;
46-
r += input[i] as Field;
47-
}
48-
r *= 0xffee134029374623874;
49-
r.to_be_bytes(32).as_array()
50-
}
51-
52-
global KeyLen = 32; // todo make param
53-
54-
// global BYTE_MULTIPLIERS: [Field; 31] = [
55-
// 1,
56-
// 0x100,
57-
// 0x10000,
58-
// 0x1000000,
59-
// 0x100000000,
60-
// 0x10000000000,
61-
// 0x1000000000000,
62-
// 0x100000000000000,
63-
// 0x10000000000000
64-
// ]
65-
6641
impl<let NumBytes: u32, let NumPackedFields: u16, let TranscriptEntries: u32> JSON<NumBytes, NumPackedFields, TranscriptEntries> {
67-
68-
// 101,105
69-
// 56,876
70-
// 44,229 cost
71-
// 700 per iteration
72-
// TODO: is poseidon2 cheap??? sounds like
7342
fn compute_keyhash_and_sort_json_entries(&mut self) {
7443
let hasher: Hasher<2> = Hasher {};
7544

7645
let mut hashlist: [Field; TranscriptEntries] = [0; TranscriptEntries];
77-
// 77321 - 73798 div 10 = 352?
78-
// should be 287 ah bad hasher
46+
7947
let two_pow_200 = 0x10000000000000000000000000000000000000000000000000000;
8048
let two_pow_216 = 0x100000000000000000000000000000000000000000000000000000000;
8149
for i in 0..TranscriptEntries {
8250
let KeyIndexData{ json_index, json_length, parent_id, array_index } = KeyIndexData::from_field(self.key_data[i]);
8351
let hash = hasher.get_keyhash(self.packed_json, json_index, json_length);
8452
hashlist[i] = hash + array_index * two_pow_200 + parent_id * two_pow_216;
85-
std::as_witness(hashlist[i]);
8653
}
87-
// ok the next pile of bullshit follows
88-
// we need to sort the JSON entries accordingf to the keyhash sort pattern
89-
// once we do that we can *finally* move on to extracting data from the json, maybe
90-
// = self.json_entries;
91-
// TODO THIS SHOULD NOT BE HERE, MESSY
92-
// let mut parent_indices: [Field; TranscriptEntries] = [0; TranscriptEntries];
93-
// for i in 0..TranscriptEntries {
94-
// let E = self.json_entries[i];
95-
// if (E.child_pointer != 0) {
96-
// let child_idx = E.child_pointer;
97-
// let parent_identity = self.json_entries[child_idx].parent_index;
98-
// parent_indices[parent_identity] = child_idx;
99-
// }
100-
// }
101-
// we want a list that maps parent ID to json entry
102-
// if I know that parent id 5 maps to idx 19 that is important
103-
// ok so now we have, for ONE child, the location of the parent
104-
//
10554

10655
let sort_result = noir_sort::sort_advanced(hashlist, lte_field_240_bit, assert_lte_240_bit);
107-
// about 2k to sort. no biggie
10856

10957
let mut sorted_entries: [Field; TranscriptEntries] = [0; TranscriptEntries];
11058

11159
for i in 0..TranscriptEntries {
11260
sorted_entries[sort_result.sort_indices[i]] = self.packed_json_entries[i];
11361
}
11462

63+
let mut ids: [Field; TranscriptEntries] = [0; TranscriptEntries];
64+
let mut parent_indices: [Field; TranscriptEntries] = [0; TranscriptEntries];
65+
let mut entry_types: [Field; TranscriptEntries] = [0; TranscriptEntries];
66+
67+
for i in 0..TranscriptEntries {
68+
// 11.75 + 3.5 = 15.25 gates per iteration
69+
let (id, parent_index, entry_type) = JSONEntry::extract_entry_type_id_and_parent_index_from_field(sorted_entries[i]);
70+
ids[i] = id;
71+
parent_indices[i] = parent_index;
72+
entry_types[i] = entry_type;
73+
}
74+
11575
let mut identity_to_json_map: [Field; TranscriptEntries] = [0; TranscriptEntries];
76+
// 6.5 gates per iteration
11677
for i in 0..TranscriptEntries {
117-
let E = JSONEntry::from_field(sorted_entries[i]);
118-
let update = ((E.entry_type == BEGIN_OBJECT_TOKEN) | (E.entry_type == BEGIN_ARRAY_TOKEN));
78+
let id = ids[i];
79+
let entry_type = entry_types[i];
80+
// 2 gates
81+
let update = TOKEN_BEGINS_OBJECT_OR_ARRAY[entry_type];
11982
// NOTE THIS RELIES ON TRANSCRIPTENTRIES ACTUALLY DESCRIBING NUMTRANSCRIPTENTRIES + 1
120-
let index = (E.id - (TranscriptEntries as Field - 1)) * update as Field
121-
+ (TranscriptEntries as Field - 1);
83+
// 1 gate
84+
let index = (id - (TranscriptEntries as Field - 1)) * update + (TranscriptEntries as Field - 1);
85+
// 3.5 gates
12286
identity_to_json_map[index] = i as Field;
123-
// if ((E.entry_type == BEGIN_OBJECT_TOKEN) | (E.entry_type == BEGIN_ARRAY_TOKEN)) {
124-
// identity_to_json_map[E.id] = i as Field;
125-
// }
12687
}
127-
// 67,802
128-
// 105,261
129-
// almost 40k?
130-
131-
// this one is expensive... if statement!
132-
/*
133-
ok wtf is going on here
134-
a json entry has a "parent_index"
135-
if the parent index changes, we are changing context
136-
if this happens then we need to find the owner of this new entry
137-
and update its "child pointer" location
138-
*/
139-
for i in 0..TranscriptEntries - 1 {
140-
// 35 gates per unpack = 105 gates per iteration
141-
// 110 * 64 = 6500 ish
142-
let parent_identity_pre = JSONEntry::from_field(sorted_entries[i]).parent_index;
143-
let parent_identity_post = JSONEntry::from_field(sorted_entries[i + 1]).parent_index;
88+
89+
// 13.5 gates per iteration
90+
let mut parent_identity_pre = parent_indices[0];
91+
for i in 1..TranscriptEntries {
92+
let parent_identity_post = parent_indices[i];
14493
// if the parent identity changes,
145-
let new_parent = parent_identity_post != parent_identity_pre;
14694

95+
// the list is sorted according to parent_ideneity,
96+
// so parent_identity increments in steps of 0 or 1
97+
// 1 gate
98+
let new_parent = parent_identity_post - parent_identity_pre;
99+
100+
// 3.5 gates
147101
let index_of_parent = identity_to_json_map[parent_identity_post];
148-
let mut updated = JSONEntry::from_field(sorted_entries[index_of_parent]);
149-
updated.child_pointer = i as Field + 1;
150-
151-
// // RELIES ON THE SMALLEST ENTRY IN THE SORTED LIST BEING EMPTY
152-
let index = ((index_of_parent - 0) * new_parent as Field) + 0;
153-
sorted_entries[index] = updated.to_field();
154-
// if (new_parent) {
155-
// let index_of_parent = identity_to_json_map[parent_identity_post];
156-
// let mut updated = JSONEntry::from_field(sorted_entries[index_of_parent]);
157-
// updated.child_pointer = i as Field + 1;
158-
// sorted_entries[index_of_parent] = updated.to_field();
159-
// }
160-
// i + 1 is the starting index of a new set of children
102+
// 1 gate + 3.5 gates
103+
let updated = JSONEntry::add_child_pointer_into_field(sorted_entries[index_of_parent], i as Field);
104+
105+
// RELIES ON THE SMALLEST ENTRY IN THE SORTED LIST BEING EMPTY
106+
// 1 gate
107+
let index = (index_of_parent * new_parent);
108+
// 3.5 gates
109+
sorted_entries[index] = updated;
110+
111+
parent_identity_pre = parent_identity_post;
161112
}
162113

163-
// phew need to consolidate.
164-
// 1: throw unused code in a scrapbook
165-
// 2: tidy data structures and remove unused fluff
166-
// 3: add metadata to JSON objeect so that we can query internal parts
167-
168-
// 4: do we need a different interface for a JSON Object vs JSON Array?
169-
// for i in 0..TranscriptEntries {
170-
// let old_child_pointer = sorted_entries[i].child_pointer;
171-
172-
// let new_child_pointer = sort_result.sort_indices[old_child_pointer];
173-
// // TODO: hacky workaround, fix
174-
// if (old_child_pointer != 0) {
175-
// sorted_entries[i].child_pointer = new_child_pointer;
176-
// }
177-
// }
178114
self.packed_json_entries = sorted_entries;
179-
180115
self.key_hashes = sort_result.sorted;
181-
// 38050
182-
// 60707 <-- cost after hashing keuys
183-
// 77244 <-- cost after sorting and updating children
184116
}
185117
}
186118

119+
// 68002

src/main.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ fn mainslice(foo: [Field; 100], bar: [u8; 1000], num_bytes: [u16; 100], start_by
304304

305305
// CURRENT VALUE OF SLICE_FIELDS IS 182 GATES 26 AUGUST
306306
for i in 0..89 {
307-
let x: [Field; 2] = slice_fields(foo, start_bytes[i], num_bytes[i]);
307+
let x: [Field; 2] = slice_fields(foo, start_bytes[i] as Field, num_bytes[i] as Field);
308308
// let x = slice_field(foo[i], start_bytes[i] as Field);
309309
println(f"{x}");
310310
// assert(x[0] == x[1]);

src/redux_tables.nr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ global OBJECT_LAYER = 0;
2121
global ARRAY_LAYER = 1;
2222
global SINGLE_VALUE_LAYER = 2;
2323

24+
global TOKEN_BEGINS_OBJECT_OR_ARRAY : [Field; 11] = [
25+
0,1,0,1,0,0,0,0,0,0,0
26+
];
2427
global TOKEN_IS_NUMERIC_OR_LITERAL: [Field; 11] = [
2528
0,0,0,0,0,0,0,0,1,1,0
2629
];

0 commit comments

Comments
 (0)