Restore BitArray constructor performance - #131833
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b
|
@EgorBot -windows_x64 -attempts 3 --filter "Perf_BitArray.BitArrayBoolArrayCtor" Note This benchmark request was generated with GitHub Copilot. |
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR tweaks BitArray constructors to reduce JIT impediments in hot paths, with the goal of restoring constructor microbenchmark performance after recent refactoring.
Changes:
- Avoids using an
out-parameter as the source value for allocation sizing in the byte/int span helpers by computing the bit length in a local first. - Removes the
out bitLengthparameter from the Boolean packing helper and assigns_bitLengthdirectly from the source length in the constructors. - Switches the Boolean packing loop induction variable to
intto keep span indexing and bounds analysis in canonical signed form for the JIT.
Suppressed comments (1)
src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs:342
lengthhere is a bit count (values.Length * BitsPerInt32), but the name reads like an element/byte count. Consider renaming it tobitLengthLocal(and updating the two uses) to make the unit explicit.
int length = values.Length * BitsPerInt32;
byte[] array = AllocateByteArray(length);
if (BitConverter.IsLittleEndian)
{
| int length = bytes.Length * BitsPerByte; | ||
| byte[] array = AllocateByteArray(length); | ||
|
|
||
| bytes.CopyTo(array); | ||
| bitLength = length; |
This comment was marked as outdated.
This comment was marked as outdated.
|
I've filed an alternative fix to make it fully memory safe #131838 (part of the on-going effort). Let's see if it improves. |
9072500 to
31004ff
Compare
| if (values[i]) | ||
| { | ||
| (uint byteIndex, uint bitOffset) = Math.DivRem(i, BitsPerByte); | ||
| _array[byteIndex] |= (byte)(1 << (int)bitOffset); |
|
@EgorBot -windows_x64 -linux_x64 -arm --filter "Perf_BitArray.BitArrayBoolArrayCtor" |
|
@EgorBot -windows_x64 -attempts 3 --filter "Perf_BitArray.BitArrayBoolArrayCtor" |
|
@EgorBot -windows_x64 -linux_x64 -arm --filter "Perf_BitArray.BitArrayIntArrayCtor" |
|
@EgorBot -windows_x64 -commits PR_131833,31004ff8dc2,fffed311025,44687f2d763 --envvars DOTNET_ReadyToRun:0 DOTNET_TieredCompilation:0 DOTNET_JitDisasm:BitArray DOTNET_JitDisasmDiffable:1 DOTNET_JitPrintInlinedMethods:BitArray --filter "Perf_BitArray.BitArraySetLengthGrow" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
|
@EgorBot -windows_x64 -commits PR_131833,31004ff8dc2,fffed311025,44687f2d763 --envvars DOTNET_ReadyToRun:0 DOTNET_TieredCompilation:0 DOTNET_JitDisasm:BitArray DOTNET_JitDisasmDiffable:1 DOTNET_JitPrintInlinedMethods:BitArray --filter "Perf_BitArray.BitArrayIntArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
|
@EgorBot -windows_x64 -commits ca4ed7d,31004ff8dc2 -attempts 5 --envvars DOTNET_ReadyToRun:0 DOTNET_TieredCompilation:0 DOTNET_JitDisasm:System.Collections.BitArray:.ctor* DOTNET_JitDisasmWithAddress:1 DOTNET_JitDisasmWithAlignmentBoundaries:1 DOTNET_JitDisasmWithCodeBytes:1 --filter "Perf_BitArray.BitArrayBoolArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
|
@EgorBot -windows_x64 -commits PR_131833,31004ff8dc2,31004ff8dc241f47d07361678d82e1e55b66f729 -attempts 5 --filter "Perf_BitArray.BitArrayBoolArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
|
@EgorBot -windows_x64 -commits PR_131833,ca4ed7d4a26,31004ff8dc2 -attempts 5 --envvars DOTNET_ReadyToRun:0 DOTNET_TieredCompilation:0 DOTNET_JitDisasm:System.Collections.BitArray:.ctor* DOTNET_JitDisasmWithAddress:1 DOTNET_JitDisasmWithAlignmentBoundaries:1 DOTNET_JitDisasmWithCodeBytes:1 --filter "Perf_BitArray.BitArrayBoolArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
|
@EgorBot -linux_x64 -commits PR_131833,ca4ed7d4a26,31004ff8dc2 -attempts 3 -perf_events cycles,instructions,branches,branch-misses --filter "Perf_BitArray.BitArrayBoolArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
3ce8665 to
fbdc44b
Compare
|
@EgorBot -windows_x64 -commits PR_131833,ca4ed7d4a26,9072500a881 -attempts 3 --filter "Perf_BitArray.BitArrayIntArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
|
@EgorBot -windows_x64 -commits PR_131833,ca4ed7d4a26,9072500a881 -attempts 3 --filter "Perf_BitArray.BitArrayBoolArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
There was a problem hiding this comment.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Note
This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.
|
@joshuajyue I would recommend running locally. You can just use the benchmark skill & your favorite AI tool to set it up and validate till it's ready. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4236475d-0243-488d-93d8-cd78e94d532b
fbdc44b to
5b91478
Compare
|
@EgorBot -windows_x64 -commits PR_131833,ca4ed7d4a26,9072500a881,a491f2f5ede -attempts 3 --filter "Perf_BitArray.BitArrayBoolArrayCtor" Note This benchmark request was generated with GitHub Copilot and reviewed before publication. |
Summary
byte[],bool[], andint[]constructor implementations to eliminate helper-call and inlining regressions.ReadOnlySpan<byte>,ReadOnlySpan<bool>, andReadOnlySpan<int>constructors.intinduction variable in the Boolean span constructor so span indexing and bounds analysis stay in the JIT's canonical signed form.Fixes #131815
Performance
Performance tracking identified regressions in three affected benchmark groups:
BitArrayBoolArrayCtor: automated bisection tied the confirmedSize: 512regression to Add ReadOnlySpan constructors to BitArray #131500 (16.73 ns to 22.65 ns, approximately +35%).BitArrayIntArrayCtor: additional regressions were reported at multiple input sizes.BitArraySetLengthGrow(Size: 4): this benchmark includes construction frombyte[]; automated bisection also flagged possible code-alignment noise, so it is included in the validation set.This change prioritizes preserving the existing array-constructor performance over sharing implementation with the span overloads. Focused Windows x64 EgorBot runs cover all three benchmark groups on the original regression architecture.
Validation
System.Private.CoreLibbuildNote
This pull request description was generated with GitHub Copilot and reviewed before publication.