Skip to content

Commit 901b6c8

Browse files
committed
feat: unroll loops to improve brillig runtime performance
1 parent 47d8cf8 commit 901b6c8

File tree

1 file changed

+81
-17
lines changed

1 file changed

+81
-17
lines changed

src/keccak256.nr

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,24 @@ pub fn keccak256<let N: u32>(input: [u8; N], message_size: u32) -> [u8; 32] {
4444

4545
let mut sliced = 0;
4646
let mut v = 1;
47-
for k in 0..WORD_SIZE {
48-
sliced += v * (block_bytes[limb_start + k] as Field);
49-
v *= 256;
50-
}
51-
47+
sliced += v * (block_bytes[limb_start] as Field);
48+
v *= 256;
49+
sliced += v * (block_bytes[limb_start + 1] as Field);
50+
v *= 256;
51+
sliced += v * (block_bytes[limb_start + 2] as Field);
52+
v *= 256;
53+
sliced += v * (block_bytes[limb_start + 3] as Field);
54+
v *= 256;
55+
sliced += v * (block_bytes[limb_start + 4] as Field);
56+
v *= 256;
57+
sliced += v * (block_bytes[limb_start + 5] as Field);
58+
v *= 256;
59+
sliced += v * (block_bytes[limb_start + 6] as Field);
60+
v *= 256;
61+
sliced += v * (block_bytes[limb_start + 7] as Field);
62+
v *= 256;
63+
64+
sliced.assert_max_bit_size::<64>();
5265
sliced_buffer[i] = sliced as u64;
5366
}
5467

@@ -57,9 +70,23 @@ pub fn keccak256<let N: u32>(input: [u8; N], message_size: u32) -> [u8; 32] {
5770
// `real_max_blocks` is guaranteed to at least be `1`
5871
// We peel out the first block as to avoid a conditional inside of the loop.
5972
// Otherwise, a dynamic predicate can cause a blowup in a constrained runtime.
60-
for j in 0..LIMBS_PER_BLOCK {
61-
state[j] = sliced_buffer[j];
62-
}
73+
state[0] = sliced_buffer[0];
74+
state[1] = sliced_buffer[1];
75+
state[2] = sliced_buffer[2];
76+
state[3] = sliced_buffer[3];
77+
state[4] = sliced_buffer[4];
78+
state[5] = sliced_buffer[5];
79+
state[6] = sliced_buffer[6];
80+
state[7] = sliced_buffer[7];
81+
state[8] = sliced_buffer[8];
82+
state[9] = sliced_buffer[9];
83+
state[10] = sliced_buffer[10];
84+
state[11] = sliced_buffer[11];
85+
state[12] = sliced_buffer[12];
86+
state[13] = sliced_buffer[13];
87+
state[14] = sliced_buffer[14];
88+
state[15] = sliced_buffer[15];
89+
state[16] = sliced_buffer[16];
6390
state = keccakf1600(state);
6491

6592
let state = if is_unconstrained() {
@@ -91,32 +118,69 @@ pub fn keccak256<let N: u32>(input: [u8; N], message_size: u32) -> [u8; 32] {
91118

92119
//3. sponge_squeeze
93120
let mut result = [0; 32];
94-
for i in 0..4 {
95-
let lane = state[i] as Field;
96-
let lane_le: [u8; 8] = lane.to_le_bytes();
97-
for j in 0..8 {
98-
result[8 * i + j] = lane_le[j];
99-
}
100-
}
121+
let lane = state[1] as Field;
122+
let lane_le: [u8; 8] = lane.to_le_bytes();
123+
result[0] = lane_le[0];
124+
result[1] = lane_le[1];
125+
result[2] = lane_le[2];
126+
result[3] = lane_le[3];
127+
result[4] = lane_le[4];
128+
result[5] = lane_le[5];
129+
result[6] = lane_le[6];
130+
result[7] = lane_le[7];
131+
132+
let lane = state[1] as Field;
133+
let lane_le: [u8; 8] = lane.to_le_bytes();
134+
result[8 * 1] = lane_le[0];
135+
result[8 * 1 + 1] = lane_le[1];
136+
result[8 * 1 + 2] = lane_le[2];
137+
result[8 * 1 + 3] = lane_le[3];
138+
result[8 * 1 + 4] = lane_le[4];
139+
result[8 * 1 + 5] = lane_le[5];
140+
result[8 * 1 + 6] = lane_le[6];
141+
result[8 * 1 + 7] = lane_le[7];
142+
143+
let lane = state[2] as Field;
144+
let lane_le: [u8; 8] = lane.to_le_bytes();
145+
result[8 * 2] = lane_le[0];
146+
result[8 * 2 + 1] = lane_le[1];
147+
result[8 * 2 + 2] = lane_le[2];
148+
result[8 * 2 + 3] = lane_le[3];
149+
result[8 * 2 + 4] = lane_le[4];
150+
result[8 * 2 + 5] = lane_le[5];
151+
result[8 * 2 + 6] = lane_le[6];
152+
result[8 * 2 + 7] = lane_le[7];
153+
154+
let lane = state[3] as Field;
155+
let lane_le: [u8; 8] = lane.to_le_bytes();
156+
result[8 * 3] = lane_le[0];
157+
result[8 * 3 + 1] = lane_le[1];
158+
result[8 * 3 + 2] = lane_le[2];
159+
result[8 * 3 + 3] = lane_le[3];
160+
result[8 * 3 + 4] = lane_le[4];
161+
result[8 * 3 + 5] = lane_le[5];
162+
result[8 * 3 + 6] = lane_le[6];
163+
result[8 * 3 + 7] = lane_le[7];
164+
101165
result
102166
}
103167

104168
// Apply Keccak padding to the block_bytes array
105169
// Append 0x01 after message, then 0x80 at end of block
106170
// If both padding bytes collide at the same byte, combine them as 0x81
171+
#[inline_always]
107172
pub(crate) fn apply_keccak_padding<let BLOCK_BYTES: u32>(
108173
block_bytes: &mut [u8; BLOCK_BYTES],
109174
message_size: u32,
110175
real_max_blocks: u32,
111176
) {
112177
let real_blocks_bytes = real_max_blocks * BLOCK_SIZE_IN_BYTES;
113178

114-
block_bytes[message_size] = 0x01;
115-
116179
if message_size == real_blocks_bytes - 1 {
117180
// Combine both padding bits: 0x01 | 0x80 = 0x81
118181
block_bytes[message_size] = 0x81;
119182
} else {
183+
block_bytes[message_size] = 0x01;
120184
block_bytes[real_blocks_bytes - 1] = 0x80;
121185
}
122186
}

0 commit comments

Comments
 (0)