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

File.*AllText* optimizations #58167

Merged
merged 18 commits into from
Oct 14, 2021
Merged

Conversation

adamsitnik
Copy link
Member

@adamsitnik adamsitnik commented Aug 26, 2021

The reduced memory allocations come from lack of StreamWriter and FileStream buffer allocations.

The reduced CPU time comes mostly from using bigger buffers and setting file preallocation size.

Windows

Method Job size Ratio Allocated
AppendAllText after 100 1.00 120 B
AppendAllText before 100 1.00 6,576 B
WriteAllText after 100 1.00 120 B
WriteAllText before 100 1.00 6,576 B
AppendAllTextAsync after 100 1.00 697 B
AppendAllTextAsync before 100 1.00 10,586 B
WriteAllTextAsync after 100 1.00 761 B
WriteAllTextAsync before 100 1.00 10,649 B
AppendAllText after 10000 0.87 121 B
AppendAllText before 10000 1.00 9,680 B
WriteAllText after 10000 0.80 125 B
WriteAllText before 10000 1.00 9,683 B
AppendAllTextAsync after 10000 0.85 698 B
AppendAllTextAsync before 10000 1.00 11,049 B
WriteAllTextAsync after 10000 0.43 768 B
WriteAllTextAsync before 10000 1.00 11,256 B
WriteAllText after 30000 0.75 125 B
WriteAllText before 30000 1.00 9,683 B
WriteAllTextAsync after 30000 0.54 765 B
WriteAllTextAsync before 30000 1.00 11,997 B
WriteAllText after 70000 0.79 126 B
WriteAllText before 70000 1.00 9,683 B
WriteAllTextAsync after 70000 0.49 767 B
WriteAllTextAsync before 70000 1.00 13,387 B
WriteAllText after 100000 0.77 124 B
WriteAllText before 100000 1.00 9,683 B
WriteAllTextAsync after 100000 0.58 770 B
WriteAllTextAsync before 100000 1.00 14,630 B

Ubuntu

Method Toolchain size Ratio Allocated
AppendAllText /after/corerun 100 0.73 112 B
AppendAllText /before/corerun 100 1.00 6,568 B
WriteAllText /after/corerun 100 0.94 112 B
WriteAllText /before/corerun 100 1.00 6,568 B
AppendAllTextAsync /after/corerun 100 0.60 480 B
AppendAllTextAsync /before/corerun 100 1.00 10,368 B
WriteAllTextAsync /after/corerun 100 0.81 544 B
WriteAllTextAsync /before/corerun 100 1.00 10,427 B
AppendAllText /after/corerun 10000 0.72 112 B
AppendAllText /before/corerun 10000 1.00 9,672 B
WriteAllText /after/corerun 10000 0.69 112 B
WriteAllText /before/corerun 10000 1.00 9,672 B
AppendAllTextAsync /after/corerun 10000 0.63 480 B
AppendAllTextAsync /before/corerun 10000 1.00 10,890 B
WriteAllTextAsync /after/corerun 10000 0.44 497 B
WriteAllTextAsync /before/corerun 10000 1.00 11,072 B
WriteAllText /after/corerun 100000 0.56 112 B
WriteAllText /before/corerun 100000 1.00 9,673 B
WriteAllTextAsync /after/corerun 100000 0.43 545 B
WriteAllTextAsync /before/corerun 100000 1.00 14,413 B

@adamsitnik adamsitnik added area-System.IO tenet-performance Performance related issue labels Aug 26, 2021
@adamsitnik adamsitnik added this to the 7.0.0 milestone Aug 26, 2021
@adamsitnik adamsitnik requested a review from jozkee August 26, 2021 10:29
@ghost
Copy link

ghost commented Aug 26, 2021

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

Issue Details

The reduced memory allocations come from lack of StreamWriter and FileStream buffer allocations.

The reduced CPU time comes mostly from using bigger buffers and setting file preallocation size.

Method Job size Ratio Allocated
AppendAllText after 100 1.00 121 B
AppendAllText before 100 1.00 6,576 B
WriteAllText after 100 1.01 121 B
WriteAllText before 100 1.00 6,577 B
AppendAllTextAsync after 100 1.00 713 B
AppendAllTextAsync before 100 1.00 10,585 B
WriteAllTextAsync after 100 0.98 778 B
WriteAllTextAsync before 100 1.00 10,648 B
AppendAllText after 10000 0.91 121 B
AppendAllText before 10000 1.00 9,680 B
WriteAllText after 10000 0.85 123 B
WriteAllText before 10000 1.00 9,682 B
AppendAllTextAsync after 10000 0.84 714 B
AppendAllTextAsync before 10000 1.00 11,048 B
WriteAllTextAsync after 10000 0.89 789 B
WriteAllTextAsync before 10000 1.00 11,242 B
WriteAllText after 100000 0.85 126 B
WriteAllText before 100000 1.00 9,684 B
WriteAllTextAsync after 100000 0.72 785 B
WriteAllTextAsync before 100000 1.00 14,598 B
Author: adamsitnik
Assignees: -
Labels:

