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

Use FileStream overload that preallocates file on WriteAllBytes #51298

Closed
wants to merge 8 commits into from

Conversation

jozkee
Copy link
Member

@jozkee jozkee commented Apr 15, 2021

Contributes to #51116
Simple change on WriteAllBytes[Async] in order to verify if there is a perf benefit on using the new FileStream ctor. This builds on top of #51111.

Perf results obtained from the benchmarks on dotnet/performance#1774:
Note: allocations increased by 128 B and appears that large writes (>=16Kb) show a perf regression of 10%. Also note that min/maxIterationCount is 100 - 101 in order to neglect the abnormal results.

BenchmarkDotNet=v0.12.1.1521-nightly, OS=Windows 10.0.19042.868 (20H2/October2020Update)
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=6.0.100-preview.3.21207.34
  [Host]     : .NET 6.0.0 (6.0.21.20104), X64 RyuJIT
  Job-EZBBOL : .NET 6.0.0 (42.42.42.42424), X64 RyuJIT
  Job-BXKKVU : .NET 6.0.0 (42.42.42.42424), X64 RyuJIT

PowerPlanMode=00000000-0000-0000-0000-000000000000  Arguments=/p:DebugType=portable  IterationTime=250.0000 ms  
MaxIterationCount=101  MinIterationCount=100  WarmupCount=1  

|             Method | Toolchain | userBuffer |         Mean |        Error |       StdDev |       Median |          Min |          Max | Ratio | Allocated |
|------------------- |---------- |----------- |-------------:|-------------:|-------------:|-------------:|-------------:|-------------:|------:|----------:|
|      WriteAllBytes |base       |        512 |     358.6 μs |      8.24 μs |     24.44 μs |     359.5 μs |     286.6 μs |     416.3 μs |  1.00 |   4,355 B |
|      WriteAllBytes |new        |        512 |     328.8 μs |      5.02 μs |     14.66 μs |     329.8 μs |     277.6 μs |     354.4 μs |  0.92 |   4,472 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |        512 |     424.5 μs |      5.59 μs |     16.21 μs |     423.7 μs |     392.0 μs |     468.1 μs |  1.00 |   5,184 B |
| WriteAllBytesAsync |new        |        512 |     404.0 μs |      4.23 μs |     12.41 μs |     404.4 μs |     378.7 μs |     435.9 μs |  0.95 |   5,312 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |       4096 |     360.7 μs |      9.54 μs |     27.98 μs |     361.9 μs |     278.2 μs |     435.0 μs |  1.00 |     224 B |
|      WriteAllBytes |new        |       4096 |     321.6 μs |      7.09 μs |     20.56 μs |     328.2 μs |     267.1 μs |     351.0 μs |  0.90 |     352 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |       4096 |     401.0 μs |      5.81 μs |     16.58 μs |     401.3 μs |     368.1 μs |     441.7 μs |  1.00 |   5,184 B |
| WriteAllBytesAsync |new        |       4096 |     414.3 μs |      7.14 μs |     19.91 μs |     410.8 μs |     369.5 μs |     473.8 μs |  1.03 |   5,312 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |      16384 |   1,634.3 μs |     90.53 μs |    266.94 μs |   1,666.7 μs |     857.9 μs |   2,257.5 μs |  1.00 |     225 B |
|      WriteAllBytes |new        |      16384 |   1,397.7 μs |    105.00 μs |    311.24 μs |   1,339.7 μs |     824.2 μs |   1,994.4 μs |  0.88 |     353 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |      16384 |   1,643.1 μs |    103.07 μs |    305.51 μs |   1,651.1 μs |   1,043.2 μs |   2,249.3 μs |  1.00 |     905 B |
| WriteAllBytesAsync |new        |      16384 |   1,732.3 μs |    121.13 μs |    359.06 μs |   1,803.8 μs |   1,077.1 μs |   2,361.7 μs |  1.10 |   1,033 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |    1048576 |   1,276.9 μs |     53.03 μs |    156.37 μs |   1,303.9 μs |     832.7 μs |   1,629.7 μs |  1.00 |     225 B |
|      WriteAllBytes |new        |    1048576 |   1,393.0 μs |     49.60 μs |    147.03 μs |   1,379.7 μs |   1,088.6 μs |   1,666.0 μs |  1.11 |     353 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |    1048576 |   2,388.1 μs |     51.55 μs |    152.81 μs |   2,418.3 μs |   1,825.8 μs |   2,670.0 μs |  1.00 |     905 B |
| WriteAllBytesAsync |new        |    1048576 |   2,234.9 μs |     71.46 μs |    211.83 μs |   2,284.9 μs |   1,287.2 μs |   2,580.5 μs |  0.94 |   1,033 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |  104857600 | 177,801.0 μs | 14,731.29 μs | 43,665.55 μs | 188,410.9 μs |  89,479.8 μs | 264,803.5 μs |  1.00 |     368 B |
|      WriteAllBytes |new        |  104857600 | 181,757.1 μs | 10,989.27 μs | 32,573.70 μs | 179,412.4 μs |  92,253.6 μs | 243,485.6 μs |  1.11 |     424 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |  104857600 | 184,642.8 μs | 15,042.38 μs | 44,587.66 μs | 193,423.9 μs | 108,295.1 μs | 261,851.5 μs |  1.00 |   1,048 B |
| WriteAllBytesAsync |new        |  104857600 | 175,904.1 μs | 16,981.90 μs | 50,336.65 μs | 187,221.0 μs |  82,085.9 μs | 259,399.6 μs |  1.03 |   1,176 B |

@jozkee jozkee added this to the 6.0.0 milestone Apr 15, 2021
@jozkee jozkee self-assigned this Apr 15, 2021
@ghost
Copy link

ghost commented Apr 15, 2021

Tagging subscribers to this area: @carlossanlop
See info in area-owners.md if you want to be subscribed.

Issue Details

Contributes to #51116
Simple change on WriteAllBytes[Async] in order to verify if there is a perf benefit on using the new FileStream ctor. This builds on top of #51111.

Perf results obtained from the benchmarks on dotnet/performance#1774:

BenchmarkDotNet=v0.12.1.1521-nightly, OS=Windows 10.0.19042.868 (20H2/October2020Update)
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=6.0.100-preview.3.21207.34
  [Host]     : .NET 6.0.0 (6.0.21.20104), X64 RyuJIT
  Job-EZBBOL : .NET 6.0.0 (42.42.42.42424), X64 RyuJIT
  Job-BXKKVU : .NET 6.0.0 (42.42.42.42424), X64 RyuJIT

PowerPlanMode=00000000-0000-0000-0000-000000000000  Arguments=/p:DebugType=portable  IterationTime=250.0000 ms  
MaxIterationCount=101  MinIterationCount=100  WarmupCount=1  

|             Method | Toolchain | userBuffer |         Mean |        Error |       StdDev |       Median |          Min |          Max | Ratio | Allocated |
|------------------- |---------- |----------- |-------------:|-------------:|-------------:|-------------:|-------------:|-------------:|------:|----------:|
|      WriteAllBytes |base       |        512 |     358.6 μs |      8.24 μs |     24.44 μs |     359.5 μs |     286.6 μs |     416.3 μs |  1.00 |   4,355 B |
|      WriteAllBytes |new        |        512 |     328.8 μs |      5.02 μs |     14.66 μs |     329.8 μs |     277.6 μs |     354.4 μs |  0.92 |   4,472 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |        512 |     424.5 μs |      5.59 μs |     16.21 μs |     423.7 μs |     392.0 μs |     468.1 μs |  1.00 |   5,184 B |
| WriteAllBytesAsync |new        |        512 |     404.0 μs |      4.23 μs |     12.41 μs |     404.4 μs |     378.7 μs |     435.9 μs |  0.95 |   5,312 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |       4096 |     360.7 μs |      9.54 μs |     27.98 μs |     361.9 μs |     278.2 μs |     435.0 μs |  1.00 |     224 B |
|      WriteAllBytes |new        |       4096 |     321.6 μs |      7.09 μs |     20.56 μs |     328.2 μs |     267.1 μs |     351.0 μs |  0.90 |     352 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |       4096 |     401.0 μs |      5.81 μs |     16.58 μs |     401.3 μs |     368.1 μs |     441.7 μs |  1.00 |   5,184 B |
| WriteAllBytesAsync |new        |       4096 |     414.3 μs |      7.14 μs |     19.91 μs |     410.8 μs |     369.5 μs |     473.8 μs |  1.03 |   5,312 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |      16384 |   1,634.3 μs |     90.53 μs |    266.94 μs |   1,666.7 μs |     857.9 μs |   2,257.5 μs |  1.00 |     225 B |
|      WriteAllBytes |new        |      16384 |   1,397.7 μs |    105.00 μs |    311.24 μs |   1,339.7 μs |     824.2 μs |   1,994.4 μs |  0.88 |     353 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |      16384 |   1,643.1 μs |    103.07 μs |    305.51 μs |   1,651.1 μs |   1,043.2 μs |   2,249.3 μs |  1.00 |     905 B |
| WriteAllBytesAsync |new        |      16384 |   1,732.3 μs |    121.13 μs |    359.06 μs |   1,803.8 μs |   1,077.1 μs |   2,361.7 μs |  1.10 |   1,033 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |    1048576 |   1,276.9 μs |     53.03 μs |    156.37 μs |   1,303.9 μs |     832.7 μs |   1,629.7 μs |  1.00 |     225 B |
|      WriteAllBytes |new        |    1048576 |   1,393.0 μs |     49.60 μs |    147.03 μs |   1,379.7 μs |   1,088.6 μs |   1,666.0 μs |  1.11 |     353 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |    1048576 |   2,388.1 μs |     51.55 μs |    152.81 μs |   2,418.3 μs |   1,825.8 μs |   2,670.0 μs |  1.00 |     905 B |
| WriteAllBytesAsync |new        |    1048576 |   2,234.9 μs |     71.46 μs |    211.83 μs |   2,284.9 μs |   1,287.2 μs |   2,580.5 μs |  0.94 |   1,033 B |
|                    |           |            |              |              |              |              |              |              |       |           |
|      WriteAllBytes |base       |  104857600 | 177,801.0 μs | 14,731.29 μs | 43,665.55 μs | 188,410.9 μs |  89,479.8 μs | 264,803.5 μs |  1.00 |     368 B |
|      WriteAllBytes |new        |  104857600 | 181,757.1 μs | 10,989.27 μs | 32,573.70 μs | 179,412.4 μs |  92,253.6 μs | 243,485.6 μs |  1.11 |     424 B |
|                    |           |            |              |              |              |              |              |              |       |           |
| WriteAllBytesAsync |base       |  104857600 | 184,642.8 μs | 15,042.38 μs | 44,587.66 μs | 193,423.9 μs | 108,295.1 μs | 261,851.5 μs |  1.00 |   1,048 B |
| WriteAllBytesAsync |new        |  104857600 | 175,904.1 μs | 16,981.90 μs | 50,336.65 μs | 187,221.0 μs |  82,085.9 μs | 259,399.6 μs |  1.03 |   1,176 B |
Author: Jozkee
Assignees: Jozkee
Labels:

area-System.IO

Milestone: 6.0.0

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@adamsitnik
Copy link
Member

@jozkee it looks like we are not going to get any speedup in this case. Could find a scenario that uses a different pattern: multiple calls to FileStream.Write* with a file size known up-front? A good example should be decompression. This might work better for us as without specifying the file size up-front, every Write extends the file. The more such writes (in case of File.WriteAllBytes it was always a single write) the higher chances.

@adamsitnik
Copy link
Member

BTW @jozkee it would be great if you could consider #51048 while looking at our FileStream usages

@jozkee
Copy link
Member Author

jozkee commented Apr 20, 2021

Closing this since it proves that is not beneficial to preallocate a file if we only perform one single Write.

it would be great if you could consider #51048 while looking at our FileStream usages

@adamsitnik yup, I will measure the benefits of no buffering on WriteAll* methods and will send a PR if its proven to be beneficial.

@jozkee jozkee closed this Apr 20, 2021
@jozkee jozkee deleted the preallocate_file branch May 3, 2021 17:43
@ghost ghost locked as resolved and limited conversation to collaborators Jun 2, 2021
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.

2 participants