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

[Performance][Gantt chart] Throttle updates #22723

Merged
merged 9 commits into from
Jul 8, 2024

Conversation

salazarm
Copy link
Contributor

@salazarm salazarm commented Jun 26, 2024

Summary & Motivation

For very large GanttChart's we end up computing a very expensive layout very frequently causing us to flood the mainthread with JS execution and making the page unresponsive. This PR throttles the updates to keep the page responsive. To do this I added a new feature to useThrottledMemo where leaving out the delay argument results in a dynamic delay based on how long the execution of the factory takes. Right now we multiply however long it takes by 4 (chosen because we use 4 in queryRefresh) with a maximum of 30fps.

Also memoized a bunch of stuff to make the frames faster.

Also no-cache on the logs query since it can return 10,000 entries at once, and on the RunAssetsQuery since it's unbounded and in this case returning a couple thousand assets

How I Tested These Changes

Before (barely any idle time):
Screenshot 2024-06-26 at 1 35 54 AM

After (lots of idle time) (longest frame is 9ms, most frames are 3ms):
Screenshot 2024-06-26 at 1 58 45 AM

Copy link

github-actions bot commented Jun 26, 2024

Deploy preview for dagit-core-storybook ready!

✅ Preview
https://dagit-core-storybook-d2ursjydz-elementl.vercel.app
https://salazarm-throttle-gantt-chart-updates.core-storybook.dagster-docs.io

Built with commit 1663426.
This pull request is being automatically deployed with vercel-action

const end = Date.now();

// Multiply by 4 so that if this is slow to compute we don't use up all of the wall time.
delayRef.current = Math.max((end - start) * 4, 32);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could probably jack this up even higher. Maybe 6? I don't see much downside to doing it, it would just make the page even more responsive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth pulling these numbers out into named consts and adding some context with comments -- it's a bit hard to understand why these were the values chosen.

Copy link
Contributor Author

@salazarm salazarm Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 was chosen because that's what we used in queryRefresh. 32 was chosen because that corresponds to 30fps which is a reasonably smooth frame rate for the UI.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for names here that inline your explanations - maybe const MIN_DELAY_30FPS = 32 and const PREFERRED_DELAY_MULTIPLIER = 4 (or something? that one is kinda hard to name)

Copy link
Member

@hellendag hellendag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No objection to being more aggressive about memoizing components, as long as it's having the desired effect. (Some long props lists in there...)

Will let @bengotow take a look too, since I think he probably knows this code better than I do.

const end = Date.now();

// Multiply by 4 so that if this is slow to compute we don't use up all of the wall time.
delayRef.current = Math.max((end - start) * 4, 32);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth pulling these numbers out into named consts and adding some context with comments -- it's a bit hard to understand why these were the values chosen.

@salazarm
Copy link
Contributor Author

salazarm commented Jul 8, 2024

ping @bengotow @hellendag @alangenfeld

metadata || EMPTY_RUN_METADATA,
scale,
nowMs,
const layout = useThrottledMemo(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, but I'm scared that this creates scenarios where a box can exist in props.layout and it does NOT exist in the adjusted layout for several renders (until this throttle fires). I don't know offhand that that breaks anything, but it's not really a case this code was tested for.

Before this merges, it'd be nice to write a bit of code that takes one of the boxes in the returned layout and deletes it, just to see what happens to the remainder of the rendering downstream.

I think this use case may happen in the wild with dynamic fan-out of ops where we create the boxes as the logs appear that reference them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, but I'm scared that this creates scenarios where a box can exist in props.layout and it does NOT exist in the adjusted layout for several renders (until this throttle fires). I don't know offhand that that breaks anything, but it's not really a case this code was tested for.

I read through the code a few times while adding this and there shouldn't be any issue here because props.layout isn't passed to any other part of the UI so there's no chance for there to be a parallel and inconsistent data structure.

const end = Date.now();

// Multiply by 4 so that if this is slow to compute we don't use up all of the wall time.
delayRef.current = Math.max((end - start) * 4, 32);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for names here that inline your explanations - maybe const MIN_DELAY_30FPS = 32 and const PREFERRED_DELAY_MULTIPLIER = 4 (or something? that one is kinda hard to name)

@salazarm salazarm merged commit d400df0 into master Jul 8, 2024
2 checks passed
@salazarm salazarm deleted the salazarm/throttle-gantt-chart-updates branch July 8, 2024 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants