Skip to content

Commit b0d9500

Browse files
committed
benchmarks: rewrite the parsing benchmarks
This commit doesn't redo the compiler benchmarks because they are not essential to the changes I am planning to make.
1 parent 532d0fa commit b0d9500

File tree

2 files changed

+281
-43
lines changed

2 files changed

+281
-43
lines changed

src/benchmarks.rs

Lines changed: 281 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,66 +7,305 @@
77
//! nightly compiler to run. See the README for exact instructions.
88
//!
99
10+
use core::str::FromStr;
11+
12+
use bitcoin::secp256k1::{Secp256k1, SecretKey};
1013
use test::{black_box, Bencher};
1114

15+
use crate::descriptor::{SinglePub, SinglePubKey};
1216
use crate::expression::Tree;
13-
use crate::miniscript::context;
14-
use crate::{Miniscript, ExtParams};
17+
use crate::{Descriptor, DescriptorPublicKey};
1518

16-
#[bench]
17-
pub fn parse_segwit0(bh: &mut Bencher) {
18-
bh.iter(|| {
19-
let tree = Miniscript::<String, context::Segwitv0>::from_str_ext(
20-
"and_v(v:pk(E),thresh(2,j:and_v(v:sha256(H),t:or_i(v:sha256(H),v:pkh(A))),s:pk(B),s:pk(C),s:pk(D),sjtv:sha256(H)))",
21-
&ExtParams::sane(),
22-
).unwrap();
23-
black_box(tree);
24-
});
19+
type Desc = Descriptor<DescriptorPublicKey>;
20+
21+
fn keygen(n: u32) -> DescriptorPublicKey {
22+
let secp = Secp256k1::new();
23+
24+
let mut sk = [0; 32];
25+
sk[31] = n as u8;
26+
sk[30] = (n >> 8) as u8;
27+
sk[29] = (n >> 16) as u8;
28+
sk[28] = (n >> 24) as u8;
29+
let sk = SecretKey::from_slice(&sk).unwrap();
30+
let pk = bitcoin::PublicKey { inner: sk.public_key(&secp), compressed: true };
31+
DescriptorPublicKey::Single(SinglePub { origin: None, key: SinglePubKey::FullKey(pk) })
32+
}
33+
34+
/// Generate a balanced binary tree with a given number of nodes.
35+
///
36+
/// This method is extremely slow relative to parsing or even re-serializing
37+
/// and should never be called from inside a benchmark.
38+
fn generate_balanced_tree_str<CombFn>(n_nodes: usize, mut combfn: CombFn) -> String
39+
where
40+
CombFn: FnMut(&str, &str) -> String,
41+
{
42+
if n_nodes == 0 {
43+
return "1".into();
2544
}
45+
let mut count = 0;
46+
let mut leaf = || {
47+
count += 1;
48+
format!("pk({})", keygen(count))
49+
};
2650

27-
#[bench]
28-
pub fn parse_segwit0_deep(bh: &mut Bencher) {
29-
bh.iter(|| {
30-
let tree = Miniscript::<String, context::Segwitv0>::from_str_ext(
31-
"and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:and_v(v:pk(1),pk(2)),pk(3)),pk(4)),pk(5)),pk(6)),pk(7)),pk(8)),pk(9)),pk(10)),pk(11)),pk(12)),pk(13)),pk(14)),pk(15)),pk(16)),pk(17)),pk(18)),pk(19)),pk(20)),pk(21))",
32-
&ExtParams::sane(),
33-
).unwrap();
34-
black_box(tree);
35-
});
51+
let mut stack = vec![];
52+
for i in 0..n_nodes {
53+
stack.push(leaf());
54+
55+
for _ in 0..(i + 1).trailing_zeros() {
56+
let right = stack.pop().unwrap();
57+
let left = stack.pop().unwrap();
58+
stack.push(combfn(&left, &right));
59+
}
3660
}
61+
assert_ne!(stack.len(), 0, "n_nodes checked above to be nonzero");
3762

38-
#[bench]
39-
pub fn parse_tree(bh: &mut Bencher) {
40-
bh.iter(|| {
41-
let tree = Tree::from_str(
42-
"and(thresh(2,and(sha256(H),or(sha256(H),pk(A))),pk(B),pk(C),pk(D),sha256(H)),pk(E))",
43-
).unwrap();
44-
black_box(tree);
45-
});
63+
while stack.len() > 1 {
64+
let right = stack.pop().unwrap();
65+
let left = stack.pop().unwrap();
66+
stack.push(combfn(&left, &right));
4667
}
4768

48-
#[bench]
49-
pub fn parse_tree_deep(bh: &mut Bencher) {
50-
bh.iter(|| {
51-
let tree = Tree::from_str(
52-
"and(and(and(and(and(and(and(and(and(and(and(and(and(and(and(and(and(and(and(and(1,2),3),4),5),6),7),8),9),10),11),12),13),14),15),16),17),18),19),20),21)"
53-
).unwrap();
54-
black_box(tree);
55-
});
69+
stack.pop().unwrap()
70+
}
71+
72+
/// Generate a one-sided binary tree with a given number of nodes.
73+
///
74+
/// This method is extremely slow relative to parsing or even re-serializing
75+
/// and should never be called from inside a benchmark.
76+
fn generate_deep_tree_str<CombFn>(n_nodes: usize, mut combfn: CombFn) -> String
77+
where
78+
CombFn: FnMut(&str, &str) -> String,
79+
{
80+
if n_nodes == 0 {
81+
return "1".into();
82+
}
83+
let mut count = 0;
84+
let mut leaf = || {
85+
count += 1;
86+
format!("pk({})", keygen(count))
87+
};
88+
89+
let mut stack = vec![];
90+
for i in 0..n_nodes {
91+
stack.push(leaf());
92+
93+
if i > 0 {
94+
let right = stack.pop().unwrap();
95+
let left = stack.pop().unwrap();
96+
stack.push(combfn(&left, &right));
97+
}
98+
}
99+
assert_eq!(stack.len(), 1);
100+
stack.pop().unwrap()
101+
}
102+
103+
macro_rules! benchmark {
104+
($ty:ty, $name:ident, $genfn:expr, $n:expr, $topstr:expr; $comb:expr) => {
105+
#[bench]
106+
fn $name(bh: &mut Bencher) {
107+
let s = format!("{}({})", $topstr, $genfn($n, $comb));
108+
bh.iter(|| black_box(<$ty>::from_str(&s).unwrap()))
109+
}
110+
};
111+
($ty:ty, $name:ident, $genfn:expr, $n:expr, $topstr:expr, parens $comb:expr) => {
112+
benchmark!($ty, $name, $genfn, $n, $topstr; |left, right| format!("{}({left},{right})", $comb));
113+
};
114+
}
115+
116+
macro_rules! benchmark_tr {
117+
// Taproot we need to treat specially
118+
($ty:ty, $name:ident, $genfn:expr, $n:expr; $comb:expr) => {
119+
#[bench]
120+
fn $name(bh: &mut Bencher) {
121+
let s = format!("tr(02b35c601492528601122c0807fa1f8bf987b9704dff438b2524d979b954e206fb,{})", $genfn($n, $comb));
122+
println!("{}", s);
123+
bh.iter(|| black_box(<$ty>::from_str(&s).unwrap()))
124+
}
125+
};
126+
($ty:ty, $name:ident, $genfn:expr, $n:expr, parens $comb:expr) => {
127+
benchmark_tr!($ty, $name, $genfn, $n; |left, right| format!("{}({left},{right})", $comb));
128+
};
129+
($ty:ty, $name:ident, $genfn:expr, $n:expr, braces) => {
130+
benchmark_tr!($ty, $name, $genfn, $n; |left, right| format!("{{{left},{right}}}"));
131+
};
132+
}
133+
134+
macro_rules! balanced_expression {
135+
($name:ident, $n:expr) => {
136+
benchmark!(Tree, $name, generate_balanced_tree_str, $n, "xyz", parens "xyz");
137+
}
138+
}
139+
140+
macro_rules! deep_expression {
141+
($name:ident, $n:expr) => {
142+
benchmark!(Tree, $name, generate_deep_tree_str, $n, "xyz", parens "xyz");
143+
}
144+
}
145+
146+
macro_rules! balanced_segwit {
147+
($name:ident, $n:expr) => {
148+
benchmark!(Desc, $name, generate_balanced_tree_str, $n, "wsh", parens "or_i");
56149
}
150+
}
151+
152+
macro_rules! deep_segwit {
153+
($name:ident, $n:expr) => {
154+
benchmark!(Desc, $name, generate_deep_tree_str, $n, "wsh", parens "or_i");
155+
}
156+
}
157+
158+
macro_rules! balanced_segwit_thresh {
159+
($name:ident, $n:expr) => {
160+
benchmark!(Desc, $name, generate_balanced_tree_str, $n, "wsh"; |l, r| format!("thresh(2,{l},a:{r})"));
161+
}
162+
}
163+
164+
macro_rules! deep_segwit_thresh {
165+
($name:ident, $n:expr) => {
166+
benchmark!(Desc, $name, generate_deep_tree_str, $n, "wsh"; |l, r| format!("thresh(2,{l},a:{r})"));
167+
}
168+
}
169+
170+
macro_rules! taproot_bigscript {
171+
($name:ident, $n:expr) => {
172+
benchmark_tr!(Desc, $name, generate_balanced_tree_str, $n, parens "or_i");
173+
}
174+
}
175+
176+
macro_rules! taproot_bigtree {
177+
($name:ident, $n:expr) => {
178+
benchmark_tr!(Desc, $name, generate_balanced_tree_str, $n, braces);
179+
};
180+
}
181+
182+
macro_rules! deep_taproot_bigscript {
183+
($name:ident, $n:expr) => {
184+
benchmark_tr!(Desc, $name, generate_deep_tree_str, $n, parens "or_i");
185+
}
186+
}
187+
188+
macro_rules! deep_taproot_bigtree {
189+
($name:ident, $n:expr) => {
190+
benchmark_tr!(Desc, $name, generate_deep_tree_str, $n, braces);
191+
};
192+
}
193+
194+
// a, b, c, etc to make the output sort in the right order
195+
balanced_expression!(parse_expression_balanced_a_0, 0);
196+
balanced_expression!(parse_expression_balanced_b_1, 1);
197+
balanced_expression!(parse_expression_balanced_c_2, 2);
198+
balanced_expression!(parse_expression_balanced_d_5, 5);
199+
balanced_expression!(parse_expression_balanced_e_10, 10);
200+
balanced_expression!(parse_expression_balanced_f_20, 20);
201+
balanced_expression!(parse_expression_balanced_g_50, 50);
202+
balanced_expression!(parse_expression_balanced_h_100, 100);
203+
balanced_expression!(parse_expression_balanced_i_200, 200);
204+
balanced_expression!(parse_expression_balanced_j_500, 500);
205+
balanced_expression!(parse_expression_balanced_k_1000, 1000);
206+
balanced_expression!(parse_expression_balanced_l_2000, 2000);
207+
balanced_expression!(parse_expression_balanced_m_5000, 5000);
208+
balanced_expression!(parse_expression_balanced_n_10000, 10000);
209+
210+
deep_expression!(parse_expression_deep_a_0, 0);
211+
deep_expression!(parse_expression_deep_b_1, 1);
212+
deep_expression!(parse_expression_deep_c_2, 2);
213+
deep_expression!(parse_expression_deep_d_5, 5);
214+
deep_expression!(parse_expression_deep_e_10, 10);
215+
deep_expression!(parse_expression_deep_f_20, 20);
216+
deep_expression!(parse_expression_deep_g_50, 50);
217+
deep_expression!(parse_expression_deep_h_100, 100);
218+
deep_expression!(parse_expression_deep_i_200, 200);
219+
deep_expression!(parse_expression_deep_j_300, 300);
220+
deep_expression!(parse_expression_deep_j_400, 400);
221+
// For "deep" benchmarks we hit max recursion depth and can't go farther
222+
223+
balanced_segwit!(parse_descriptor_balanced_segwit_a_0, 0);
224+
balanced_segwit!(parse_descriptor_balanced_segwit_b_1, 1);
225+
balanced_segwit!(parse_descriptor_balanced_segwit_c_10, 10);
226+
balanced_segwit!(parse_descriptor_balanced_segwit_d_20, 20);
227+
balanced_segwit!(parse_descriptor_balanced_segwit_e_40, 40);
228+
balanced_segwit!(parse_descriptor_balanced_segwit_f_60, 60);
229+
balanced_segwit!(parse_descriptor_balanced_segwit_g_80, 80);
230+
balanced_segwit!(parse_descriptor_balanced_segwit_h_90, 90);
231+
deep_segwit!(parse_descriptor_deep_segwit_a_0, 0);
232+
deep_segwit!(parse_descriptor_deep_segwit_b_1, 1);
233+
deep_segwit!(parse_descriptor_deep_segwit_c_10, 10);
234+
deep_segwit!(parse_descriptor_deep_segwit_d_20, 20);
235+
deep_segwit!(parse_descriptor_deep_segwit_e_40, 40);
236+
deep_segwit!(parse_descriptor_deep_segwit_f_60, 60);
237+
deep_segwit!(parse_descriptor_deep_segwit_g_80, 80);
238+
deep_segwit!(parse_descriptor_deep_segwit_h_90, 90);
239+
// With or_i construction we cannot segwit more than 94 keys without exceeding the max witness size.
240+
241+
balanced_segwit_thresh!(parse_descriptor_balanced_segwit_thresh_a_1, 1);
242+
balanced_segwit_thresh!(parse_descriptor_balanced_segwit_thresh_b_10, 10);
243+
balanced_segwit_thresh!(parse_descriptor_balanced_segwit_thresh_c_20, 20);
244+
balanced_segwit_thresh!(parse_descriptor_balanced_segwit_thresh_d_40, 40);
245+
balanced_segwit_thresh!(parse_descriptor_balanced_segwit_thresh_e_60, 60);
246+
balanced_segwit_thresh!(parse_descriptor_balanced_segwit_thresh_f_80, 80);
247+
balanced_segwit_thresh!(parse_descriptor_balanced_segwit_thresh_g_90, 90);
248+
deep_segwit_thresh!(parse_descriptor_deep_segwit_thresh_a_1, 1);
249+
deep_segwit_thresh!(parse_descriptor_deep_segwit_thresh_b_10, 10);
250+
deep_segwit_thresh!(parse_descriptor_deep_segwit_thresh_c_20, 20);
251+
deep_segwit_thresh!(parse_descriptor_deep_segwit_thresh_d_40, 40);
252+
deep_segwit_thresh!(parse_descriptor_deep_segwit_thresh_e_60, 60);
253+
deep_segwit_thresh!(parse_descriptor_deep_segwit_thresh_f_80, 80);
254+
deep_segwit_thresh!(parse_descriptor_deep_segwit_thresh_g_90, 90);
255+
256+
//taproot_bigscript!(parse_descriptor_tr_oneleaf_a_0, 0); // See #734
257+
taproot_bigscript!(parse_descriptor_tr_oneleaf_a_1, 1);
258+
taproot_bigscript!(parse_descriptor_tr_oneleaf_b_10, 10);
259+
taproot_bigscript!(parse_descriptor_tr_oneleaf_c_20, 20);
260+
taproot_bigscript!(parse_descriptor_tr_oneleaf_d_50, 50);
261+
taproot_bigscript!(parse_descriptor_tr_oneleaf_e_100, 100);
262+
taproot_bigscript!(parse_descriptor_tr_oneleaf_f_200, 200);
263+
taproot_bigscript!(parse_descriptor_tr_oneleaf_g_500, 500);
264+
taproot_bigscript!(parse_descriptor_tr_oneleaf_h_1000, 1000);
265+
taproot_bigscript!(parse_descriptor_tr_oneleaf_i_2000, 2000);
266+
taproot_bigscript!(parse_descriptor_tr_oneleaf_j_5000, 5000);
267+
taproot_bigscript!(parse_descriptor_tr_oneleaf_k_10000, 10000);
268+
269+
taproot_bigtree!(parse_descriptor_tr_bigtree_a_1, 1);
270+
taproot_bigtree!(parse_descriptor_tr_bigtree_b_2, 2);
271+
taproot_bigtree!(parse_descriptor_tr_bigtree_c_5, 5);
272+
taproot_bigtree!(parse_descriptor_tr_bigtree_d_10, 10);
273+
taproot_bigtree!(parse_descriptor_tr_bigtree_e_20, 20);
274+
taproot_bigtree!(parse_descriptor_tr_bigtree_f_50, 50);
275+
taproot_bigtree!(parse_descriptor_tr_bigtree_g_100, 100);
276+
taproot_bigtree!(parse_descriptor_tr_bigtree_h_200, 200);
277+
taproot_bigtree!(parse_descriptor_tr_bigtree_i_500, 500);
278+
taproot_bigtree!(parse_descriptor_tr_bigtree_j_1000, 1000);
279+
taproot_bigtree!(parse_descriptor_tr_bigtree_k_2000, 2000);
280+
taproot_bigtree!(parse_descriptor_tr_bigtree_l_5000, 5000);
281+
taproot_bigtree!(parse_descriptor_tr_bigtree_m_10000, 10000);
282+
283+
deep_taproot_bigscript!(parse_descriptor_tr_deep_oneleaf_a_1, 1);
284+
deep_taproot_bigscript!(parse_descriptor_tr_deep_oneleaf_b_10, 10);
285+
deep_taproot_bigscript!(parse_descriptor_tr_deep_oneleaf_c_20, 20);
286+
deep_taproot_bigscript!(parse_descriptor_tr_deep_oneleaf_d_50, 50);
287+
deep_taproot_bigscript!(parse_descriptor_tr_deep_oneleaf_e_100, 100);
288+
deep_taproot_bigscript!(parse_descriptor_tr_deep_oneleaf_f_200, 200);
289+
290+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_a_1, 1);
291+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_b_2, 2);
292+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_c_5, 5);
293+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_d_10, 10);
294+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_e_20, 20);
295+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_f_50, 50);
296+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_g_100, 100);
297+
deep_taproot_bigtree!(parse_descriptor_tr_deep_bigtree_h_128, 128);
298+
// taproot trees are not allowed to be 129 deep
57299

58300
#[cfg(feature = "compiler")]
59301
mod compiler_benches {
60302
use super::*;
61-
62-
use core::str::FromStr;
63-
64-
use crate::Error;
65-
use crate::policy::Concrete;
66-
use crate::policy::compiler::CompilerError;
67303
use crate::descriptor::Descriptor;
68304
use crate::miniscript::Tap;
305+
use crate::policy::compiler::CompilerError;
306+
use crate::policy::Concrete;
69307
use crate::prelude::*;
308+
use crate::Error;
70309

71310
type TapMsRes = Result<Miniscript<String, Tap>, CompilerError>;
72311
type TapDesc = Result<Descriptor<String>, Error>;

src/expression/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,3 @@ mod tests {
314314
assert_eq!(valid_chars, super::VALID_CHARS);
315315
}
316316
}
317-

0 commit comments

Comments
 (0)