Skip to content

Commit

Permalink
Avoid extra allocations while generating HTML reports
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark-Simulacrum committed Dec 17, 2023
1 parent 76cd44d commit ffb0b6a
Show file tree
Hide file tree
Showing 21 changed files with 531 additions and 375 deletions.
4 changes: 3 additions & 1 deletion src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,5 +161,7 @@ pub fn render_template<C: Serialize>(name: &str, context: &C) -> Fallible<String
tera = &TERA_CACHE;
}

Ok(tera.render(name, context).to_failure()?)
Ok(tera
.render(name, context)
.map_err(|e| failure::format_err!("{:?}", e))?)
}
2 changes: 1 addition & 1 deletion src/report/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl ResultName for TestResult {
}
}

#[derive(Serialize)]
#[derive(PartialEq, Eq, Hash, Serialize)]
pub enum Color {
Single(&'static str),
Striped(&'static str, &'static str),
Expand Down
156 changes: 96 additions & 60 deletions src/report/html.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use crate::assets;
use crate::experiments::Experiment;
use crate::prelude::*;
Expand All @@ -6,7 +8,9 @@ use crate::report::{
ResultColor, ResultName, TestResults,
};
use crate::results::EncodingType;
use indexmap::IndexMap;
use indexmap::{IndexMap, IndexSet};

use super::CrateVersionStatus;

#[derive(Serialize)]
struct NavbarItem {
Expand All @@ -23,15 +27,15 @@ enum CurrentPage {
}

#[derive(Serialize)]
enum ReportCratesHTML {
Plain(Vec<CrateResultHTML>),
enum ReportCratesHTML<'a> {
Plain(Vec<CrateResultHTML<'a>>),
Tree {
count: u32,
tree: IndexMap<String, Vec<CrateResultHTML>>,
tree: IndexMap<String, Vec<CrateResultHTML<'a>>>,
},
RootResults {
count: u32,
results: IndexMap<String, Vec<CrateResultHTML>>,
results: IndexMap<String, Vec<CrateResultHTML<'a>>>,
},
}

Expand Down Expand Up @@ -61,13 +65,13 @@ impl CurrentPage {
struct ResultsContext<'a> {
ex: &'a Experiment,
nav: Vec<NavbarItem>,
categories: Vec<(Comparison, ReportCratesHTML)>,
// (comparison, category color, ...)
categories: Vec<(Comparison, usize, ReportCratesHTML<'a>)>,
info: IndexMap<Comparison, u32>,
full: bool,
crates_count: usize,
comparison_colors: IndexMap<Comparison, Color>,
result_colors: Vec<Color>,
result_names: Vec<String>,
colors: IndexSet<Color>,
result_names: IndexSet<String>,
}

#[derive(Serialize)]
Expand All @@ -80,20 +84,52 @@ struct DownloadsContext<'a> {
}

#[derive(Serialize)]
struct CrateResultHTML {
name: String,
url: String,
struct CrateResultHTML<'a> {
name: &'a str,
url: &'a str,
res: Comparison,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<String>,
runs: [Option<BuildTestResultHTML>; 2],
status: Option<CrateVersionStatus>,
color_idx: usize,
runs: [Option<BuildTestResultHTML<'a>>; 2],
}

// Map TestResult to usize to avoid the presence of special characters in html
#[derive(Serialize)]
struct BuildTestResultHTML {
res: usize,
log: String,
struct BuildTestResultHTML<'a> {
color_idx: usize,
name_idx: usize,
log: &'a str,
}

fn to_html_crate_result<'a>(
colors: &mut IndexSet<Color>,
result_names: &mut IndexSet<String>,
category_color: usize,
result: &'a CrateResult,
) -> CrateResultHTML<'a> {
let mut runs = [None, None];

for (pos, run) in result.runs.iter().enumerate() {
if let Some(run) = run {
let (color_idx, _) = colors.insert_full(run.res.color());
let (name_idx, _) = result_names.insert_full(run.res.short_name());
runs[pos] = Some(BuildTestResultHTML {
color_idx,
name_idx,
log: run.log.as_str(),
});
}
}

CrateResultHTML {
name: result.name.as_str(),
url: result.url.as_str(),
status: result.status,
res: result.res,
color_idx: category_color,
runs,
}
}

fn write_report<W: ReportWriter>(
Expand All @@ -105,54 +141,37 @@ fn write_report<W: ReportWriter>(
dest: &W,
output_templates: bool,
) -> Fallible<()> {
let mut comparison_colors = IndexMap::new();
let mut test_results_to_int = IndexMap::new();
let mut result_colors = Vec::new();
let mut result_names = Vec::new();
let mut colors = IndexSet::new();
let mut result_names = IndexSet::new();

let mut to_html_crate_result = |result: CrateResult| {
let mut runs = [None, None];

for (pos, run) in result.runs.iter().enumerate() {
if let Some(ref run) = run {
let idx = test_results_to_int
.entry(run.res.clone())
.or_insert_with(|| {
result_colors.push(run.res.color());
result_names.push(run.res.short_name());
result_names.len() - 1
});
runs[pos] = Some(BuildTestResultHTML {
res: *idx,
log: run.log.clone(),
});
}
}

CrateResultHTML {
name: result.name.clone(),
url: result.url.clone(),
status: result.status.map(|status| status.to_string()),
res: result.res,
runs,
}
};
let color_for_category = res
.categories
.keys()
.map(|category| (category.color(), colors.insert_full(category.color()).0))
.collect::<HashMap<_, _>>();

let categories = res
.categories
.iter()
.filter(|(category, _)| full || category.show_in_summary())
.map(|(&category, crates)| (category, crates.to_owned()))
.map(|(&category, crates)| (category, crates))
.flat_map(|(category, crates)| {
comparison_colors.insert(category, category.color());

let category_color_idx = *color_for_category.get(&category.color()).unwrap();
match crates {
ReportCrates::Plain(crates) => vec![(
category,
category_color_idx,
ReportCratesHTML::Plain(
crates
.into_iter()
.map(|result| to_html_crate_result(result))
.iter()
.map(|result| {
to_html_crate_result(
&mut colors,
&mut result_names,
category_color_idx,
result,
)
})
.collect::<Vec<_>>(),
),
)]
Expand All @@ -163,8 +182,15 @@ fn write_report<W: ReportWriter>(
.map(|(root, deps)| {
(
root.to_string(),
deps.into_iter()
.map(|result| to_html_crate_result(result))
deps.iter()
.map(|result| {
to_html_crate_result(
&mut colors,
&mut result_names,
category_color_idx,
result,
)
})
.collect::<Vec<_>>(),
)
})
Expand All @@ -175,8 +201,15 @@ fn write_report<W: ReportWriter>(
(
res.long_name(),
krates
.into_iter()
.map(|result| to_html_crate_result(result))
.iter()
.map(|result| {
to_html_crate_result(
&mut colors,
&mut result_names,
category_color_idx,
result,
)
})
.collect::<Vec<_>>(),
)
})
Expand All @@ -185,13 +218,15 @@ fn write_report<W: ReportWriter>(
vec![
(
category,
category_color_idx,
ReportCratesHTML::Tree {
count: tree.keys().len() as u32,
tree,
},
),
(
category,
category_color_idx,
ReportCratesHTML::RootResults {
count: results.keys().len() as u32,
results,
Expand All @@ -216,13 +251,14 @@ fn write_report<W: ReportWriter>(
info: res.info.clone(),
full,
crates_count,
comparison_colors,
result_colors,
colors,
result_names,
};

info!("generating {}", to);
let html = minifier::html::minify(&assets::render_template("report/results.html", &context)?);
let rendered = assets::render_template("report/results.html", &context)
.context("rendering template report/results.html")?;
let html = minifier::html::minify(&rendered);
dest.write_string(to, html.into(), &mime::TEXT_HTML)?;

if output_templates {
Expand Down
6 changes: 3 additions & 3 deletions templates/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
{% for run in crate.runs %}
<span class="run">
{% if run %}
<b class="r{{ run.res }}"></b>
<a href="{{ run.log|safe }}/log.txt">{{ result_names[run.res] }}</a>
<b class="c{{ run.color_idx }}"></b>
<a href="{{ run.log|safe }}/log.txt">{{ result_names[run.name_idx] }}</a>
{% else %}
<b class="c{{ crate.res }}"></b>
<b class="c{{ crate.color_idx }}"></b>
{{ crate.res }}
{% endif %}
</span>
Expand Down
27 changes: 9 additions & 18 deletions templates/report/results.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,8 @@

{% block extra_head %}
<style>
{% for color in result_colors %}
.r{{ loop.index0 }} {
{% if color.Single %}
background: {{ color.Single }};
{% elif color.Striped %}
background: repeating-linear-gradient(-45deg, {{ color.Striped[0] }}, {{ color.Striped[0] }} 15px, {{ color.Striped[1] }} 15px, {{ color.Striped[1] }} 30px);
{% endif %}
}
{% endfor %}

{% for name, color in comparison_colors %}
.c{{ name }} {
{% for color in colors %}
.c{{ loop.index0 }} {
{% if color.Single %}
background: {{ color.Single }};
{% elif color.Striped %}
Expand All @@ -31,10 +21,11 @@
{% if categories %}
{% for iter in categories %}
{% set name = iter.0 %}
{% set crates = iter.1 %}
{% set category_color_idx = iter.1 %}
{% set crates = iter.2 %}
<div class="category">
{% if crates.Plain %}
<div class="header c{{ name }} toggle" data-toggle="#crt-{{ name }}">
<div class="header c{{ category_color_idx }} toggle" data-toggle="#crt-{{ name }}">
{{ name }} ({{ crates.Plain|length }})
</div>
<div class="crates hidden" id="crt-{{ name }}">
Expand All @@ -44,14 +35,14 @@
{% endfor %}
</div>
{% elif crates.Tree and crates.Tree.count > 0 %}
<div class="header c{{ name }} toggle" data-toggle="#crt-{{ name }}-tr">
<div class="header c{{ category_color_idx }} toggle" data-toggle="#crt-{{ name }}-tr">
{{ name }}: dependencies ({{ crates.Tree.count }} root crates, {{info[name]}} {{ name }} crates in total)
</div>
<div class="crates hidden" id="crt-{{ name }}-tr">
{% for root, subcrates in crates.Tree.tree %}
<div class="category">
<div class="flex toggle" data-toggle="#{{ name }}-tr{{ loop.index }}">
<div class="header c{{ name}} subheader">{{ name}}</div>
<div class="header c{{ category_color_idx }} subheader">{{ name}}</div>
<div class="header header-background">
{{ root }} ({{ subcrates|length }})
</div>
Expand All @@ -66,14 +57,14 @@
{% endfor %}
</div>
{% elif crates.RootResults %}
<div class="header c{{ name }} toggle" data-toggle="#crt-{{ name }}-rt">
<div class="header c{{ category_color_idx }} toggle" data-toggle="#crt-{{ name }}-rt">
{{ name }}: root results ({{ crates.RootResults.count }} different results, {{info[name]}} {{ name }} crates in total)
</div>
<div class="crates hidden" id="crt-{{ name }}-rt">
{% for result, subcrates in crates.RootResults.results %}
<div class="category">
<div class="flex toggle" data-toggle="#{{ name }}-rt{{ loop.index }}">
<div class="header c{{ name}} subheader">{{ name}}</div>
<div class="header c{{ category_color_idx }} subheader">{{ name}}</div>
<div class="header header-background">
{{ result }} ({{ subcrates|length }})
</div>
Expand Down
Loading

0 comments on commit ffb0b6a

Please sign in to comment.