area-System.IO, tenet-performance

Milestone: 7.0.0

@adamsitnik adamsitnik changed the title File optimizations File.*AllText* optimizations Aug 26, 2021
@adamsitnik
Copy link
Member Author

@jozkee please let me know if you would like me to separate the refactor from the optimizations

src/libraries/System.Private.CoreLib/src/System/IO/File.cs Outdated Show resolved Hide resolved
src/libraries/System.Private.CoreLib/src/System/IO/File.cs Outdated Show resolved Hide resolved
src/libraries/System.Private.CoreLib/src/System/IO/File.cs Outdated Show resolved Hide resolved
@@ -890,14 +650,10 @@ public static Task WriteAllLinesAsync(string path, IEnumerable<string> contents,

public static Task WriteAllLinesAsync(string path, IEnumerable<string> contents, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
{
if (path == null)
throw new ArgumentNullException(nameof(path));
Validate(path, encoding);
Copy link
Member

Choose a reason for hiding this comment

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

It's probably fine, but this does change which exception will be thrown if there are multiple things wrong.

src/libraries/System.Private.CoreLib/src/System/IO/File.cs Outdated Show resolved Hide resolved
@danmoseley
Copy link
Member

I assume no new tests are warranted as it's purely perf, but having touched this code can you see any we maybe are missing?

@adamsitnik
Copy link
Member Author

@stephentoub @danmoseley I've added missing tests, which identified a bug, fixed it and updated perf numbers to include Linux numbers as well. On Linux it's now even two times faster for large strings!

Copy link
Member

@jozkee jozkee left a comment

Choose a reason for hiding this comment

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

Otherwise; LGTM, Thanks.

@adamsitnik
Copy link
Member Author

I am closing the PR as it's pointless with the regression that has been recently introduced and backported to 6.0 (#59705)

@adamsitnik adamsitnik closed this Oct 12, 2021
@stephentoub
Copy link
Member

You'd made a bunch of style changes, consolidating input validation, etc. Do you want to keep those?

@adamsitnik
Copy link
Member Author

adamsitnik commented Oct 12, 2021

Conversely, it was also buggy after

It was, because I wanted to clarify how preallocationSize should work, make sure that everyone agrees and then update this PR.

@adamsitnik adamsitnik reopened this Oct 14, 2021
@adamsitnik
Copy link
Member Author

I was able to get some nice perf wins anyway (mostly due to using larger buffers which reduced the number of sys-calls). The failures are not related (System.IO.Tests.FileStream_DeleteOnClose.OpenOrCreate_DeleteOnClose_UsableAsMutex), I am merging it.

@adamsitnik adamsitnik merged commit cf49643 into dotnet:main Oct 14, 2021
@adamsitnik adamsitnik deleted the fileOptimizations branch October 14, 2021 14:40
@danmoseley
Copy link
Member

Nice!

@kunalspathak
Copy link
Member

Ubuntu x64 improvements - dotnet/perf-autofiling-issues#1897

@kunalspathak
Copy link
Member

alpine improvements - dotnet/perf-autofiling-issues#1922

@kunalspathak
Copy link
Member

windows x64 improvements - dotnet/perf-autofiling-issues#1933

@danmoseley
Copy link
Member

woo hoo.

also, @kunalspathak @adamsitnik what happened here? it's not obvious to me that this change improved File.Exists --

image

@kunalspathak
Copy link
Member

@danmoseley
Copy link
Member

A curious bimodality that seems to stay in one state for a week or more. I wonder whether iterations within an individual run are stable? I don't see a way to determine from the report.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 18, 2021
@AndyAyersMS
Copy link
Member

Improvement dotnet/perf-autofiling-issues#1795 (click on full history to see)
newplot (85)

@adamsitnik
Copy link
Member Author

adamsitnik commented Nov 23, 2021

It's interesting that it got both faster and more stable despite still being an IO benchmark. I suspect that it might be caused by using the preallocation size which avoids fragmentation.

@danmoseley
Copy link
Member

That is a beautiful graph. Nice.

@jeffhandley
Copy link
Member

That is a beautiful graph. Nice.

Wow; it really is! Great work, and I love seeing the data loop closed like this, attributing the improvements to the change.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.IO tenet-performance Performance related issue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants