Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

CSHARP-5666: Remove GetBitArray allocations in BsonClassMapSerializer.DeserializeClass #1767

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

Merged
BorisDog merged 1 commit into mongodb:main from BorisDog:csharp5666
Sep 30, 2025

Conversation

@BorisDog
Copy link
Contributor

@BorisDog BorisDog commented Sep 1, 2025

No description provided.

@BorisDog BorisDog requested a review from a team as a code owner September 1, 2025 18:19
@BorisDog BorisDog requested review from papafe and removed request for a team September 1, 2025 18:19
@BorisDog BorisDog added the improvement Optimizations or refactoring (no new features or fixes). label Sep 1, 2025
papafe
papafe previously approved these changes Sep 2, 2025
Copy link
Contributor

@papafe papafe left a comment

Choose a reason for hiding this comment

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

Overall it looks good, just a small number of comments.

internal static class FastMemberMapHelper
{
publicstaticuint[]GetBitArray(intmemberCount)
internalrefstructBitArray()
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should choose a different name so there is no confusion with System.Collections.BitArray?
Not sure it's completely necessary.

Copy link
Contributor Author

@BorisDog BorisDog Sep 9, 2025
edited
Loading

Choose a reason for hiding this comment

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

I think it's minor but still a valid point.
Changed to MemebersBitArray. Not sure about this naming though. Please suggest better alternatives if there are any.

[InlineData(1024, 993)]
[InlineData(1024, 1000)]
[InlineData(1024, 1023)]
public void Deserialize_should_throw_FormatException_when_no_required_element_is_found(int membersCount, int missingMemberIndex)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should rename it to
Deserialize_should_throw_FormatException_when_required_element_is_not_found?

Copy link
Contributor Author

@BorisDog BorisDog Sep 9, 2025

Choose a reason for hiding this comment

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

Thanks, done.

Copy link
Contributor

papafe commented Sep 2, 2025

By the way, have we tried benchmarking what is the difference in allocation on a test case?

Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

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

Looks really good. I have some minor suggestions. Let me know what you think.

var memberMapBitArray = FastMemberMapHelper.GetBitArray(allMemberMaps.Count);

var (bitArrayLength, useStackAlloc) = FastMemberMapHelper.GetMembersBitArrayLength(_classMap.AllMemberMaps.Count);
using var bitArray = useStackAlloc ? FastMemberMapHelper.GetMembersBitArray(stackalloc uint[bitArrayLength]) : FastMemberMapHelper.GetMembersBitArray(bitArrayLength);
Copy link
Contributor

Choose a reason for hiding this comment

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

I was confused by the name bitArrayLength because it sounded like it was the number of bits, but it's actually the number of uints.

I suggest some renaming:

var (lengthInUInts, useStackAlloc) = FastMemberMapHelper.GetLengthInUInts(allMemberMaps.Count);
using var bitArray = useStackAlloc ? FastMemberMapHelper.GetMembersBitArray(stackalloc uint[lengthInUInts]) : FastMemberMapHelper.GetMembersBitArray(lengthInUInts);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea, done.

}
if ((bitBlock & 15) == 0)

public MembersBitArray(int spanLength, uint[] rentedBuffer, ArrayPool<uint> arrayPool) : this()
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of passing in BOTH the rentedBuffer and the arrayPool it came from how about just passing in the arrayPool?

public MembersBitArray(int lengthInUInts, ArrayPool<uint> arrayPool) : this()
{ 
 _arrayPool = arrayPool; 
 _rentedBuffer = _arrayPool.Rent(lengthInUInts); 
 _bitArray = _rentedBuffer.AsSpan(0, lengthInUInts); 
 
 _bitArray.Clear(); 
} 

Also suggest renaming spanLength to lengthInUInts to standardize on a name that clearly indicates it is the length in uints and not the length in bits.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea, it's cleaner. Done.

{
var length = (membersCount + 31) >> 5;
return (length, length <= 8); // Use stackalloc for up to 256 members
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Some suggested renamings for clarity:

public static (int LengthInUInts, bool UseStackAlloc) GetLengthInUInts(int membersCoun
{
 var lengthInUInts = (membersCount + 31) >> 5;
 return (lengthInUInts, lengthInUInts <= 8); // Use stackalloc for up to 256 members
} 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

new(span);

public static MembersBitArray GetMembersBitArray(int length) =>
new(length, ArrayPool<uint>.Shared.Rent(length), ArrayPool<uint>.Shared);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested refactoring to only pass the ArrayPool:

public static MembersBitArray GetMembersBitArray(int lengthInUInts) =>
 new(lengthInUInts, ArrayPool<uint>.Shared); 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

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

Minor changes requested.

var extraElementsMemberMapIndex = _classMap.ExtraElementsMemberMapIndex;
var memberMapBitArray = FastMemberMapHelper.GetBitArray(allMemberMaps.Count);

var (lengthInUInts, useStackAlloc) = FastMemberMapHelper.GetLengthInUInts(_classMap.AllMemberMaps.Count);
Copy link
Contributor

Choose a reason for hiding this comment

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

I forgot to mention that you could just use the existing allMemberMaps variable instead of _classMap.AllMemberMaps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, done.

}
if ((bitBlock & 15) == 0)

public MembersBitArray(int lengthInUints, ArrayPool<uint> arrayPool) : this()
Copy link
Contributor

Choose a reason for hiding this comment

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

Standardize capitalization as lengthInUInts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, done.


public static (int LengthInUInts, bool UseStackAlloc) GetLengthInUInts(int membersCount)
{
var length = (membersCount + 31) >> 5;
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename length to lengthInUInts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

public static MembersBitArray GetMembersBitArray(Span<uint> span) =>
new(span);

public static MembersBitArray GetMembersBitArray(int length) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename length to lengthInUInts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@rstam rstam self-requested a review September 10, 2025 17:02
papafe
papafe previously approved these changes Sep 15, 2025
Copy link
Contributor

@papafe papafe left a comment

Choose a reason for hiding this comment

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

LGTM

rstam
rstam previously approved these changes Sep 19, 2025
Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@rstam rstam left a comment

Choose a reason for hiding this comment

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

LGTM

@BorisDog BorisDog merged commit 1ea18fb into mongodb:main Sep 30, 2025
33 of 36 checks passed
@BorisDog BorisDog deleted the csharp5666 branch September 30, 2025 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@rstam rstam rstam approved these changes

@papafe papafe papafe left review comments

@sanych-sun sanych-sun Awaiting requested review from sanych-sun

Assignees

No one assigned

Labels

improvement Optimizations or refactoring (no new features or fixes).

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /