mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
dynamic updates of visualizations to avoid OOM
This commit is contained in:
parent
7623b5becd
commit
97fda7f23b
@ -7,7 +7,8 @@ export class MergeTree {
|
|||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
this.time = 0;
|
this.time = 0;
|
||||||
this.inserted_parts_count = 0;
|
this.total_part_count = 0;
|
||||||
|
this.inserted_part_count = 0;
|
||||||
this.inserted_bytes = 0;
|
this.inserted_bytes = 0;
|
||||||
this.written_bytes = 0; // inserts + merges
|
this.written_bytes = 0; // inserts + merges
|
||||||
this.inserted_utility = 0; // utility = size * log(size)
|
this.inserted_utility = 0; // utility = size * log(size)
|
||||||
@ -49,14 +50,15 @@ export class MergeTree {
|
|||||||
let log_bytes = Math.log2(bytes);
|
let log_bytes = Math.log2(bytes);
|
||||||
let utility = bytes * log_bytes;
|
let utility = bytes * log_bytes;
|
||||||
let result = {
|
let result = {
|
||||||
|
id: this.total_part_count,
|
||||||
bytes,
|
bytes,
|
||||||
log_bytes,
|
log_bytes,
|
||||||
utility,
|
utility,
|
||||||
entropy: 0,
|
entropy: 0,
|
||||||
created: now,
|
created: now,
|
||||||
level: 0,
|
level: 0,
|
||||||
begin: this.inserted_parts_count,
|
begin: this.inserted_part_count,
|
||||||
end: this.inserted_parts_count + 1,
|
end: this.inserted_part_count + 1,
|
||||||
left_bytes: this.inserted_bytes,
|
left_bytes: this.inserted_bytes,
|
||||||
right_bytes: this.inserted_bytes + bytes,
|
right_bytes: this.inserted_bytes + bytes,
|
||||||
is_leftmost: false,
|
is_leftmost: false,
|
||||||
@ -68,7 +70,8 @@ export class MergeTree {
|
|||||||
};
|
};
|
||||||
this.parts.push(result);
|
this.parts.push(result);
|
||||||
this.active_part_count++;
|
this.active_part_count++;
|
||||||
this.inserted_parts_count++;
|
this.inserted_part_count++;
|
||||||
|
this.total_part_count++;
|
||||||
this.inserted_bytes += bytes;
|
this.inserted_bytes += bytes;
|
||||||
this.written_bytes += bytes;
|
this.written_bytes += bytes;
|
||||||
this.inserted_utility += utility;
|
this.inserted_utility += utility;
|
||||||
@ -123,6 +126,7 @@ export class MergeTree {
|
|||||||
const entropy = (utility - utility0) / bytes;
|
const entropy = (utility - utility0) / bytes;
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
|
id: this.total_part_count,
|
||||||
bytes,
|
bytes,
|
||||||
log_bytes,
|
log_bytes,
|
||||||
utility,
|
utility,
|
||||||
@ -140,6 +144,7 @@ export class MergeTree {
|
|||||||
};
|
};
|
||||||
this.parts.push(result);
|
this.parts.push(result);
|
||||||
this.active_part_count++;
|
this.active_part_count++;
|
||||||
|
this.total_part_count++;
|
||||||
for (let p of parts_to_merge)
|
for (let p of parts_to_merge)
|
||||||
{
|
{
|
||||||
if (p.active == false)
|
if (p.active == false)
|
||||||
|
@ -26,7 +26,7 @@ export class MergeTreeInserter
|
|||||||
case 'insert':
|
case 'insert':
|
||||||
const part = this.mt.insertPart(value.bytes);
|
const part = this.mt.insertPart(value.bytes);
|
||||||
if (this.signals.on_insert)
|
if (this.signals.on_insert)
|
||||||
this.signals.on_insert({sim: this.sim, mt: this.mt, part});
|
await this.signals.on_insert({sim: this.sim, mt: this.mt, part});
|
||||||
break;
|
break;
|
||||||
case 'sleep':
|
case 'sleep':
|
||||||
if (value.delay > 0)
|
if (value.delay > 0)
|
||||||
|
@ -38,7 +38,7 @@ export class MergeTreeMerger
|
|||||||
const {parts_to_merge} = value;
|
const {parts_to_merge} = value;
|
||||||
this.#beginMerge(parts_to_merge);
|
this.#beginMerge(parts_to_merge);
|
||||||
if (this.signals.on_merge_begin)
|
if (this.signals.on_merge_begin)
|
||||||
this.signals.on_merge_begin({sim: this.sim, mt: this.mt, parts_to_merge});
|
await this.signals.on_merge_begin({sim: this.sim, mt: this.mt, parts_to_merge});
|
||||||
break;
|
break;
|
||||||
case 'wait':
|
case 'wait':
|
||||||
if (this.merges_running == 0)
|
if (this.merges_running == 0)
|
||||||
@ -64,16 +64,16 @@ export class MergeTreeMerger
|
|||||||
const bytes = parts_to_merge.reduce((sum, d) => sum + d.bytes, 0);
|
const bytes = parts_to_merge.reduce((sum, d) => sum + d.bytes, 0);
|
||||||
const mergeDuration = this.mt.mergeDuration(bytes, parts_to_merge.length);
|
const mergeDuration = this.mt.mergeDuration(bytes, parts_to_merge.length);
|
||||||
|
|
||||||
// Create a Promise that will be resolved on merge finish
|
// Schedule merge finish event
|
||||||
return this.pool.schedule(mergeDuration, "MergeEnd", (sim, event) => this.#onMergeEnd(parts_to_merge));
|
return this.pool.schedule(mergeDuration, "MergeEnd", async (sim, event) => await this.#onMergeEnd(parts_to_merge));
|
||||||
}
|
}
|
||||||
|
|
||||||
#onMergeEnd(parts_to_merge)
|
async #onMergeEnd(parts_to_merge)
|
||||||
{
|
{
|
||||||
this.mt.advanceTime(this.sim.time);
|
this.mt.advanceTime(this.sim.time);
|
||||||
let part = this.mt.finishMergeParts(parts_to_merge);
|
let part = this.mt.finishMergeParts(parts_to_merge);
|
||||||
if (this.signals.on_merge_end)
|
if (this.signals.on_merge_end)
|
||||||
this.signals.on_merge_end({sim: this.sim, mt: this.mt, part, parts_to_merge});
|
await this.signals.on_merge_end({sim: this.sim, mt: this.mt, part, parts_to_merge});
|
||||||
this.merges_running--;
|
this.merges_running--;
|
||||||
this.#iterateSelector();
|
this.#iterateSelector();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,35 @@
|
|||||||
import { valueToColor, formatBytesWithUnit, determineTickStep } from './visualizeHelpers.js';
|
import { valueToColor, formatBytesWithUnit, determineTickStep } from './visualizeHelpers.js';
|
||||||
import { infoButton } from './infoButton.js';
|
import { infoButton } from './infoButton.js';
|
||||||
|
|
||||||
|
function formatNumber(number)
|
||||||
|
{
|
||||||
|
if (number >= Math.pow(1000, 4))
|
||||||
|
return (number / Math.pow(1000, 4)).toFixed(1) + 'T';
|
||||||
|
else if (number >= Math.pow(1000, 3))
|
||||||
|
return (number / Math.pow(1000, 3)).toFixed(1) + 'G';
|
||||||
|
else if (number >= Math.pow(1000, 2))
|
||||||
|
return (number / Math.pow(1000, 2)).toFixed(1) + 'M';
|
||||||
|
else if (number >= 1000)
|
||||||
|
return (number / 1000).toFixed(1) + 'K';
|
||||||
|
else
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBytes(bytes)
|
||||||
|
{
|
||||||
|
const digits = 0;
|
||||||
|
if (bytes >= Math.pow(1024, 4))
|
||||||
|
return (bytes / Math.pow(1024, 4)).toFixed(digits) + 'TB';
|
||||||
|
else if (bytes >= Math.pow(1024, 3))
|
||||||
|
return (bytes / Math.pow(1024, 3)).toFixed(digits) + 'GB';
|
||||||
|
else if (bytes >= Math.pow(1024, 2))
|
||||||
|
return (bytes / Math.pow(1024, 2)).toFixed(digits) + 'MB';
|
||||||
|
else if (bytes >= 1024)
|
||||||
|
return (bytes / 1024).toFixed(digits) + 'KB';
|
||||||
|
else
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
class MergeTreeVisualizer {
|
class MergeTreeVisualizer {
|
||||||
getMargin() { return { left: 50, right: 40, top: 60, bottom: 60 }; }
|
getMargin() { return { left: 50, right: 40, top: 60, bottom: 60 }; }
|
||||||
|
|
||||||
@ -50,15 +79,8 @@ class MergeTreeVisualizer {
|
|||||||
this.max_source_part_count = d3.max(mt.parts, d => d.source_part_count);
|
this.max_source_part_count = d3.max(mt.parts, d => d.source_part_count);
|
||||||
|
|
||||||
// Compute scale ranges
|
// Compute scale ranges
|
||||||
this.minXValue = d3.min(mt.parts, d => this.getLeft(d));
|
this.computeXAggregates(mt);
|
||||||
this.maxXValue = d3.max(mt.parts, d => this.getRight(d));
|
this.computeYAggregates(mt);
|
||||||
if (this.isYAxisReversed()) {
|
|
||||||
this.minYValue = d3.min(mt.parts, d => this.getTop(d));
|
|
||||||
this.maxYValue = d3.max(mt.parts, d => this.getBottom(d));
|
|
||||||
} else {
|
|
||||||
this.minYValue = d3.min(mt.parts, d => this.getBottom(d));
|
|
||||||
this.maxYValue = d3.max(mt.parts, d => this.getTop(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the SVG container
|
// Create the SVG container
|
||||||
this.svgContainer = container
|
this.svgContainer = container
|
||||||
@ -67,11 +89,28 @@ class MergeTreeVisualizer {
|
|||||||
.attr("height", this.svgHeight);
|
.attr("height", this.svgHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computeXAggregates(mt) {
|
||||||
|
this.minXValue = d3.min(mt.parts, d => this.getLeft(d));
|
||||||
|
this.maxXValue = d3.max(mt.parts, d => this.getRight(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
computeYAggregates(mt) {
|
||||||
|
if (this.isYAxisReversed()) {
|
||||||
|
this.minYValue = d3.min(mt.parts, d => this.getTop(d));
|
||||||
|
this.maxYValue = d3.max(mt.parts, d => this.getBottom(d));
|
||||||
|
} else {
|
||||||
|
this.minYValue = d3.min(mt.parts, d => this.getBottom(d));
|
||||||
|
this.maxYValue = d3.max(mt.parts, d => this.getTop(d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initXScaleLinear() {
|
initXScaleLinear() {
|
||||||
// Set up the horizontal scale (x-axis) — linear scale
|
// Set up the horizontal scale (x-axis) — linear scale
|
||||||
this.xScale = d3.scaleLinear()
|
this.xScale = d3.scaleLinear()
|
||||||
.domain([this.minXValue, this.maxXValue])
|
|
||||||
.range([this.margin.left, this.svgWidth - this.margin.right]);
|
.range([this.margin.left, this.svgWidth - this.margin.right]);
|
||||||
|
|
||||||
|
this.updateXDomain = () => this.xScale.domain([this.minXValue, this.maxXValue]);
|
||||||
|
this.updateXDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
getYRange() {
|
getYRange() {
|
||||||
@ -83,25 +122,33 @@ class MergeTreeVisualizer {
|
|||||||
// Set up the vertical scale (y-axis) — logarithmic scale
|
// Set up the vertical scale (y-axis) — logarithmic scale
|
||||||
this.yScale = d3.scaleLog()
|
this.yScale = d3.scaleLog()
|
||||||
.base(2)
|
.base(2)
|
||||||
.domain([Math.max(1, this.minYValue), Math.pow(2, Math.ceil(Math.log2(this.maxYValue)))])
|
|
||||||
.range(this.getYRange());
|
.range(this.getYRange());
|
||||||
|
|
||||||
|
this.updateYDomain = () => this.yScale.domain([Math.max(1, this.minYValue), Math.pow(2, Math.ceil(Math.log2(this.maxYValue)))])
|
||||||
|
this.updateYDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
initYScaleLinear() {
|
initYScaleLinear() {
|
||||||
// Set up the vertical scale (y-axis) — linear scale
|
// Set up the vertical scale (y-axis) — linear scale
|
||||||
this.yScale = d3.scaleLinear()
|
this.yScale = d3.scaleLinear()
|
||||||
.domain([this.minYValue, this.maxYValue])
|
|
||||||
.range(this.getYRange());
|
.range(this.getYRange());
|
||||||
|
|
||||||
|
this.updateYDomain = () => this.yScale.domain([this.minYValue, this.maxYValue])
|
||||||
|
this.updateYDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
createXAxisLinear() {
|
createXAxisLinear() {
|
||||||
this.xAxis = this.isYAxisReversed() ? d3.axisTop(this.xScale) : d3.axisBottom(this.xScale);
|
this.xAxisFormatter = formatBytes; // formatBytesWithUnit;
|
||||||
|
this.xAxisType = this.isYAxisReversed() ? d3.axisTop : d3.axisBottom;
|
||||||
|
|
||||||
const tickStep = determineTickStep(this.maxXValue);
|
const tickStep = determineTickStep(this.maxXValue - this.minXValue);
|
||||||
const translateY = this.isYAxisReversed() ? this.margin.top : this.svgHeight - this.margin.bottom;
|
const translateY = this.isYAxisReversed() ? this.margin.top : this.svgHeight - this.margin.bottom;
|
||||||
this.svgContainer.append("g")
|
this.xAxis = this.xAxisType(this.xScale)
|
||||||
|
.tickValues(d3.range(this.minXValue, this.maxXValue, tickStep))
|
||||||
|
.tickFormat(this.xAxisFormatter);
|
||||||
|
this.xAxisGroup = this.svgContainer.append("g")
|
||||||
.attr("transform", `translate(0, ${translateY})`)
|
.attr("transform", `translate(0, ${translateY})`)
|
||||||
.call(this.xAxis.tickValues(d3.range(0, this.maxXValue, tickStep)).tickFormat(formatBytesWithUnit));
|
.call(this.xAxis);
|
||||||
|
|
||||||
// Add axis title
|
// Add axis title
|
||||||
this.svgContainer.append("text")
|
this.svgContainer.append("text")
|
||||||
@ -113,11 +160,14 @@ class MergeTreeVisualizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createYAxisPowersOfTwo() {
|
createYAxisPowersOfTwo() {
|
||||||
|
this.yAxisFormatter = formatBytes; // d => `2^${Math.log2(d)}`;
|
||||||
|
this.yAxisType = d3.axisLeft;
|
||||||
|
|
||||||
const powersOfTwo = Array.from({ length: 50 }, (v, i) => Math.pow(2, i + 1));
|
const powersOfTwo = Array.from({ length: 50 }, (v, i) => Math.pow(2, i + 1));
|
||||||
this.yAxis = d3.axisLeft(this.yScale)
|
this.yAxis = this.yAxisType(this.yScale)
|
||||||
.tickValues(powersOfTwo.filter(d => d >= this.minYValue && d <= this.maxYValue))
|
.tickValues(powersOfTwo.filter(d => d >= this.minYValue && d <= this.maxYValue))
|
||||||
.tickFormat(d => `2^${Math.log2(d)}`);
|
.tickFormat(this.yAxisFormatter);
|
||||||
const yAxisGroup = this.svgContainer.append("g")
|
this.yAxisGroup = this.svgContainer.append("g")
|
||||||
.attr("transform", `translate(${this.margin.left}, 0)`)
|
.attr("transform", `translate(${this.margin.left}, 0)`)
|
||||||
.call(this.yAxis);
|
.call(this.yAxis);
|
||||||
|
|
||||||
@ -130,23 +180,26 @@ class MergeTreeVisualizer {
|
|||||||
.attr("font-size", "14px")
|
.attr("font-size", "14px")
|
||||||
.text("Log(PartSize)");
|
.text("Log(PartSize)");
|
||||||
|
|
||||||
yAxisGroup.selectAll(".tick text")
|
// This does not work with updating, switched to formatBytes
|
||||||
.each(function(d) {
|
// this.yAxisGroup.selectAll(".tick text")
|
||||||
const exponent = Math.log2(d);
|
// .each(function(d) {
|
||||||
const self = d3.select(this);
|
// const exponent = Math.log2(d);
|
||||||
self.text("");
|
// const self = d3.select(this);
|
||||||
self.append("tspan").text("2");
|
// self.text("");
|
||||||
self.append("tspan")
|
// self.append("tspan").text("2");
|
||||||
.attr("dy", "-0.7em")
|
// self.append("tspan")
|
||||||
.attr("font-size", "70%").text(exponent);
|
// .attr("dy", "-0.7em")
|
||||||
});
|
// .attr("font-size", "70%").text(exponent);
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
createYAxisLinear() {
|
createYAxisLinear() {
|
||||||
this.yAxis = d3.axisLeft(this.yScale)
|
this.yAxisFormatter = formatNumber;
|
||||||
|
this.yAxisType = d3.axisLeft;
|
||||||
|
this.yAxis = this.yAxisType(this.yScale)
|
||||||
.tickArguments([5])
|
.tickArguments([5])
|
||||||
.tickFormat(d => Number.isInteger(d) ? d : "");
|
.tickFormat(this.yAxisFormatter);
|
||||||
this.svgContainer.append("g")
|
this.yAxisGroup = this.svgContainer.append("g")
|
||||||
.attr("transform", `translate(${this.margin.left}, 0)`)
|
.attr("transform", `translate(${this.margin.left}, 0)`)
|
||||||
.call(this.yAxis);
|
.call(this.yAxis);
|
||||||
|
|
||||||
@ -160,6 +213,26 @@ class MergeTreeVisualizer {
|
|||||||
.text("Source parts count");
|
.text("Source parts count");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateX(mt) {
|
||||||
|
// Rescale axis
|
||||||
|
this.computeXAggregates(mt);
|
||||||
|
this.updateXDomain();
|
||||||
|
|
||||||
|
// Update axes with transitions
|
||||||
|
this.xAxisGroup.transition() // .duration(1000)
|
||||||
|
.call(this.xAxisType(this.xScale).tickFormat(this.xAxisFormatter));
|
||||||
|
};
|
||||||
|
|
||||||
|
updateY(mt) {
|
||||||
|
// Rescale axis
|
||||||
|
this.computeYAggregates(mt);
|
||||||
|
this.updateYDomain();
|
||||||
|
|
||||||
|
// Update axes with transitions
|
||||||
|
this.yAxisGroup.transition() // .duration(1000)
|
||||||
|
.call(this.yAxisType(this.yScale).tickFormat(this.yAxisFormatter));
|
||||||
|
};
|
||||||
|
|
||||||
createDescription(text, x = 10, y = 60) {
|
createDescription(text, x = 10, y = 60) {
|
||||||
this.svgContainer.node().__tippy = infoButton(this.svgContainer, x, y, text);
|
this.svgContainer.node().__tippy = infoButton(this.svgContainer, x, y, text);
|
||||||
}
|
}
|
||||||
@ -169,44 +242,90 @@ class MergeTreeVisualizer {
|
|||||||
pxt(value) { return value; }
|
pxt(value) { return value; }
|
||||||
pxb(value) { return Math.max(1, value); }
|
pxb(value) { return Math.max(1, value); }
|
||||||
|
|
||||||
createMerges(mt) {
|
processMerges(mt) {
|
||||||
// Append rectangles for merges
|
// Check if the merges group already exists, create it if not, and save the reference
|
||||||
this.svgContainer.append("g").attr("class", "viz-merge").selectAll("rect")
|
if (!this.mergesGroup) {
|
||||||
.data(mt.parts)
|
this.mergesGroup = this.svgContainer.append("g").attr("class", "viz-merge");
|
||||||
.enter()
|
}
|
||||||
.filter(d => !d.active)
|
|
||||||
.append("rect")
|
// Filter inactive parts and join the data to the rectangles
|
||||||
|
const inactiveParts = mt.parts.filter(d => !d.active);
|
||||||
|
const merges = this.mergesGroup.selectAll("rect")
|
||||||
|
.data(inactiveParts, d => d.id); // Assuming each part has a unique 'id'
|
||||||
|
|
||||||
|
// Handle the enter phase for new elements
|
||||||
|
const mergesEnter = merges.enter().append("rect");
|
||||||
|
|
||||||
|
// Merge the enter and update selections, and set attributes for both
|
||||||
|
mergesEnter.merge(merges)
|
||||||
.attr("x", d => this.pxl(this.getMergeLeft(d)))
|
.attr("x", d => this.pxl(this.getMergeLeft(d)))
|
||||||
.attr("y", d => this.pxt(this.getMergeTop(d)))
|
.attr("y", d => this.pxt(this.getMergeTop(d)))
|
||||||
.attr("width", d => this.pxr(this.getMergeRight(d) - this.getMergeLeft(d)))
|
.attr("width", d => this.pxr(this.getMergeRight(d) - this.getMergeLeft(d)))
|
||||||
.attr("height", d => this.pxb(this.getMergeBottom(d) - this.getMergeTop(d)))
|
.attr("height", d => this.pxb(this.getMergeBottom(d) - this.getMergeTop(d)))
|
||||||
.attr("fill", d => this.getMergeColor(d));
|
.attr("fill", d => this.getMergeColor(d));
|
||||||
|
|
||||||
|
// Handle the exit phase for removed elements
|
||||||
|
merges.exit().remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
createParts(mt) {
|
processParts(mt) {
|
||||||
// Append rectangles for parts
|
// Check if the parts group already exists, create it if not, and save the reference
|
||||||
this.svgContainer.append("g").attr("class", "viz-part").selectAll("rect")
|
if (!this.partsGroup) {
|
||||||
.data(mt.parts)
|
this.partsGroup = this.svgContainer.append("g").attr("class", "viz-part");
|
||||||
.enter()
|
}
|
||||||
.append("rect")
|
|
||||||
|
// Join the data to the rectangles
|
||||||
|
const parts = this.partsGroup.selectAll("rect")
|
||||||
|
.data(mt.parts, d => d.id); // Assuming each part has a unique 'id'
|
||||||
|
|
||||||
|
// Handle the enter phase for new elements
|
||||||
|
const partsEnter = parts.enter()
|
||||||
|
.append("rect");
|
||||||
|
|
||||||
|
// Merge the enter and update selections, and set attributes for both
|
||||||
|
partsEnter.merge(parts)
|
||||||
.attr("x", d => this.pxl(this.getPartLeft(d)))
|
.attr("x", d => this.pxl(this.getPartLeft(d)))
|
||||||
.attr("y", d => this.pxt(this.getPartTop(d)))
|
.attr("y", d => this.pxt(this.getPartTop(d)))
|
||||||
.attr("width", d => this.pxr(this.getPartRight(d) - this.getPartLeft(d)))
|
.attr("width", d => this.pxr(this.getPartRight(d) - this.getPartLeft(d)))
|
||||||
.attr("height", d => this.pxb(this.getPartBottom(d) - this.getPartTop(d)))
|
.attr("height", d => this.pxb(this.getPartBottom(d) - this.getPartTop(d)))
|
||||||
.attr("fill", d => this.getPartColor(d));
|
.attr("fill", d => this.getPartColor(d));
|
||||||
|
|
||||||
|
// Handle the exit phase for removed elements
|
||||||
|
parts.exit().remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
createPartMarks(mt) {
|
processPartMarks(mt) {
|
||||||
// Append marks for parts begin
|
// Check if the part marks group already exists, create it if not, and save the reference
|
||||||
this.svgContainer.append("g").attr("class", "viz-part-mark").selectAll("rect")
|
if (!this.partMarksGroup) {
|
||||||
.data(mt.parts)
|
this.partMarksGroup = this.svgContainer.append("g").attr("class", "viz-part-mark");
|
||||||
.enter()
|
}
|
||||||
.append("rect")
|
|
||||||
|
// Join the data to the rectangles
|
||||||
|
const partMarks = this.partMarksGroup.selectAll("rect")
|
||||||
|
.data(mt.parts, d => d.id); // Assuming each part has a unique 'id'
|
||||||
|
|
||||||
|
// Handle the enter phase for new elements
|
||||||
|
const partMarksEnter = partMarks.enter().append("rect");
|
||||||
|
|
||||||
|
// Merge the enter and update selections, and set attributes for both
|
||||||
|
partMarksEnter.merge(partMarks)
|
||||||
.attr("x", d => this.pxl(this.getPartLeft(d)))
|
.attr("x", d => this.pxl(this.getPartLeft(d)))
|
||||||
.attr("y", d => this.pxt(this.getPartTop(d)))
|
.attr("y", d => this.pxt(this.getPartTop(d)))
|
||||||
.attr("width", d => this.pxr(Math.min(this.part_mark_width, this.getPartRight(d) - this.getPartLeft(d))))
|
.attr("width", d => this.pxr(Math.min(this.part_mark_width, this.getPartRight(d) - this.getPartLeft(d))))
|
||||||
.attr("height", d => this.pxb(this.getPartBottom(d) - this.getPartTop(d)))
|
.attr("height", d => this.pxb(this.getPartBottom(d) - this.getPartTop(d)))
|
||||||
.attr("fill", d => this.getPartMarkColor(d));
|
.attr("fill", d => this.getPartMarkColor(d));
|
||||||
|
|
||||||
|
// Handle the exit phase for removed elements
|
||||||
|
partMarks.exit().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
update(mt) {
|
||||||
|
this.updateX(mt);
|
||||||
|
this.updateY(mt);
|
||||||
|
|
||||||
|
this.processMerges(mt);
|
||||||
|
this.processParts(mt);
|
||||||
|
this.processPartMarks(mt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,9 +355,9 @@ class MergeTreeUtilityVisualizer extends MergeTreeVisualizer {
|
|||||||
this.initXScaleLinear();
|
this.initXScaleLinear();
|
||||||
this.initYScalePowersOfTwo();
|
this.initYScalePowersOfTwo();
|
||||||
|
|
||||||
this.createMerges(mt);
|
this.processMerges(mt);
|
||||||
this.createParts(mt);
|
this.processParts(mt);
|
||||||
this.createPartMarks(mt);
|
this.processPartMarks(mt);
|
||||||
|
|
||||||
this.createXAxisLinear();
|
this.createXAxisLinear();
|
||||||
this.createYAxisPowersOfTwo();
|
this.createYAxisPowersOfTwo();
|
||||||
@ -322,9 +441,9 @@ class MergeTreeTimeVisualizer extends MergeTreeVisualizer {
|
|||||||
this.initXScaleLinear();
|
this.initXScaleLinear();
|
||||||
this.initYScaleLinear();
|
this.initYScaleLinear();
|
||||||
|
|
||||||
this.createMerges(mt);
|
this.processMerges(mt);
|
||||||
this.createParts(mt);
|
this.processParts(mt);
|
||||||
this.createPartMarks(mt);
|
this.processPartMarks(mt);
|
||||||
|
|
||||||
this.createXAxisLinear();
|
this.createXAxisLinear();
|
||||||
this.createYAxisLinear();
|
this.createYAxisLinear();
|
||||||
|
@ -18,7 +18,7 @@ export class WorkerPool
|
|||||||
// Method to schedule a task
|
// Method to schedule a task
|
||||||
schedule(duration, name, callback)
|
schedule(duration, name, callback)
|
||||||
{
|
{
|
||||||
const task = new Event(name, (sim, event) => this.#finishTask(sim, event, callback));
|
const task = new Event(name, async (sim, event) => await this.#finishTask(sim, event, callback));
|
||||||
task.duration = duration;
|
task.duration = duration;
|
||||||
|
|
||||||
if (this.available_workers > 0) // If a worker is available, start the task immediately
|
if (this.available_workers > 0) // If a worker is available, start the task immediately
|
||||||
@ -36,11 +36,11 @@ export class WorkerPool
|
|||||||
this.sim.scheduleEventAt(this.sim.time + task.duration, task);
|
this.sim.scheduleEventAt(this.sim.time + task.duration, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
#finishTask(sim, event, callback)
|
async #finishTask(sim, event, callback)
|
||||||
{
|
{
|
||||||
//console.log(`Ending task with duration ${task.duration} at time ${this.sim.time}`);
|
//console.log(`Ending task with duration ${task.duration} at time ${this.sim.time}`);
|
||||||
this.available_workers++;
|
this.available_workers++;
|
||||||
callback(sim, event);
|
await callback(sim, event);
|
||||||
while (this.available_workers > 0 && this.queue.length > 0)
|
while (this.available_workers > 0 && this.queue.length > 0)
|
||||||
this.#beginTask(this.queue.shift());
|
this.#beginTask(this.queue.shift());
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6" id="exec-container"></div>
|
<div class="col-md-6" id="time-container"></div>
|
||||||
<div class="col-md-6" id="util-container"></div>
|
<div class="col-md-6" id="util-container"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,12 +63,12 @@ export async function testMergeTreeMerger()
|
|||||||
.call(function(row) {
|
.call(function(row) {
|
||||||
row.append("div")
|
row.append("div")
|
||||||
.attr("class", "col-md-6")
|
.attr("class", "col-md-6")
|
||||||
.attr("id", "merge-tree-merge-exec-container");
|
.attr("id", "merge-tree-merge-time-container");
|
||||||
row.append("div")
|
row.append("div")
|
||||||
.attr("class", "col-md-6")
|
.attr("class", "col-md-6")
|
||||||
.attr("id", "merge-tree-merge-util-container");
|
.attr("id", "merge-tree-merge-util-container");
|
||||||
});
|
});
|
||||||
|
|
||||||
new MergeTreeUtilityVisualizer(mt, d3.select("#merge-tree-merge-util-container"));
|
new MergeTreeUtilityVisualizer(mt, d3.select("#merge-tree-merge-util-container"));
|
||||||
new MergeTreeTimeVisualizer(mt, d3.select("#merge-tree-merge-exec-container"));
|
new MergeTreeTimeVisualizer(mt, d3.select("#merge-tree-merge-time-container"));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user