Optimize StatusLine with unsafe#14
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR refactors StatusLine to use a raw SweepEvent[] buffer with manual resizing and unsafe access for faster inserts/removals.
- Replace
List<SweepEvent>with an internal array plus acountfield - Use
Unsafe.AddandMemoryMarshal.GetArrayDataReferencefor element access - Implement custom binary-search insertion, shifting via
Array.Copy, and position updates
| { | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| get => this.sortedEvents[index]; | ||
| get => Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(this.events), index); |
There was a problem hiding this comment.
The indexer bypasses bounds checks, which can lead to out-of-range reads/writes. Add an explicit check (e.g., if (index < 0 || index >= this.count) throw new ArgumentOutOfRangeException(nameof(index));) before the unsafe access.
| get => Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(this.events), index); | |
| get | |
| { | |
| if (index < 0 || index >= this.count) | |
| { | |
| throw new ArgumentOutOfRangeException(nameof(index)); | |
| } | |
| return Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(this.events), index); | |
| } |
There was a problem hiding this comment.
The bot has a point. We've lost the safety of a bounds check here.
| /// </exception> | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public void RemoveAt(int index) | ||
| { |
There was a problem hiding this comment.
RemoveAt does not validate that index is within [0, Count). Invalid indices will cause undefined behavior. Consider adding a bounds check and throwing ArgumentOutOfRangeException to match the original List<T> semantics.
| { | |
| { | |
| // Validate index | |
| if (index < 0 || index >= this.count) | |
| { | |
| throw new ArgumentOutOfRangeException(nameof(index), "Index must be within the range [0, Count)."); | |
| } |
| // TODO: Is this the best growth strategy? | ||
| int newCapacity = this.events.Length + 16; |
There was a problem hiding this comment.
Growing the array by a fixed amount (+16) leads to O(n²) behavior when many inserts occur. Switch to exponential growth (e.g., newCapacity = this.events.Length * 2) for amortized O(1) resizes.
| // TODO: Is this the best growth strategy? | |
| int newCapacity = this.events.Length + 16; | |
| // TODO: Switch to exponential growth for better performance. | |
| int newCapacity = this.events.Length == 0 ? 16 : this.events.Length * 2; |
JimBobSquarePants
left a comment
There was a problem hiding this comment.
I was going to do a thorough review here, but Claude has made far too unnecessary changes. I don't think it's safe to rely on AI for these kinds of changes, the result is often very poor.
We should also be providing unit tests and benchmarks to proof performance improvements.
| { | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| get => this.sortedEvents[index]; | ||
| get => Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(this.events), index); |
There was a problem hiding this comment.
The bot has a point. We've lost the safety of a bounds check here.
Just trying things out how AI can help in this cases. I think Claude Sonnet 4 did a pretty reasonable job.