-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-5348: Avoid allocations for Bson*Context #1791
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
base: main
Are you sure you want to change the base?
Conversation
_bsonStream = (stream as BsonStream) ?? new BsonStreamAdapter(stream); | ||
|
||
_context = null; | ||
_context = new(ContextType.TopLevel, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use TopLevel
instead of null
public class BsonBinaryReaderTests | ||
{ | ||
[Fact] | ||
public void Bookmarks_should_work() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added bookmarks test for better coverage of BsonBinaryReader
.
GuidRepresentation.Unspecified)] GuidRepresentation guidRepresentation) | ||
[InlineData(BsonBinarySubType.UuidStandard)] | ||
[InlineData(BsonBinarySubType.UuidLegacy)] | ||
public void ReadBinaryData_should_read_correct_subtype(BsonBinarySubType subType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated cleanup.
GuidRepresentation
wasn't used in ReadBinaryData_subtype_3_should_use_GuidRepresentation_from_settings
and ReadBinaryData_subtype_4_should_use_GuidRepresentation_Standard
tests. Both tests combined into single ReadBinaryData_should_read_correct_subtype
test.
#pragma warning restore CA2213 // Disposable never disposed | ||
private readonly BsonStream _bsonStream; | ||
private BsonBinaryReaderContext _context; | ||
private readonly Lazy<Stack<BsonBinaryReaderContext>> _contexts = new(() => new()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've traded allocations of contexts for whatever allocations the stack class does.
Consider using a non-default initial capacity, probably around 4 (maybe 8), otherwise the first few calls to Push
will result in memory allocations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lazy
requires an allocation also. Maybe just go ahead and allocate the Stack?
Actually maybe multiple allocations. I think the factory lambda is an allocation as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private readonly Stack<BsonBinaryReaderContext> _contextStack = new(4);
return new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _bsonStream.Position); | ||
} | ||
public override BsonReaderBookmark GetBookmark() => | ||
new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _contexts.Value.ToArray(), _bsonStream.Position); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's encapsulate all logic about how to restore the context stack into the bookmark, so here we can just pass the _contextStack
and let the bookmark call ToArray
(or whatever it wants to do).
new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _contextStack, _bsonStream.Position);
|
||
_context = _context.PopContext(_bsonStream.Position); | ||
_context = PopContext(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's encapsulate all side effects of popping the context in the PopContext
method (see below).
PopContext();
} | ||
|
||
_context = _context.PopContext(_bsonStream.Position); | ||
_context = PopContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PopContext();
if (_context.ContextType == ContextType.JavaScriptWithScope) | ||
{ | ||
_context = _context.PopContext(_bsonStream.Position); // JavaScriptWithScope | ||
_context = PopContext(); // JavaScriptWithScope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PopContext(); // JavaScriptWithScope
{ | ||
BackpatchSize(); // size of the JavaScript with scope value | ||
_context = _context.ParentContext; | ||
_context = _contexts.Value.Pop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PopContext();
_context = new BsonBinaryWriterContext(_context, ContextType.JavaScriptWithScope, _bsonStream.Position); | ||
|
||
_contexts.Value.Push(_context); | ||
_context = new(ContextType.JavaScriptWithScope, _bsonStream.Position); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Single line instead of two:
PushContext(new(ContextType.JavaScriptWithScope, _bsonStream.Position));
{ | ||
BackpatchSize(); // size of the JavaScript with scope value | ||
_context = _context.ParentContext; | ||
_context = _contexts.Value.Pop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PopContext();
_context = new BsonBinaryWriterContext(_context, ContextType.Array, _bsonStream.Position); | ||
|
||
_contexts.Value.Push(_context); | ||
_context = new(ContextType.Array, _bsonStream.Position); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PushContext(new(ContextType.Array, _bsonStream.Position));
var contextType = (State == BsonWriterState.ScopeDocument) ? ContextType.ScopeDocument : ContextType.Document; | ||
_context = new BsonBinaryWriterContext(_context, contextType, _bsonStream.Position); | ||
_contexts.Value.Push(_context); | ||
_context = new(contextType, _bsonStream.Position); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PushContext(new(contextType, _bsonStream.Position));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add new helper methods:
private void PopContext()
{
_context = _contextStack.Pop();
}
private void PushContext(BsonBinaryWriterContext context)
{
_contextStack.Push(_context);
_context = context;
}
Convert
BsonBinaryWriterContext
andBsonBinaryReaderContext
to structs, to reduces allocations on a hot path.