Skip to content

Avoid allocations in BeginAsyncPacketRead #12039

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Erarndt
Copy link
Contributor

@Erarndt Erarndt commented Jun 18, 2025

Context

We can avoid a few allocations in the BeginAsyncPacketRead() path. There are repeated delegate creations that can be reused, and there is some state that is used in BodyReadComplete() that we can get without allocating.

There are ~40MB of allocations of AsyncCallback

image

and ~10MB of Tuple allocations

image

We can avoid these allocations by reusing the delegates and storing the state that's used in BodyReadComplete() (just the packet length) as a field.

Changes Made

Testing

Notes

@Copilot Copilot AI review requested due to automatic review settings June 18, 2025 21:30
Copy link
Contributor

@Copilot 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 optimizes the asynchronous packet read process by caching delegate callbacks and reusing a field to store the packet length, thereby reducing allocations.

  • Cached delegates for header and body read callbacks to avoid repeated delegate creation.
  • Replaced tuple allocations in BeginRead calls by storing the packet length in a field that is reused across asynchronous operations.

Comment on lines 972 to +977
// Ensures the buffer is at least this length.
// It avoids reallocations if the buffer is already large enough.
_readBufferMemoryStream.SetLength(packetLength);
_readBufferMemoryStream.SetLength(_currentPacketLength);
byte[] packetData = _readBufferMemoryStream.GetBuffer();

_pipeStream.BeginRead(packetData, 0, packetLength, BodyReadComplete, new Tuple<byte[], int>(packetData, packetLength));
_pipeStream.BeginRead(packetData, 0, _currentPacketLength, _bodyReadCompleteCallback, this);
Copy link
Preview

Copilot AI Jun 18, 2025

Choose a reason for hiding this comment

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

Using the shared field '_currentPacketLength' for asynchronous operations assumes that read operations are strictly sequential. It would be safer to capture the packet length in a local variable to prevent potential race conditions if multiple reads were ever initiated concurrently.

Copilot uses AI. Check for mistakes.

…rndt/packetReadAllocs

# Conflicts:
#	src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs
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.

2 participants