mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-21 18:10:42 -04:00
FEATURE: Benchmarking and tracing
Benchmarks and tracing as a compile time feature. This should help while tracking down performance bottlenecks.
This commit is contained in:
parent
f3d995178b
commit
b12ac18481
28
Cargo.toml
28
Cargo.toml
@ -15,12 +15,38 @@ version = "^3.2"
|
||||
[dependencies]
|
||||
nom_locate = "^0.1.1"
|
||||
clap = "~2.26.0"
|
||||
serde_json = "~1.0.9"
|
||||
serde_json = "~1.0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
bencher = "~0.1.5"
|
||||
cpuprofiler = "0.0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "parse"
|
||||
harness = false
|
||||
path = "src/benches/parse.rs"
|
||||
|
||||
[profile.bench]
|
||||
opt-level = 0
|
||||
debug = true
|
||||
rpath = false
|
||||
lto = false
|
||||
debug-assertions = false
|
||||
codegen-units = 16
|
||||
incremental = false
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
[lib]
|
||||
name = "ucglib"
|
||||
path = "src/lib.rs"
|
||||
bench = false
|
||||
|
||||
[[bin]]
|
||||
name = "ucg"
|
||||
path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
tracing = []
|
@ -23,6 +23,11 @@ let connstr_mapper = macro (item) => {
|
||||
|
||||
let db_conns = map connstr_mapper.str [db_conn1, db_conn2];
|
||||
|
||||
let cplx_list = [
|
||||
{foo = 1},
|
||||
{bar = {foo=1}},
|
||||
];
|
||||
|
||||
// Our server configuration.
|
||||
let server_config = {
|
||||
db_conn1 = db_conns.0, // connection one
|
||||
|
125
src/benches/parse.rs
Normal file
125
src/benches/parse.rs
Normal file
@ -0,0 +1,125 @@
|
||||
#![allow(unused_must_use)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
extern crate cpuprofiler;
|
||||
extern crate nom_locate;
|
||||
extern crate ucglib;
|
||||
|
||||
use bencher::Bencher;
|
||||
//use cpuprofiler::PROFILER;
|
||||
|
||||
macro_rules! bench_parse {
|
||||
($parsemac:ident($i:expr), $b:expr) => {
|
||||
bench_parse!($i, $parsemac, $b)
|
||||
};
|
||||
($i:expr, $f:expr, $b:expr) => {
|
||||
let input = nom_locate::LocatedSpan::new($i);
|
||||
let val = tokenize(input).unwrap();
|
||||
$b.iter(|| {
|
||||
$f(TokenIter {
|
||||
source: val.as_slice(),
|
||||
})
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
use ucglib::parse::*;
|
||||
use ucglib::tokenizer::*;
|
||||
|
||||
fn do_parse(i: &str) {
|
||||
parse(nom_locate::LocatedSpan::new(i));
|
||||
}
|
||||
|
||||
fn parse_int(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("1;"));
|
||||
}
|
||||
|
||||
fn parse_whole_float(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("1.0;"));
|
||||
}
|
||||
|
||||
fn parse_pre_partial_float(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("1.;"));
|
||||
}
|
||||
|
||||
fn parse_post_partial_float(b: &mut Bencher) {
|
||||
b.iter(|| do_parse(".0;"));
|
||||
}
|
||||
|
||||
fn parse_bareword(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("foo;"));
|
||||
}
|
||||
|
||||
fn parse_string(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("\"foo\";"));
|
||||
}
|
||||
|
||||
fn parse_simple_tuple(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("{foo = 1, bar = \"2\",};"));
|
||||
}
|
||||
|
||||
fn parse_complex_tuple(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("{foo = 1, { bar = \"2\",}};"));
|
||||
}
|
||||
|
||||
fn parse_simple_list(b: &mut Bencher) {
|
||||
//PROFILER
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .start("./my-parse_list.profile")
|
||||
// .unwrap();
|
||||
b.iter(|| do_parse("[1, 2, 3];"));
|
||||
//PROFILER.lock().unwrap().stop().unwrap();
|
||||
}
|
||||
|
||||
fn parse_complex_tuple_list(b: &mut Bencher) {
|
||||
//PROFILER
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .start("./my-parse_list.profile")
|
||||
// .unwrap();
|
||||
let input = "[{foo=1}, {bar=2}, {quux=4}];";
|
||||
b.iter(|| do_parse(input));
|
||||
//bench_parse!(non_op_expression(input), b);
|
||||
//PROFILER.lock().unwrap().stop().unwrap();
|
||||
}
|
||||
|
||||
fn parse_complex_nested_list(b: &mut Bencher) {
|
||||
//PROFILER
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .start("./my-parse_list.profile")
|
||||
// .unwrap();
|
||||
let input = "[[1,2], [3,4], [4,5]];";
|
||||
b.iter(|| do_parse(input));
|
||||
//bench_parse!(non_op_expression(input), b);
|
||||
//PROFILER.lock().unwrap().stop().unwrap();
|
||||
}
|
||||
|
||||
fn parse_selector_tuple_head(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("{foo=1}.foo;"));
|
||||
}
|
||||
|
||||
fn parse_selector_symbol_head(b: &mut Bencher) {
|
||||
b.iter(|| do_parse("bar.foo;"));
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
benches,
|
||||
parse_int,
|
||||
parse_whole_float,
|
||||
parse_pre_partial_float,
|
||||
parse_post_partial_float,
|
||||
parse_bareword,
|
||||
parse_string,
|
||||
parse_simple_tuple,
|
||||
parse_complex_tuple,
|
||||
parse_simple_list,
|
||||
parse_complex_tuple_list,
|
||||
parse_complex_nested_list,
|
||||
parse_selector_tuple_head,
|
||||
parse_selector_symbol_head,
|
||||
);
|
||||
|
||||
benchmark_main!(benches);
|
148
src/parse/mod.rs
148
src/parse/mod.rs
@ -27,8 +27,39 @@ use tokenizer::*;
|
||||
|
||||
type NomResult<'a, O> = nom::IResult<TokenIter<'a>, O, error::Error>;
|
||||
|
||||
type ParseResult<O> = Result<O, error::Error>;
|
||||
#[cfg(feature = "tracing")]
|
||||
const ENABLE_TRACE: bool = true;
|
||||
#[cfg(not(feature = "tracing"))]
|
||||
const ENABLE_TRACE: bool = false;
|
||||
|
||||
type ParseResult<O> = Result<O, error::Error>;
|
||||
macro_rules! trace_nom {
|
||||
($i:expr, $rule:ident!( $($args:tt)* )) => {
|
||||
{
|
||||
if ENABLE_TRACE {
|
||||
eprintln!("Entering Rule: {:?} {:?}", stringify!($rule), $i);
|
||||
}
|
||||
let result = $rule($i, $($args)* );
|
||||
if ENABLE_TRACE {
|
||||
eprintln!("Exiting Rule: {:?}", stringify!($rule));
|
||||
}
|
||||
result
|
||||
}
|
||||
};
|
||||
|
||||
($i:expr, $rule:ident) => {
|
||||
{
|
||||
if ENABLE_TRACE {
|
||||
eprintln!("Entering Rule: {:?} {:?}", stringify!($rule), $i);
|
||||
}
|
||||
let result = call!($i, $rule);
|
||||
if ENABLE_TRACE {
|
||||
eprintln!("Exiting Rule: {:?}", stringify!($rule));
|
||||
}
|
||||
result
|
||||
}
|
||||
};
|
||||
}
|
||||
fn symbol_to_value(s: &Token) -> ParseResult<Value> {
|
||||
Ok(Value::Symbol(value_node!(
|
||||
s.fragment.to_string(),
|
||||
@ -300,24 +331,27 @@ named!(empty_value<TokenIter, Value, error::Error>,
|
||||
)
|
||||
);
|
||||
|
||||
named!(compound_value<TokenIter, Value, error::Error>,
|
||||
alt!(list_value | tuple)
|
||||
named!(pub compound_value<TokenIter, Value, error::Error>,
|
||||
alt_peek!(
|
||||
punct!("[") => trace_nom!(list_value) |
|
||||
punct!("{") => trace_nom!(tuple)
|
||||
)
|
||||
);
|
||||
|
||||
named!(scalar_value<TokenIter, Value, error::Error>,
|
||||
alt!(
|
||||
boolean_value |
|
||||
empty_value |
|
||||
number |
|
||||
quoted_value
|
||||
trace_nom!(boolean_value) |
|
||||
trace_nom!(empty_value) |
|
||||
trace_nom!(number) |
|
||||
trace_nom!(quoted_value)
|
||||
)
|
||||
);
|
||||
|
||||
named!(value<TokenIter, Value, error::Error>,
|
||||
named!(pub value<TokenIter, Value, error::Error>,
|
||||
alt!(
|
||||
selector_value
|
||||
| compound_value
|
||||
| scalar_value
|
||||
trace_nom!(selector_value)
|
||||
| trace_nom!(compound_value)
|
||||
| trace_nom!(scalar_value)
|
||||
)
|
||||
);
|
||||
|
||||
@ -327,7 +361,7 @@ fn value_to_expression(v: Value) -> ParseResult<Expression> {
|
||||
|
||||
named!(simple_expression<TokenIter, Expression, error::Error>,
|
||||
map_res!(
|
||||
value,
|
||||
trace_nom!(value),
|
||||
value_to_expression
|
||||
)
|
||||
);
|
||||
@ -343,9 +377,9 @@ fn tuple_to_binary_expression(
|
||||
}))
|
||||
}
|
||||
|
||||
/// do_binary_expr implements precedence based parsing where the more tightly bound parsers
|
||||
/// are passed in as lowerrule parsers. We default to grouped_expression and simple_expression as
|
||||
/// the most tightly bound expressions.
|
||||
/// do_binary_expr implements precedence based parsing where the more tightly bound
|
||||
/// parsers are passed in as lowerrule parsers. We default to any non_op_expression
|
||||
/// as the most tightly bound expressions.
|
||||
macro_rules! do_binary_expr {
|
||||
($i:expr, $oprule:ident!( $($args:tt)* )) => {
|
||||
do_binary_expr!($i, $oprule!($($args)*), non_op_expression)
|
||||
@ -385,7 +419,7 @@ named!(sum_expression<TokenIter, Expression, error::Error>,
|
||||
alt_peek!(
|
||||
punct!("+") => math_op_type |
|
||||
punct!("-") => math_op_type),
|
||||
alt!(product_expression | simple_expression | grouped_expression))
|
||||
alt!(trace_nom!(product_expression) | trace_nom!(simple_expression) | trace_nom!(grouped_expression)))
|
||||
);
|
||||
|
||||
named!(product_expression<TokenIter, Expression, error::Error>,
|
||||
@ -397,7 +431,7 @@ named!(product_expression<TokenIter, Expression, error::Error>,
|
||||
);
|
||||
|
||||
named!(math_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(sum_expression | product_expression)
|
||||
alt!(trace_nom!(sum_expression) | trace_nom!(product_expression))
|
||||
);
|
||||
|
||||
// TODO(jwall): Change comparison operators to use the do_binary_expr! with precedence?
|
||||
@ -427,9 +461,9 @@ named!(compare_expression<TokenIter, Expression, error::Error>,
|
||||
map_res!(
|
||||
do_parse!(
|
||||
pos: pos >>
|
||||
left: alt!(math_expression | non_op_expression) >>
|
||||
left: alt!(trace_nom!(math_expression) | trace_nom!(non_op_expression)) >>
|
||||
typ: compare_op_type >>
|
||||
right: alt!(math_expression | non_op_expression) >>
|
||||
right: alt!(trace_nom!(math_expression) | trace_nom!(non_op_expression)) >>
|
||||
(pos, typ, left, right)
|
||||
),
|
||||
tuple_to_compare_expression
|
||||
@ -438,7 +472,7 @@ named!(compare_expression<TokenIter, Expression, error::Error>,
|
||||
|
||||
// FIXME(jwall): This is really *really* slow.
|
||||
named!(op_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(math_expression | compare_expression)
|
||||
alt!(trace_nom!(math_expression) | trace_nom!(compare_expression))
|
||||
);
|
||||
|
||||
fn expression_to_grouped_expression(e: Expression) -> ParseResult<Expression> {
|
||||
@ -447,7 +481,7 @@ fn expression_to_grouped_expression(e: Expression) -> ParseResult<Expression> {
|
||||
|
||||
named!(grouped_expression<TokenIter, Expression, error::Error>,
|
||||
map_res!(
|
||||
preceded!(punct!("("), terminated!(expression, punct!(")"))),
|
||||
preceded!(punct!("("), terminated!(trace_nom!(expression), punct!(")"))),
|
||||
expression_to_grouped_expression
|
||||
)
|
||||
);
|
||||
@ -557,9 +591,9 @@ named!(copy_expression<TokenIter, Expression, error::Error>,
|
||||
map_res!(
|
||||
do_parse!(
|
||||
pos: pos >>
|
||||
selector: selector_list >>
|
||||
selector: trace_nom!(selector_list) >>
|
||||
punct!("{") >>
|
||||
fields: field_list >>
|
||||
fields: trace_nom!(field_list) >>
|
||||
opt_res!(punct!(",")) >> // noms opt! macro does not preserve error types properly but this one does.
|
||||
punct!("}") >>
|
||||
(SelectorDef::new(selector, pos.line, pos.column as usize), fields)
|
||||
@ -597,10 +631,10 @@ named!(macro_expression<TokenIter, Expression, error::Error>,
|
||||
pos: pos >>
|
||||
word!("macro") >>
|
||||
punct!("(") >>
|
||||
arglist: arglist >>
|
||||
arglist: trace_nom!(arglist) >>
|
||||
punct!(")") >>
|
||||
punct!("=>") >>
|
||||
map: tuple >>
|
||||
map: trace_nom!(tuple) >>
|
||||
(pos, arglist, map)
|
||||
),
|
||||
tuple_to_macro
|
||||
@ -627,9 +661,9 @@ named!(select_expression<TokenIter, Expression, error::Error>,
|
||||
map_res!(
|
||||
do_parse!(
|
||||
start: word!("select") >>
|
||||
val: terminated!(expression, punct!(",")) >>
|
||||
default: terminated!(expression, punct!(",")) >>
|
||||
map: tuple >>
|
||||
val: terminated!(trace_nom!(expression), punct!(",")) >>
|
||||
default: terminated!(trace_nom!(expression), punct!(",")) >>
|
||||
map: trace_nom!(tuple) >>
|
||||
(start.pos.clone(), val, default, map)
|
||||
),
|
||||
tuple_to_select
|
||||
@ -650,7 +684,7 @@ named!(format_expression<TokenIter, Expression, error::Error>,
|
||||
tmpl: match_type!(STR) >>
|
||||
punct!("%") >>
|
||||
punct!("(") >>
|
||||
args: separated_list!(punct!(","), expression) >>
|
||||
args: separated_list!(punct!(","), trace_nom!(expression)) >>
|
||||
punct!(")") >>
|
||||
(tmpl, args)
|
||||
),
|
||||
@ -685,7 +719,7 @@ fn vec_to_selector_value(t: (Position, SelectorList)) -> ParseResult<Value> {
|
||||
named!(selector_value<TokenIter, Value, error::Error>,
|
||||
map_res!(
|
||||
do_parse!(
|
||||
sl: selector_list >>
|
||||
sl: trace_nom!(selector_list) >>
|
||||
(sl.head.pos().clone(), sl)
|
||||
),
|
||||
vec_to_selector_value
|
||||
@ -695,9 +729,9 @@ named!(selector_value<TokenIter, Value, error::Error>,
|
||||
named!(call_expression<TokenIter, Expression, error::Error>,
|
||||
map_res!(
|
||||
do_parse!(
|
||||
macroname: selector_value >>
|
||||
macroname: trace_nom!(selector_value) >>
|
||||
punct!("(") >>
|
||||
args: separated_list!(punct!(","), expression) >>
|
||||
args: separated_list!(punct!(","), trace_nom!(expression)) >>
|
||||
punct!(")") >>
|
||||
(macroname.pos().clone(), macroname, args)
|
||||
),
|
||||
@ -797,36 +831,41 @@ named!(list_op_expression<TokenIter, Expression, error::Error>,
|
||||
do_parse!(
|
||||
pos: pos >>
|
||||
optype: alt!(word!("map") | word!("filter")) >>
|
||||
macroname: selector_value >>
|
||||
list: symbol_or_list >>
|
||||
macroname: trace_nom!(selector_value) >>
|
||||
list: trace_nom!(symbol_or_list) >>
|
||||
(pos, optype, macroname, list)
|
||||
),
|
||||
tuple_to_list_op
|
||||
)
|
||||
);
|
||||
|
||||
named!(non_op_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(list_op_expression |
|
||||
macro_expression |
|
||||
format_expression |
|
||||
select_expression |
|
||||
call_expression |
|
||||
copy_expression |
|
||||
grouped_expression |
|
||||
simple_expression)
|
||||
named!(pub non_op_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(trace_nom!(list_op_expression) |
|
||||
trace_nom!(macro_expression) |
|
||||
trace_nom!(format_expression) |
|
||||
trace_nom!(select_expression) |
|
||||
trace_nom!(grouped_expression) |
|
||||
trace_nom!(call_expression) |
|
||||
trace_nom!(copy_expression) |
|
||||
trace_nom!(simple_expression))
|
||||
);
|
||||
|
||||
named!(expression<TokenIter, Expression, error::Error>,
|
||||
alt_complete!(op_expression | non_op_expression)
|
||||
// TODO(jwall): Because we have to parse an op_expression before a non_op_expression
|
||||
// this parser is really slow. The reason is that the op_expression is unable to fail
|
||||
// quickly enough and wastes a lot of parsing time before it realizes that this is not
|
||||
// an operator expression. Fixing this probably means hand rolling this parsing
|
||||
// function so we can identify early whether this is an op_expression or not.
|
||||
named!(pub expression<TokenIter, Expression, error::Error>,
|
||||
alt_complete!(trace_nom!(op_expression) | trace_nom!(non_op_expression))
|
||||
);
|
||||
|
||||
fn expression_to_statement(v: Expression) -> ParseResult<Statement> {
|
||||
Ok(Statement::Expression(v))
|
||||
}
|
||||
|
||||
named!(expression_statement<TokenIter, Statement, error::Error>,
|
||||
named!(pub expression_statement<TokenIter, Statement, error::Error>,
|
||||
map_res!(
|
||||
terminated!(expression, punct!(";")),
|
||||
terminated!(trace_nom!(expression), punct!(";")),
|
||||
expression_to_statement
|
||||
)
|
||||
);
|
||||
@ -843,7 +882,7 @@ named!(let_stmt_body<TokenIter, Statement, error::Error>,
|
||||
do_parse!(
|
||||
name: match_type!(BAREWORD) >>
|
||||
punct!("=") >>
|
||||
val: expression >>
|
||||
val: trace_nom!(expression) >>
|
||||
punct!(";") >>
|
||||
(name, val)),
|
||||
tuple_to_let
|
||||
@ -859,7 +898,7 @@ named!(let_statement<TokenIter, Statement, error::Error>,
|
||||
error::Error::new(
|
||||
"Invalid syntax for let binding",
|
||||
error::ErrorType::ParseError, pos)),
|
||||
let_stmt_body) >>
|
||||
trace_nom!(let_stmt_body)) >>
|
||||
(stmt)
|
||||
)
|
||||
);
|
||||
@ -893,7 +932,7 @@ named!(import_statement<TokenIter, Statement, error::Error>,
|
||||
error::Error::new(
|
||||
"Invalid syntax for import",
|
||||
error::ErrorType::ParseError, pos)),
|
||||
import_stmt_body) >>
|
||||
trace_nom!(import_stmt_body)) >>
|
||||
(stmt)
|
||||
)
|
||||
);
|
||||
@ -916,10 +955,11 @@ named!(assert_statement<TokenIter, Statement, error::Error>,
|
||||
//trace_macros!(true);
|
||||
fn statement(i: TokenIter) -> nom::IResult<TokenIter, Statement, error::Error> {
|
||||
return alt_peek!(i,
|
||||
word!("assert") => assert_statement |
|
||||
word!("import") => import_statement |
|
||||
word!("let") => let_statement |
|
||||
expression_statement);
|
||||
word!("assert") => trace_nom!(assert_statement) |
|
||||
word!("import") => trace_nom!(import_statement) |
|
||||
word!("let") => trace_nom!(let_statement) |
|
||||
trace_nom!(expression_statement)
|
||||
);
|
||||
}
|
||||
//trace_macros!(false);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user