Skip to content
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

More WriteGather fixes #109826

Merged
merged 15 commits into from
Dec 12, 2024
Merged

More WriteGather fixes #109826

merged 15 commits into from
Dec 12, 2024

Conversation

adamsitnik
Copy link
Member

@adamsitnik adamsitnik commented Nov 14, 2024

  • don't run these tests in parallel, as each test cases uses more than 4 GB ram and disk
  • fix the test: handle incomplete reads that should happen when we hit the max buffer limit
  • incomplete write fix:
    • pin the buffers only once
    • when re-trying, do that only for the actual reminder
    • handle Int32 overflow on macOS

@adamsitnik adamsitnik added this to the 10.0.0 milestone Nov 14, 2024
@adamsitnik adamsitnik self-assigned this Nov 14, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

ReadOnlyMemory<byte> buffer = buffers[i];
totalBytesToWrite += buffer.Length;

MemoryHandle memoryHandle = buffer.Pin();
Copy link
Member Author

@adamsitnik adamsitnik Nov 14, 2024

Choose a reason for hiding this comment

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

So far, for incomplete writes we were pinning the memory for every retry attempt. I am not sure if this can create some kind of edge case bugs, but I think we can do it just once, before we enter the main loop

{
if (asyncMethod)
{
await RandomAccess.WriteAsync(sfh, writeBuffers, fileOffset);
bytesRead = await RandomAccess.ReadAsync(sfh, readBuffers, fileOffset);
Copy link
Member Author

Choose a reason for hiding this comment

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

this was a test bug, the test assumed that the read won't ever be a partial read

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

long bytesWritten;
Span<Interop.Sys.IOVector> left = vectors.Slice(buffersOffset);
Span<Interop.Sys.IOVector> left = vectors.Slice(buffersOffset, buffersCount - buffersOffset);
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to change L171 to stackalloc Interop.Sys.IOVector[IovStackThreshold].Slice(0, buffersCount), and then vectors will have Length == buffersCount and you won't need the second argument here, since vectors will always be correctly sized?

Copy link
Member Author

Choose a reason for hiding this comment

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

It looks better, thanks!

{
int n = buffers[i].Length;
int n = buffers[buffersOffset].Length;
Copy link
Member

@stephentoub stephentoub Dec 3, 2024

Choose a reason for hiding this comment

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

Is there anything that could go horribly wrong if the caller-provided IReadOnlyList changed between accesses? It looks ok, just confirming. We'd want to avoid a concurrency bug becoming a memory safety bug.

Copy link
Member Author

Choose a reason for hiding this comment

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

this was great question! since we perform a "copy" for the purpose of pinning and IOV, I was able to read that value from the copy.


totalLength += vectors[i].Count;

if (totalLength > INT_MAX)
Copy link
Member

Choose a reason for hiding this comment

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

Can size_t be 32-bit, in which case totalLength might overflow above?

Copy link
Member Author

Choose a reason for hiding this comment

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

It can be 32-bit (on 32 bit systems), but it's unsigned so for:

  • inputs that are > int.MaxValue && < uint.MaxValue there will be no overflow
  • valid inputs that are > uint.MaxValue should not be possible, as it's impossible to allocate more than uint.MaxValue memory on 32 bit systems.

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@adamsitnik
Copy link
Member Author

/ba-g unrelated job timeout (Started 8h 28m 8s ago and still runinng)

@adamsitnik adamsitnik merged commit 4951e38 into dotnet:main Dec 12, 2024
152 of 164 checks passed
adamsitnik added a commit to adamsitnik/runtime that referenced this pull request Dec 12, 2024
* don't run these tests in parallel, as each test cases uses more than 4 GB ram and disk!

* fix the test: handle incomplete reads that should happen when we hit the max buffer limit

* incomplete write fix:

- pin the buffers only once
- when re-trying, do that only for the actual reminder

* Use native memory to get OOM a soon as we run out of memory (hoping to avoid the process getting killed on Linux when OOM happens)

* For macOS preadv and pwritev can fail with EINVAL when the total length of all vectors overflows a 32-bit integer.

* add an assert that is going to warn us if vector.Count is ever more than Int32.MaxValue

---------

Co-authored-by: Michał Petryka <35800402+MichalPetryka@users.noreply.github.com>
adamsitnik added a commit to adamsitnik/runtime that referenced this pull request Dec 12, 2024
* don't run these tests in parallel, as each test cases uses more than 4 GB ram and disk!

* fix the test: handle incomplete reads that should happen when we hit the max buffer limit

* incomplete write fix:

- pin the buffers only once
- when re-trying, do that only for the actual reminder

* Use native memory to get OOM a soon as we run out of memory (hoping to avoid the process getting killed on Linux when OOM happens)

* For macOS preadv and pwritev can fail with EINVAL when the total length of all vectors overflows a 32-bit integer.

* add an assert that is going to warn us if vector.Count is ever more than Int32.MaxValue

---------

Co-authored-by: Michał Petryka <35800402+MichalPetryka@users.noreply.github.com>
# Conflicts:
#	src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Dec 14, 2024
* don't run these tests in parallel, as each test cases uses more than 4 GB ram and disk!

* fix the test: handle incomplete reads that should happen when we hit the max buffer limit

* incomplete write fix:

- pin the buffers only once
- when re-trying, do that only for the actual reminder

* Use native memory to get OOM a soon as we run out of memory (hoping to avoid the process getting killed on Linux when OOM happens)

* For macOS preadv and pwritev can fail with EINVAL when the total length of all vectors overflows a 32-bit integer.

* add an assert that is going to warn us if vector.Count is ever more than Int32.MaxValue

---------

Co-authored-by: Michał Petryka <35800402+MichalPetryka@users.noreply.github.com>
@github-actions github-actions bot locked and limited conversation to collaborators Jan 12, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants