Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid extra allocations while generating HTML reports #717

Merged
merged 1 commit into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading