Skip to content

Reduce Allocations by removing creation of List<SweepEvent> = new(4)#12

Closed
stefannikolei wants to merge 3 commits intoSixLabors:mainfrom
stefannikolei:sn/performance
Closed

Reduce Allocations by removing creation of List<SweepEvent> = new(4)#12
stefannikolei wants to merge 3 commits intoSixLabors:mainfrom
stefannikolei:sn/performance

Conversation

@stefannikolei
Copy link
Copy Markdown
Contributor

This eliminates the allocation of a new List in the method PossibleIntersection

Copilot AI review requested due to automatic review settings July 6, 2025 12:35
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR removes the allocation of a temporary List<SweepEvent> in PossibleIntersection and replaces it with four nullable local variables plus a flag, adding null checks before use.

  • Replaced List<SweepEvent> events with SweepEvent? first/second/third/fourth and bool firstSet
  • Updated the logic for assigning and ordering these variables instead of events.Add
  • Inserted ArgumentNullException.ThrowIfNull calls before each DivideSegment invocation
Comments suppressed due to low confidence (1)

src/PolygonClipper/PolygonClipper.cs:572

  • [nitpick] Variable names first, second, third, and fourth are ambiguous. Consider renaming them to indicate their roles (e.g., startEventA, startEventB, endEventA, endEventB) for better clarity.
        SweepEvent? first = null;

Comment thread src/PolygonClipper/PolygonClipper.cs Outdated
? (le2.OtherEvent, le1.OtherEvent)
: (le1.OtherEvent, le2.OtherEvent);

if (!firstSet)
Copy link

Copilot AI Jul 6, 2025

Choose a reason for hiding this comment

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

The firstSet flag is never updated in this branch when assigning first and second, which can lead to third and fourth remaining null incorrectly. Add firstSet = true; after second = rightSecond; to maintain the intended flow.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@JimBobSquarePants JimBobSquarePants left a comment

Choose a reason for hiding this comment

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

Ignore the bot and see my comment regarding passing a span to the method.

Comment thread src/PolygonClipper/PolygonClipper.cs Outdated
Comment thread src/PolygonClipper/PolygonClipper.cs Outdated
// The line segments associated with le1 and le2 overlap.
// TODO: Rewrite this to avoid allocation.
List<SweepEvent> events = new(4);
SweepEvent? first = null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would attack this a different way by creating a span outside of the loop and passing it to the private method as a parameter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will look into that.

Comment thread src/PolygonClipper/PolygonClipper.cs Outdated
{
DivideSegment(events[0], events[1].Point, eventQueue, comparer);
DivideSegment(events[1], events[2].Point, eventQueue, comparer);
ArgumentNullException.ThrowIfNull(first);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All these guard checks would kill performance as we're calling this method from a while loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good to know. That was the NRE brain doing its work 😊

@stefannikolei
Copy link
Copy Markdown
Contributor Author

BaseLine:

Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Clipper 75.08 us 1.459 us 2.517 us 2.88 0.11 5.8594 0.3662 47.9 KB 1.50
Clipper2 26.08 us 0.485 us 0.454 us 1.00 0.02 3.8757 0.2747 31.84 KB 1.00

NoSpan:

Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Clipper 76.97 us 1.530 us 2.145 us 3.15 0.10 5.7373 - 47.21 KB 1.48
Clipper2 24.43 us 0.463 us 0.387 us 1.00 0.02 3.8757 0.2747 31.84 KB 1.00

WithSpan:

Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Allocated Alloc Ratio
Clipper 76.23 us 1.474 us 2.161 us 2.99 0.11 5.7373 0.3662 47.27 KB 1.48
Clipper2 25.51 us 0.458 us 0.579 us 1.00 0.03 3.8757 0.2747 31.84 KB 1.00

? (le2.OtherEvent, le1.OtherEvent)
: (le1.OtherEvent, le2.OtherEvent);

if (!firstSet)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we've still got bits of the two different optimization attempts here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

With list .add was used. Now it is added with the index. So I need a way to track that. You have another idea how to do it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I’m having a crack just now. I think I have it worked out in a neat way.

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.

3 participants