dcc92be7ad
- Add button to select top-10 instance types per category - Right align category selection buttons - Lazily draw the graph for snappier UI - Pre-init instance variables in details-selection Change-Id: I61ea80d523c49215b9d418e66698a12cbc050316 No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2210681 Commit-Queue: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Cr-Commit-Position: refs/heads/master@{#67950}
228 lines
7.0 KiB
JavaScript
228 lines
7.0 KiB
JavaScript
// Copyright 2018 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
'use strict';
|
|
|
|
import {
|
|
VIEW_BY_INSTANCE_TYPE,
|
|
VIEW_BY_INSTANCE_CATEGORY,
|
|
VIEW_BY_FIELD_TYPE
|
|
} from './details-selection.js';
|
|
|
|
defineCustomElement('global-timeline', (templateText) =>
|
|
class GlobalTimeline extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
const shadowRoot = this.attachShadow({mode: 'open'});
|
|
shadowRoot.innerHTML = templateText;
|
|
}
|
|
|
|
$(id) {
|
|
return this.shadowRoot.querySelector(id);
|
|
}
|
|
|
|
set data(value) {
|
|
this._data = value;
|
|
this.stateChanged();
|
|
}
|
|
|
|
get data() {
|
|
return this._data;
|
|
}
|
|
|
|
set selection(value) {
|
|
this._selection = value;
|
|
this.stateChanged();
|
|
}
|
|
|
|
get selection() {
|
|
return this._selection;
|
|
}
|
|
|
|
isValid() {
|
|
return this.data && this.selection;
|
|
}
|
|
|
|
hide() {
|
|
this.$('#container').style.display = 'none';
|
|
}
|
|
|
|
show() {
|
|
this.$('#container').style.display = 'block';
|
|
}
|
|
|
|
stateChanged() {
|
|
if (this.isValid()) {
|
|
this.drawChart();
|
|
} else {
|
|
this.hide();
|
|
}
|
|
}
|
|
|
|
getFieldData() {
|
|
const labels = [
|
|
{type: 'number', label: 'Time'},
|
|
{type: 'number', label: 'Ptr compression benefit'},
|
|
{type: 'string', role: 'tooltip'},
|
|
{type: 'number', label: 'Embedder fields'},
|
|
{type: 'number', label: 'Tagged fields (excl. in-object Smis)'},
|
|
{type: 'number', label: 'In-object Smi-only fields'},
|
|
{type: 'number', label: 'Other raw fields'},
|
|
{type: 'number', label: 'Unboxed doubles'},
|
|
{type: 'number', label: 'Boxed doubles'},
|
|
{type: 'number', label: 'String data'}
|
|
];
|
|
const chart_data = [labels];
|
|
const isolate_data = this.data[this.selection.isolate];
|
|
let sum_total = 0;
|
|
let sum_ptr_compr_benefit_perc = 0;
|
|
let count = 0;
|
|
Object.keys(isolate_data.gcs).forEach(gc_key => {
|
|
const gc_data = isolate_data.gcs[gc_key];
|
|
const data_set = gc_data[this.selection.data_set].field_data;
|
|
const data = [];
|
|
data.push(gc_data.time * kMillis2Seconds);
|
|
const total = data_set.tagged_fields +
|
|
data_set.inobject_smi_fields +
|
|
data_set.embedder_fields +
|
|
data_set.other_raw_fields +
|
|
data_set.unboxed_double_fields +
|
|
data_set.boxed_double_fields +
|
|
data_set.string_data;
|
|
const ptr_compr_benefit =
|
|
(data_set.inobject_smi_fields + data_set.tagged_fields) / 2;
|
|
const ptr_compr_benefit_perc = ptr_compr_benefit / total * 100;
|
|
sum_total += total;
|
|
sum_ptr_compr_benefit_perc += ptr_compr_benefit_perc;
|
|
count++;
|
|
const tooltip = "Ptr compression benefit: " +
|
|
(ptr_compr_benefit / KB).toFixed(2) + "KB " +
|
|
" (" + ptr_compr_benefit_perc.toFixed(2) + "%)";
|
|
data.push(ptr_compr_benefit / KB);
|
|
data.push(tooltip);
|
|
data.push(data_set.embedder_fields / KB);
|
|
data.push(data_set.tagged_fields / KB);
|
|
data.push(data_set.inobject_smi_fields / KB);
|
|
data.push(data_set.other_raw_fields / KB);
|
|
data.push(data_set.unboxed_double_fields / KB);
|
|
data.push(data_set.boxed_double_fields / KB);
|
|
data.push(data_set.string_data / KB);
|
|
chart_data.push(data);
|
|
});
|
|
const avg_ptr_compr_benefit_perc =
|
|
count ? sum_ptr_compr_benefit_perc / count : 0;
|
|
console.log("==================================================");
|
|
console.log("= Average ptr compression benefit is " +
|
|
avg_ptr_compr_benefit_perc.toFixed(2) + "%");
|
|
console.log("= Average V8 heap size " +
|
|
(sum_total / count / KB).toFixed(2) + " KB");
|
|
console.log("==================================================");
|
|
return chart_data;
|
|
}
|
|
|
|
getCategoryData() {
|
|
const categories = Object.keys(this.selection.categories)
|
|
.map(k => this.selection.category_names.get(k));
|
|
const labels = ['Time', ...categories];
|
|
const chart_data = [labels];
|
|
const isolate_data = this.data[this.selection.isolate];
|
|
Object.keys(isolate_data.gcs).forEach(gc_key => {
|
|
const gc_data = isolate_data.gcs[gc_key];
|
|
const data_set = gc_data[this.selection.data_set].instance_type_data;
|
|
const data = [];
|
|
data.push(gc_data.time * kMillis2Seconds);
|
|
Object.values(this.selection.categories).forEach(instance_types => {
|
|
data.push(
|
|
instance_types
|
|
.map(instance_type => {
|
|
return data_set[instance_type].overall;
|
|
})
|
|
.reduce((accu, current) => accu + current, 0) /
|
|
KB);
|
|
});
|
|
chart_data.push(data);
|
|
});
|
|
return chart_data;
|
|
}
|
|
|
|
getInstanceTypeData() {
|
|
const instance_types =
|
|
Object.values(this.selection.categories)
|
|
.reduce((accu, current) => accu.concat(current), []);
|
|
const labels = ['Time', ...instance_types];
|
|
const chart_data = [labels];
|
|
const isolate_data = this.data[this.selection.isolate];
|
|
Object.keys(isolate_data.gcs).forEach(gc_key => {
|
|
const gc_data = isolate_data.gcs[gc_key];
|
|
const data_set = gc_data[this.selection.data_set].instance_type_data;
|
|
const data = [];
|
|
data.push(gc_data.time * kMillis2Seconds);
|
|
instance_types.forEach(instance_type => {
|
|
data.push(data_set[instance_type].overall / KB);
|
|
});
|
|
chart_data.push(data);
|
|
});
|
|
return chart_data;
|
|
}
|
|
|
|
getChartData() {
|
|
switch (this.selection.data_view) {
|
|
case VIEW_BY_FIELD_TYPE:
|
|
return this.getFieldData();
|
|
case VIEW_BY_INSTANCE_CATEGORY:
|
|
return this.getCategoryData();
|
|
case VIEW_BY_INSTANCE_TYPE:
|
|
default:
|
|
return this.getInstanceTypeData();
|
|
}
|
|
}
|
|
|
|
getChartOptions() {
|
|
const options = {
|
|
isStacked: true,
|
|
hAxis: {
|
|
format: '###.##s',
|
|
title: 'Time [s]',
|
|
},
|
|
vAxis: {
|
|
format: '#,###KB',
|
|
title: 'Memory consumption [KBytes]'
|
|
},
|
|
chartArea: {left:100, width: '85%', height: '70%'},
|
|
legend: {position: 'top', maxLines: '1'},
|
|
pointsVisible: true,
|
|
pointSize: 5,
|
|
explorer: {},
|
|
};
|
|
switch (this.selection.data_view) {
|
|
case VIEW_BY_FIELD_TYPE:
|
|
// Overlay pointer compression benefit on top of the graph
|
|
return Object.assign(options, {
|
|
series: {0: {type: 'line', lineDashStyle: [13, 13]}},
|
|
});
|
|
case VIEW_BY_INSTANCE_CATEGORY:
|
|
case VIEW_BY_INSTANCE_TYPE:
|
|
default:
|
|
return options;
|
|
}
|
|
}
|
|
|
|
drawChart() {
|
|
setTimeout(() => this._drawChart(), 10);
|
|
}
|
|
|
|
_drawChart() {
|
|
console.assert(this.data, 'invalid data');
|
|
console.assert(this.selection, 'invalid selection');
|
|
|
|
const chart_data = this.getChartData();
|
|
|
|
const data = google.visualization.arrayToDataTable(chart_data);
|
|
const options = this.getChartOptions();
|
|
const chart = new google.visualization.AreaChart(this.$('#chart'));
|
|
this.show();
|
|
chart.draw(data, google.charts.Line.convertOptions(options));
|
|
}
|
|
});
|