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

Reduce complexity overhead (and avoid one copy) when obtaining character SSDQs in SpriteTextDrawNode #5883

Merged
merged 5 commits into from
Jul 4, 2023

Conversation

peppy
Copy link
Member

@peppy peppy commented Jul 3, 2023

Of note, this doesn't show a huge improvement in benchmarks, but in an edge case noticed in osu!, the AddRange operation can add a perceivable overhead:

JetBrains Rider 2023-07-03 at 08 17 17

Before After
JetBrains Rider 2023-07-03 at 08 16 07 JetBrains Rider 2023-07-03 at 08 15 11

We believe this may be a dotnet runtime quirk.

Even though this change can't be shown in isolated benchmarks, I'd argue the code quality improvement is worth it.

Of note, the caching of the SSDQs at SpriteText was a bit redundant as it was being invalidated by basically everything. So it makes sense to just fetch it every time, regardless, in the draw node itself.

The only potential saving we could obtain with the previous logic would be to shift the load to the Update frame. But this wasn't being done. Rather than investigating whether that has any benefits, I'd rather focus on getting SpriteText rewritten to use an optimised shader.

master

Method Mean Error StdDev Allocated
TestStaticText 984.4 us 1.30 us 1.22 us 6 B
TestMovingText 2,701.5 us 47.68 us 42.27 us 117 B
TestChangingText 11,381.6 us 161.87 us 143.49 us 64182 B

this pr (pooled at 2967626)

Method Mean Error StdDev Allocated
TestStaticText 985.3 us 2.46 us 2.30 us 6 B
TestMovingText 2,928.0 us 57.80 us 75.16 us 119 B
TestChangingText 11,426.4 us 186.17 us 155.46 us 64184 B

this pr (list at 90a2e28)

Method Mean Error StdDev Allocated
TestStaticText 985.5 us 1.80 us 1.68 us 6 B
TestMovingText 2,476.1 us 47.54 us 50.86 us 110 B
TestChangingText 10,793.3 us 211.44 us 217.14 us 64180 B

peppy added 3 commits July 3, 2023 15:51
…ter SSDQs in `SpriteTextDrawNode`

Of note, this doesn't show a huge improvement in benchmarks, but in an
edge case noticed in osu!, the `AddRange` operation can add a
perceivable overhead.

We believe this may be a dotnet runtime quirk.

Even though this change can't be shown in isolated benchmarks, I'd argue
the code quality improvement is worth it.

Of note, the caching of the SSDQs at `SpriteText` was a bit redundant as
it was being invalidated by basically everything. So it makes sense to
just fetch it every time, regardless, in the draw node itself.

The only potential saving we could obtain with the previous logic would
be to shift the load to the `Update` frame. But this wasn't being done.
Rather than investigating whether that has any benefits, I'd rather
focus on getting `SpriteText` rewritten to use an optimised shader.
Copy link
Collaborator

@bdach bdach left a comment

Choose a reason for hiding this comment

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

Would like @smoogipoo's take on the invalidation stuff if possible.

{
int partCount = Source.characters.Count;

parts ??= new List<ScreenSpaceCharacterPart>(partCount);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is the optimisation, correct? The lazy list init and the partCount spec?

It's not what I would call "avoid one copy" exactly because it's not directly avoiding copies, it's avoiding list reallocs due to having to expand the collection to match incoming count (which in turn will cause struct copies due to how list-of-struct works), which makes me mildly confused. Strictly speaking it would save to the order of O(n) copies if I'm not mistaken?

Copy link
Member Author

@peppy peppy Jul 3, 2023

Choose a reason for hiding this comment

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

The copy I meant was the fact that items were previously added once to a list in SpriteText then subsequently copied to a second list in DrawNode.

The main goal was to remove the weird InsertRange overhead, and this was enough to do that.

@bdach bdach requested a review from smoogipoo July 3, 2023 22:03
@peppy
Copy link
Member Author

peppy commented Jul 3, 2023

Would like @smoogipoo's take on the invalidation stuff if possible.

I double-checked with him IRL on the invalidation part when making this change, so he should be on board with it 😄

@peppy
Copy link
Member Author

peppy commented Jul 4, 2023

With EnsureCapacity (wouldn't expect a change, so just confirming it doesn't get worse):

Method Mean Error StdDev Allocated
TestStaticText 985.8 us 0.99 us 0.93 us 6 B
TestMovingText 2,529.1 us 49.84 us 48.95 us 108 B
TestChangingText 10,811.6 us 79.74 us 74.59 us 64166 B

@bdach bdach removed the request for review from smoogipoo July 4, 2023 19:53
@bdach bdach enabled auto-merge July 4, 2023 20:11
@bdach bdach merged commit 050857d into ppy:master Jul 4, 2023
Comment on lines -504 to -505
private readonly LayoutValue parentScreenSpaceCache = new LayoutValue(Invalidation.DrawSize | Invalidation.Presence | Invalidation.DrawInfo, InvalidationSource.Parent);
private readonly LayoutValue localScreenSpaceCache = new LayoutValue(Invalidation.MiscGeometry, InvalidationSource.Self);
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose one of the advantages of this is that the overhead only occurred once per invalidation, rather than 3 times (once for each DrawNode) per invalidation.

Copy link
Member Author

Choose a reason for hiding this comment

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

The lookup was already lazy, so I'm not sure if this would ever be the case, but maybe.

@peppy peppy deleted the sprite-text-draw-node-perf branch September 14, 2023 09:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants