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

Improve loading performance #48

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
8 changes: 7 additions & 1 deletion src/event_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ export class EventList {
private highlightedEventId: string;
private positionListener: (eventId: string) => void;
private jsonListener: (eventId: string) => void;
private tempList: DocumentFragment = document.createDocumentFragment();
constructor(
readonly container: HTMLElement,
readonly template: HTMLTemplateElement,
) {}

clear(): void {
this.container.innerHTML = "";
this.tempList = document.createDocumentFragment();
}

onEventClick(fn): void {
Expand Down Expand Up @@ -53,7 +55,11 @@ export class EventList {
if (ev.state_key != null) {
row.style.fontWeight = "600";
}
this.container.appendChild(row);
this.tempList.appendChild(row);
}

render() {
this.container.appendChild(this.tempList);
}

highlight(eventId: string) {
Expand Down
63 changes: 49 additions & 14 deletions src/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,28 @@ const textualRepresentation = (ev: RenderableMatrixEvent, scenario?: Scenario) =

const redraw = (vis: HTMLDivElement, events: MatrixEvent[], opts: RenderOptions) => {
// copy the events so we don't alter the caller's copy
// https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone
// biome-ignore lint/style/noParameterAssign:
events = JSON.parse(JSON.stringify(events));
events = structuredClone(events);
// sort events chronologically
const data: Array<RenderableMatrixEvent> = events; // .sort((a, b) => a.origin_server_ts - b.origin_server_ts);

const eventsById: Map<string, RenderableMatrixEvent> = new Map();
for (let i = 0; i < data.length; i++) {
data[i].streamPosition = i;
eventsById.set(data[i].event_id, data[i]);
}
const eventsById: Map<string, RenderableMatrixEvent> = new Map(
data.map((ev, i) => {
ev.streamPosition = i;
return [ev.event_id, ev];
}),
);

// and insert potential placeholders for dangling edges.
// we slice to do a shallow copy given we're inserting placeholders into data
for (const d of data.slice()) {
const dataCopy = [...data];

// Lookup maps for inserting placeholders at the right position
const eventIdIndexLookup = new Map<string, number>();
const placeholdersByEventId: Map<string, RenderableMatrixEvent> = new Map();

for (const [index, d] of dataCopy.entries()) {
// order parents chronologically
d.prev_events = d.prev_events.sort((a: string, b: string) => {
return (eventsById.get(a)?.streamPosition || 0) - (eventsById.get(b)?.streamPosition || 0);
Expand All @@ -68,6 +76,8 @@ const redraw = (vis: HTMLDivElement, events: MatrixEvent[], opts: RenderOptions)
// remove auth events that point to create events, as they are very duplicative.
//d.auth_events = d.auth_events.filter(id => eventsById.get(id)?.type !== 'm.room.create');

eventIdIndexLookup.set(d.event_id, index);

for (const p of d.prev_events) {
if (!eventsById.get(p)) {
const placeholder = {
Expand All @@ -82,10 +92,8 @@ const redraw = (vis: HTMLDivElement, events: MatrixEvent[], opts: RenderOptions)
origin_server_ts: 0,
};
eventsById.set(p, placeholder);
// insert the placeholder immediately before the event which refers to it
const i = data.findIndex((ev) => ev.event_id === d.event_id);
console.log("inserting placeholder prev_event at ", i);
data.splice(i, 0, placeholder);
// insert the placeholder into a temp map to later insert at the right position
placeholdersByEventId.set(d.event_id, placeholder);
}

// update children on parents
Expand All @@ -96,6 +104,22 @@ const redraw = (vis: HTMLDivElement, events: MatrixEvent[], opts: RenderOptions)
}
}

// Insert placeholders into the data array at the right position before the referenced event
// The key is the event_id of the event that references the placeholder
//
// We do this to speed up the algorithm
for (const [key, value] of placeholdersByEventId) {
const index = eventIdIndexLookup.get(key);
if (index !== undefined) {
data.splice(index, 0, value);

// Update the indices in the map for subsequent elements
for (let i = index; i < data.length; i++) {
eventIdIndexLookup.set(data[i].event_id, i);
}
}
}

// which lanes are in use for prev_events that point to a given event_id
// so we know how to fill up the lanes.
const lanes: Array<string> = [];
Expand Down Expand Up @@ -280,12 +304,23 @@ const redraw = (vis: HTMLDivElement, events: MatrixEvent[], opts: RenderOptions)
}
while (edges.length > 0 && i > edges.at(-1)?.y) edges.pop();
if (p.next_events) {
edges.push({
const newEdge = {
x: eventsById.get(p.next_events.at(-1)).x,
y: eventsById.get(p.next_events.at(-1)).y,
});
};
// Insert newEdge into the sorted position
let inserted = false;
for (let j = edges.length - 1; j >= 0; j--) {
if (edges[j].x <= newEdge.x) {
edges.splice(j + 1, 0, newEdge);
inserted = true;
break;
}
}
if (!inserted) {
edges.unshift(newEdge);
}
}
edges.sort((a, b) => a.x - b.x);
d.laneWidth = edges.at(-1)?.x;
if (balanceTwoWayForks && d.laneWidth % 2) {
// balance simple 2-way forks
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class Dag {
scenario.events.forEach((ev, i) => {
eventList.appendEvent(i, ev);
});
eventList.render();
eventList.highlight(this.debugger.current());
eventList.onEventClick((eventId: string) => {
this.debugger.goTo(eventId);
Expand Down