This repository has been archived on 2023-04-04. You can view files and clone it, but cannot push or open issues or pull requests.
p3l/templates/main.rs.j2
2022-10-16 20:14:55 +02:00

298 lines
7.1 KiB
Django/Jinja

#![allow(unused_imports)]
#![warn(unreachable_pub)]
#![forbid(elided_lifetimes_in_paths, unsafe_code)]
use bpaf::construct;
use indexmap::IndexMap;
use indicatif::{ParallelProgressIterator as _, ProgressBar, ProgressStyle};
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator as _};
use stdlib::{ArgValue, OutputPrinterAny, OutputPrinterTwoVars, int};
use std::{io::stdout, path::PathBuf};
#[derive(Clone, Debug)]
struct Args {
runs: u64,
{%- for arg in inputs %}
in_{{arg.ident}}: ArgValue,
{%- endfor %}
{%- for out in outputs %}
out{{loop.index0}}: (OutputPrinter
{%- if out.idents.len() == 2 %}TwoVars{% else %}Any{% endif -%}
, Option<PathBuf>),
{%- endfor %}
}
struct Values {
{%- for arg in inputs %}
{{arg.ident}}: int,
{%- endfor %}
}
impl Args {
fn values(&self) -> Values {
Values {
{%- for arg in inputs %}
{{arg.ident}}: self.in_{{arg.ident}}.get_value(),
{%- endfor %}
}
}
fn parse() -> Self {
let runs = bpaf::short('n')
.help("Specify the number of runs (defaults to 1)")
.argument("RUNS")
.from_str()
.fallback(1);
{%- for arg in inputs %}
let in_{{arg.ident}} = {
let value = bpaf::{{arg.short_long}}({{arg.quote}}{{arg.ident}}{{arg.quote}})
.help("Set a value for {{arg.ident}}")
.argument("VALUE")
.from_str()
.map(ArgValue::Value);
let min = bpaf::long("{{arg.ident}}-min")
.help("Set a range for {{arg.ident}} with this minimum value (inclusive)")
.argument("MIN-VALUE")
.from_str();
let max = bpaf::long("{{arg.ident}}-max")
.help("Set a range for {{arg.ident}} with this maximum value (inclusive)")
.argument("MAX-VALUE")
.from_str();
let range = construct!(ArgValue::Range { min, max });
value.or_else(range)
};
{%- endfor %}
{%- for out in outputs %}
let out{{loop.index0}} = {
let printer = bpaf::long("
{%- for var in out.idents %}{{var}}-{% endfor %}printer")
.help("Set the output printer for output (
{%- for var in out.idents %}{{var}}, {% endfor %})")
.argument("PRINTER")
.from_str()
.default();
let path = bpaf::long("
{%- for var in out.idents %}{{var}}-{% endfor %}out")
.help("Write the output to a file")
.argument("PATH")
.from_str()
.map(Some)
.default();
construct!(printer, path)
};
{%- endfor %}
let parser = construct!(Self {
runs,
{%- for arg in inputs %}
in_{{arg.ident}},
{%- endfor %}
{%- for out in outputs %}
out{{loop.index0}}
{%- endfor %}
});
let info = bpaf::Info::default();
info.for_parser(parser).run()
}
}
/// This is the output captured immediately after running one sample.
#[derive(Eq, Hash, PartialEq)]
struct Output {
{%- for out in outputs %}
{%- for var in out.idents %}
{{var}}: int,
{%- endfor %}
{%- endfor %}
}
{%- for out in outputs %}
/// This type is the output of one tuple from one sample.
#[derive(Eq, Hash, PartialEq)]
struct Output{{loop.index0}}(
{%- for var in out.idents %}int, {% endfor -%}
);
impl IntoIterator for Output{{loop.index0}} {
type Item = (&'static str, int);
type IntoIter = OutputIntoIter{{loop.index0}};
fn into_iter(self) -> Self::IntoIter {
OutputIntoIter{{loop.index0}} {
output: self,
idx: 0
}
}
}
impl<'a> IntoIterator for &'a Output{{loop.index0}} {
type Item = (&'static str, int);
type IntoIter = OutputIter{{loop.index0}}<'a>;
fn into_iter(self) -> Self::IntoIter {
OutputIter{{loop.index0}} {
output: self,
idx: 0
}
}
}
fn outputiter{{loop.index0}}_next(
output: &Output{{loop.index0}},
idx: &mut usize
) -> Option<(&'static str, int)> {
let next = match *idx {
{%- for var in out.idents %}
{{loop.index0}} => (stringify!({{var}}), output.{{loop.index0}}),
{%- endfor %}
_ => return None
};
*idx += 1;
Some(next)
}
/// This type associates the output values with their variable names.
#[doc(hidden)]
struct OutputIntoIter{{loop.index0}} {
output: Output{{loop.index0}},
idx: usize
}
impl Iterator for OutputIntoIter{{loop.index0}} {
type Item = (&'static str, int);
fn next(&mut self) -> Option<Self::Item> {
outputiter{{loop.index0}}_next(&self.output, &mut self.idx)
}
}
/// This type associates the output values with their variable names.
#[doc(hidden)]
struct OutputIter{{loop.index0}}<'a> {
output: &'a Output{{loop.index0}},
idx: usize
}
impl Iterator for OutputIter{{loop.index0}}<'_> {
type Item = (&'static str, int);
fn next(&mut self) -> Option<Self::Item> {
outputiter{{loop.index0}}_next(self.output, &mut self.idx)
}
}
{%- endfor %}
/// This type collects the output from each sample and processes them
/// into output values. It cannot use output iterators directly as we
/// need an `Eq` implementation.
#[derive(Default)]
struct OutputEntries {
{%- for out in outputs %}
entries{{loop.index0}}: IndexMap<Output{{loop.index0}}, usize>,
{%- endfor %}
len: usize
}
impl FromParallelIterator<Output> for OutputEntries {
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = Output>
{
par_iter
.into_par_iter()
.fold(Self::default, |mut list, {% if outputs.is_empty() %}_{% else %}out{% endif %}| {
list.len += 1;
{%- for out in outputs %}
list.entries{{loop.index0}}
.entry(Output{{loop.index0}}(
{%- for var in out.idents %}out.{{var}}, {% endfor -%}
))
.and_modify(|qty| *qty += 1)
.or_insert(1);
{%- endfor %}
list
})
.reduce(Self::default, |mut list, other| {
list.len += other.len;
{%- for out in outputs %}
for (out, count) in other.entries{{loop.index0}} {
list.entries{{loop.index0}}
.entry(out)
.and_modify(|qty| *qty += count)
.or_insert(count);
}
{%- endfor %}
list
})
}
}
/// This type is used to divide the output entries into output iterators.
struct OutputLists {
{%- for out in outputs %}
out{{loop.index0}}: stdlib::OutputList<
IndexMap<Output{{loop.index0}}, usize>
>,
{%- endfor %}
}
impl From<OutputEntries> for OutputLists {
{%- if outputs.is_empty() %}
#[allow(unused_variables)]
{%- endif %}
fn from(this: OutputEntries) -> Self {
{%- for out in outputs %}
let mut out{{loop.index0}} = this.entries{{loop.index0}};
out{{loop.index0}}.sort_unstable_by(
|_, lhs_qty, _, rhs_qty| rhs_qty.cmp(lhs_qty)
);
{% endfor -%}
Self {
{%- for out in outputs %}
out{{loop.index0}}: stdlib::OutputList::new(
out{{loop.index0}},
this.len
),
{%- endfor %}
}
}
}
mod funs;
mod types;
fn main() {
let args = Args::parse();
let pb = ProgressBar::new(args.runs);
pb.set_prefix("Collecting samples ...");
pb.set_style(ProgressStyle::default_bar()
.template("{prefix} {percent:>3}% [{wide_bar}] {pos:>7}/{len:7} [{elapsed_precise}<{eta_precise}; {per_sec} samples/s]")
.unwrap()
.progress_chars("#> "));
{%- if outputs.is_empty() %}
#[allow(unused_variables)]
{%- endif %}
let out: OutputLists = (0 .. args.runs)
.into_par_iter()
.progress_with(pb)
.map(|_| funs::run(args.values()))
.collect::<OutputEntries>()
.into();
{%- for out in outputs %}
{%- if !loop.first %}
println!();
{%- endif %}
stdlib::OutputTarget::print(args.out{{loop.index0}}, out.out{{loop.index0}});
{%- endfor %}
}