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

StreamReader sync/async optimizations: ArrayPool, IndexOfAny, ValueStringBuilder #62552

Closed
wants to merge 26 commits into from

Conversation

Trapov
Copy link

@Trapov Trapov commented Dec 8, 2021

No description provided.

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Dec 8, 2021
@ghost
Copy link

ghost commented Dec 8, 2021

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

Issue Details

null

Author: Trapov
Assignees: -
Labels:

area-System.IO

Milestone: -

@Trapov Trapov changed the title Store string-builder as a field and clear when reuse. Store string-builder as a field and clear when reuse + IndexOfAny Dec 8, 2021
@Trapov
Copy link
Author

Trapov commented Dec 8, 2021

First commit:

Method Job Toolchain LineLengthRange Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Allocated
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 576.81 us 29.131 us 32.379 us 562.49 us 540.34 us 643.70 us 1.00 0.00 187.5000 2.2321 1,155 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 579.85 us 23.834 us 27.447 us 568.93 us 536.74 us 637.39 us 1.01 0.07 187.5000 - 1,155 KB
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 31.16 us 1.892 us 1.943 us 31.12 us 28.09 us 34.21 us 1.00 0.00 10.4608 0.1324 65 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 27.64 us 3.124 us 3.598 us 27.46 us 23.35 us 34.90 us 0.90 0.16 7.8125 0.1883 48 KB
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 338.23 us 3.676 us 3.439 us 339.46 us 328.85 us 341.25 us 1.00 0.00 125.0000 1.3298 771 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 347.04 us 1.816 us 1.610 us 347.21 us 344.91 us 350.68 us 1.03 0.01 125.0000 1.3889 771 KB
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 168.74 us 2.675 us 2.502 us 167.66 us 165.08 us 172.47 us 1.00 0.00 55.6319 0.6868 344 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 207.40 us 0.979 us 0.868 us 207.53 us 205.60 us 208.89 us 1.23 0.02 55.6507 - 341 KB
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 65.34 us 6.810 us 7.842 us 62.75 us 57.27 us 83.04 us 1.00 0.00 17.8241 - 110 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 81.23 us 6.146 us 7.077 us 83.22 us 67.65 us 92.21 us 1.26 0.21 17.2739 - 107 KB
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 47.61 us 6.259 us 7.208 us 48.00 us 35.45 us 62.57 us 1.00 0.00 9.5588 - 59 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 41.29 us 4.558 us 5.066 us 41.44 us 35.00 us 51.81 us 0.89 0.23 9.0054 0.1344 55 KB
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 36.41 us 5.433 us 6.257 us 32.57 us 30.47 us 47.04 us 1.00 0.00 10.6383 0.1662 65 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 28.45 us 3.326 us 3.830 us 27.53 us 24.42 us 35.71 us 0.80 0.14 7.4954 0.1153 46 KB
ReadLineAsync Job-LRHBFX /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 41.31 us 4.936 us 5.684 us 41.06 us 32.03 us 51.89 us 1.00 0.00 14.8121 0.3613 92 KB
ReadLineAsync Job-WPRXLK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 31.82 us 3.128 us 3.603 us 31.36 us 26.26 us 38.29 us 0.78 0.13 7.5758 0.1994 47 KB

@Trapov
Copy link
Author

Trapov commented Dec 8, 2021

Second commit.

Method Job Toolchain LineLengthRange Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Allocated
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 662.746 us 113.3171 us 125.9516 us 635.766 us 530.010 us 981.626 us 1.00 0.00 187.5000 2.0161 1,155 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 897.942 us 153.5380 us 176.8146 us 884.897 us 627.131 us 1,174.790 us 1.43 0.42 187.5000 - 1,155 KB
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 44.210 us 6.8340 us 7.8700 us 44.208 us 32.168 us 60.262 us 1.00 0.00 10.4962 0.1193 65 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 10.893 us 1.3715 us 1.4085 us 10.705 us 9.150 us 14.385 us 0.26 0.04 7.8711 0.2085 48 KB
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 378.469 us 31.3504 us 32.1946 us 388.850 us 330.829 us 426.392 us 1.00 0.00 125.0000 1.3889 771 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 460.742 us 64.4477 us 66.1830 us 459.692 us 361.494 us 646.085 us 1.23 0.20 125.0000 - 771 KB
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 234.039 us 33.1543 us 36.8509 us 230.729 us 181.060 us 319.581 us 1.00 0.00 55.9896 - 344 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 295.321 us 65.5221 us 75.4553 us 269.663 us 202.976 us 437.634 us 1.29 0.29 54.9242 - 341 KB
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 77.645 us 10.5925 us 12.1983 us 76.216 us 61.252 us 101.756 us 1.00 0.00 17.9005 - 110 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 55.522 us 5.4000 us 6.2187 us 54.816 us 46.218 us 69.039 us 0.74 0.16 17.2619 0.1984 107 KB
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 52.007 us 8.5124 us 9.8029 us 52.466 us 37.995 us 73.383 us 1.00 0.00 9.6154 - 59 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 26.025 us 3.8753 us 4.1465 us 25.841 us 19.443 us 35.776 us 0.51 0.15 8.9447 0.1132 55 KB
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 32.651 us 2.6058 us 3.0008 us 31.169 us 29.556 us 39.670 us 1.00 0.00 10.5676 0.1510 65 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 8.686 us 0.4398 us 0.4888 us 8.517 us 8.158 us 9.859 us 0.27 0.03 7.5146 0.1986 46 KB
ReadLineAsync Job-SKXEQW /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 37.268 us 2.6887 us 2.8769 us 37.231 us 32.383 us 42.540 us 1.00 0.00 14.9336 0.4148 92 KB
ReadLineAsync Job-UNIDYD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 10.994 us 1.9150 us 2.2053 us 10.871 us 7.850 us 14.618 us 0.30 0.06 7.5902 0.1905 47 KB

@Trapov
Copy link
Author

Trapov commented Dec 8, 2021

Third commit.

Method Job Toolchain LineLengthRange Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Allocated
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 610.499 us 45.8071 us 52.7516 us 606.611 us 536.488 us 728.957 us 1.00 0.00 187.5000 2.1552 1,155 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 609.997 us 10.9450 us 9.1396 us 607.176 us 598.037 us 629.751 us 0.99 0.09 187.5000 2.4038 1,155 KB
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 29.742 us 0.2418 us 0.2261 us 29.774 us 29.406 us 30.153 us 1.00 0.00 10.4755 0.1177 65 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 8.426 us 0.1208 us 0.1071 us 8.418 us 8.277 us 8.581 us 0.28 0.00 7.8587 0.2015 48 KB
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 317.568 us 2.0502 us 1.8175 us 317.490 us 314.543 us 321.392 us 1.00 0.00 125.0000 1.2755 771 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 356.798 us 4.1285 us 3.8618 us 355.689 us 351.650 us 364.344 us 1.12 0.01 125.0000 1.3889 771 KB
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 218.355 us 20.7127 us 23.0221 us 212.493 us 184.479 us 264.052 us 1.00 0.00 55.7065 0.6793 344 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 183.303 us 14.4207 us 16.0286 us 175.745 us 171.088 us 229.335 us 0.85 0.09 55.3161 0.7184 341 KB
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 67.853 us 6.6293 us 7.6343 us 66.260 us 58.617 us 80.864 us 1.00 0.00 18.0000 - 110 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 46.189 us 0.7011 us 0.5855 us 45.973 us 45.376 us 47.320 us 0.70 0.07 17.2414 0.1959 107 KB
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 35.469 us 0.3500 us 0.3103 us 35.388 us 35.122 us 36.106 us 1.00 0.00 9.5937 - 59 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 17.950 us 0.1317 us 0.1167 us 17.942 us 17.785 us 18.204 us 0.51 0.01 9.0103 0.0715 55 KB
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 29.064 us 0.3262 us 0.2891 us 28.979 us 28.752 us 29.734 us 1.00 0.00 10.6285 0.1155 65 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 10.322 us 1.8122 us 2.0869 us 9.830 us 8.158 us 14.673 us 0.38 0.07 7.5246 0.1946 46 KB
ReadLineAsync Job-BCLYJE /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 34.621 us 2.6252 us 2.9179 us 33.592 us 31.286 us 41.322 us 1.00 0.00 14.8810 0.3720 92 KB
ReadLineAsync Job-AIFESD /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 9.381 us 0.9107 us 1.0488 us 9.299 us 7.818 us 11.542 us 0.27 0.04 7.5916 0.1963 47 KB

@Trapov
Copy link
Author

Trapov commented Dec 8, 2021

@stephentoub @adamsitnik moved your suggestions to here.

@stephentoub
Copy link
Member

Thanks. My use of StringBuilder in the other PR comments was mostly for a quick hack to exemplify the wins. I think we should look here at instead using char arrays from ArrayPool.

@Trapov
Copy link
Author

Trapov commented Dec 9, 2021

Thanks. My use of StringBuilder in the other PR comments was mostly for a quick hack to exemplify the wins. I think we should look here at instead using char arrays from ArrayPool.

Wouldn't be there a problem when there's only one line and we return and rent new char arrays other and other until we hit EOF?

@Trapov Trapov force-pushed the optimizations/7.0.0/stream-reader branch from 8c20efb to fb6ccb3 Compare December 9, 2021 09:00
@Trapov Trapov force-pushed the optimizations/7.0.0/stream-reader branch from fb6ccb3 to e2e318f Compare December 9, 2021 09:15
@Trapov
Copy link
Author

Trapov commented Dec 9, 2021

Thanks. My use of StringBuilder in the other PR comments was mostly for a quick hack to exemplify the wins. I think we should look here at instead using char arrays from ArrayPool.

Wouldn't be there a problem when there's only one line and we return and rent new char arrays other and other until we hit EOF?
@stephentoub

  1. If we read the full buffer and there's more to consume (not yet new line characters) we would have to reallocate/resize the polled array
  2. If we read the buffer (and before that read the buffer and no symbols were met) and a new line symbols there then we would have to resize/join current buffer + polled array.
  3. We wouldn't need a polled array if it's a first read and we see new-line characters so we would make it in place from the buffer.

The first and the second cases do need reallocating/resizing pooled array.

CheckAndRead() //  pos == len and Read()

do{
  
  
  if (HaveEndLine()){
     return lineFromBuffer or (pooledArray + lineFromBuffer) Reallocated/Resized
  }
  
  pooledArray = ArrayPool.Rent(sizeOfBuffer) + copyBuffer + pooledArray (Reallocated/Resized)
  
}
while{Reading()}

@stephentoub
Copy link
Member

Wouldn't be there a problem when there's only one line and we return and rent new char arrays other and other until we hit EOF?

Why is that a problem?

@Trapov
Copy link
Author

Trapov commented Dec 9, 2021

Wouldn't be there a problem when there's only one line and we return and rent new char arrays other and other until we hit EOF?

Why is that a problem?

I meant reallocating and resizing the polled arrays wouldn't the cost be higher?

@stephentoub
Copy link
Member

I meant reallocating and resizing the polled arrays wouldn't the cost be higher?

There would be some cost, but it should also be rare. We can afford to rent a larger char[] initially than we can proactively allocate a StringBuilder, and by renting/returning, we save the cost of creating a new StringBuilder for each StreamReader, plus avoid increasing the size of the StreamReader itself. We also avoid holding on to a potentially large object for the lifetime of the StreamReader and instead allow other code in the process to use that same memory, or potentially for it to be reclaimed.

@Trapov
Copy link
Author

Trapov commented Dec 9, 2021

@stephentoub Added array pools as a draft and they do look better. I've picked 80 as default increase factor, probably gonna change to something different. I assume we could redo passing the arrays and copying them as spans, and change Array.Copy to Buffer.Copy but I'm not sure.

Also gonna run unit-tests locally. Feel like I broke something.

Method Job Toolchain LineLengthRange Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Allocated
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 522.034 us 7.0370 us 6.2381 us 520.286 us 515.192 us 537.143 us 1.00 0.00 187.5000 2.0833 1,155 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 636.177 us 8.1706 us 6.3790 us 635.035 us 624.068 us 647.340 us 1.22 0.01 187.5000 - 1,155 KB
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 29.147 us 0.5828 us 0.6478 us 28.984 us 28.503 us 30.683 us 1.00 0.00 10.5119 0.1143 65 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 9.698 us 0.1297 us 0.1213 us 9.690 us 9.511 us 9.911 us 0.33 0.01 8.8465 0.1149 54 KB
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 318.809 us 4.8207 us 4.2735 us 318.232 us 313.885 us 329.554 us 1.00 0.00 125.0000 1.2500 771 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 373.266 us 6.9424 us 6.1542 us 373.846 us 364.966 us 383.455 us 1.17 0.02 125.0000 1.4535 771 KB
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 165.901 us 2.0257 us 1.7957 us 166.238 us 163.289 us 169.531 us 1.00 0.00 55.8511 0.6649 344 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 172.244 us 2.0826 us 1.7391 us 172.141 us 169.617 us 174.969 us 1.04 0.01 55.6319 0.6868 344 KB
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 58.729 us 1.1529 us 1.2336 us 58.550 us 57.323 us 61.251 us 1.00 0.00 17.9228 0.2298 110 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 46.200 us 0.5590 us 0.4956 us 46.094 us 45.393 us 47.170 us 0.79 0.02 17.7266 0.1827 110 KB
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 34.953 us 0.4604 us 0.4307 us 34.956 us 34.306 us 35.947 us 1.00 0.00 9.6366 - 59 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 18.279 us 0.1876 us 0.1567 us 18.287 us 18.040 us 18.631 us 0.52 0.01 9.3163 0.0734 57 KB
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 29.082 us 0.2937 us 0.2603 us 29.078 us 28.675 us 29.515 us 1.00 0.00 10.6308 0.1168 65 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 9.497 us 0.1593 us 0.1412 us 9.512 us 9.250 us 9.719 us 0.33 0.01 9.0269 0.1147 56 KB
ReadLineAsync Job-ZXCHUZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 30.532 us 0.2215 us 0.2072 us 30.475 us 30.280 us 30.997 us 1.00 0.00 14.9562 0.3648 92 KB
ReadLineAsync Job-QMQFHK /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 9.897 us 0.1248 us 0.1167 us 9.854 us 9.746 us 10.130 us 0.32 0.00 11.1146 0.3522 68 KB

@Trapov
Copy link
Author

Trapov commented Dec 9, 2021

Can't really run the tests I hope the CI would spot an error if there is one (I hope no errors) :(

#62586 (created an issue)

Anyway the benchmarks are here so feel free to comment. I'm gonna play with the Spans until any other comments/suggestions.

@Trapov Trapov changed the title Store string-builder as a field and clear when reuse + IndexOfAny Store string-builder as a field and clear when reuse + IndexOfAny + ArrayPool Dec 9, 2021
@Trapov
Copy link
Author

Trapov commented Dec 9, 2021

Fixed the tests. And figured out how to run them locally (wrote about it in the issue and closed it).

@Trapov
Copy link
Author

Trapov commented Dec 9, 2021

Benchmarks after fixing the tests.

Method Job Toolchain LineLengthRange Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Allocated
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 523.748 us 8.9409 us 8.3633 us 520.891 us 513.102 us 542.318 us 1.00 0.00 187.5000 2.0161 1,155 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 0] 655.019 us 8.7931 us 8.2251 us 654.981 us 637.813 us 665.928 us 1.25 0.02 187.5000 - 1,155 KB
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 31.184 us 0.5500 us 0.4876 us 31.022 us 30.707 us 32.180 us 1.00 0.00 10.5422 0.1255 65 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 0, 1024] 9.271 us 0.1739 us 0.1626 us 9.319 us 9.045 us 9.560 us 0.30 0.01 6.2796 0.0739 39 KB
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 325.870 us 6.2568 us 6.4253 us 324.772 us 315.801 us 337.069 us 1.00 0.00 125.0000 1.3021 771 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 1] 370.305 us 3.6738 us 3.4365 us 371.015 us 364.259 us 375.299 us 1.14 0.02 125.0000 1.4535 771 KB
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 167.550 us 1.6253 us 1.5203 us 167.785 us 164.828 us 169.526 us 1.00 0.00 55.7796 0.6720 344 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 1, 8] 177.799 us 3.3752 us 3.3149 us 176.830 us 173.500 us 185.600 us 1.06 0.02 55.0272 0.6793 341 KB
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 58.646 us 0.6930 us 0.6144 us 58.625 us 57.410 us 59.579 us 1.00 0.00 17.7920 0.2281 110 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 9, 32] 47.613 us 0.5092 us 0.4252 us 47.569 us 46.797 us 48.296 us 0.81 0.01 17.2872 0.1900 106 KB
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 34.964 us 0.3600 us 0.3368 us 34.960 us 34.423 us 35.655 us 1.00 0.00 9.5291 - 59 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 33, 128] 18.300 us 0.1990 us 0.1861 us 18.278 us 18.089 us 18.651 us 0.52 0.01 8.8362 0.0718 54 KB
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 29.496 us 0.4170 us 0.3697 us 29.412 us 29.069 us 30.358 us 1.00 0.00 10.5535 0.1173 65 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [ 129, 1024] 8.553 us 0.0618 us 0.0578 us 8.543 us 8.475 us 8.644 us 0.29 0.00 6.2842 0.1025 39 KB
ReadLineAsync Job-PWLHPZ /testhost-main/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 31.037 us 0.3864 us 0.3614 us 31.032 us 30.576 us 31.664 us 1.00 0.00 14.9457 0.3706 92 KB
ReadLineAsync Job-BQBLXH /testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun [1025, 2048] 8.214 us 0.1540 us 0.1440 us 8.204 us 8.008 us 8.440 us 0.26 0.01 6.1749 0.0980 38 KB

@Trapov
Copy link
Author

Trapov commented May 11, 2022

@danmoseley removed readtoend changes.

@Trapov Trapov requested a review from danmoseley May 11, 2022 18:17
@danmoseley
Copy link
Member

danmoseley commented May 11, 2022

Can you update the benchmark now -- I don't see ReadLine results. We care about ReadLine and ReadLineAsync benchmarks, I would expect the others to be unchanged (of course we should verify)

Some nice improvements in the ReadLineAsync numbers though. I'm guessing the short line cases are slower because IndexOfAny(..) is slower than a trivial loop when the hit is very early in the buffer.

Something interesting I noticed is that the perf characteristics will be slightly different on Windows vs Linux/Mac (that you have). That's because the perf tests use the platform line ending:
https://github.com/dotnet/performance/blob/9062f791140cea0eaba3f3625c845b17e1a80dd4/src/benchmarks/micro/libraries/System.IO/TextReaderReadLineTests.cs#L45

You might want to locally edit that line to use \r\n and then rerun, to see those numbers too. I wouldn't expect it to be that different, but it will run through a slightly different path. It will mostly affect very short lines of course. Eg., [1,1] will create about 5000 lines in the 16K buffer on Windows, and 8000 lines on Mac.

@stephentoub any concerns about the changes here? The local methods seem ugly, but as discussed, we seem to be missing a reusable type here for this case.

@Trapov
Copy link
Author

Trapov commented May 11, 2022

@danmoseley added NewLine to the params (N RN map to the \n \r\n) and ran the benchmarks with ReadLine+ReadLineAsync

BenchmarkDotNet=v0.13.1.1786-nightly, OS=macOS Monterey 12.3.1 (21E258) [Darwin 21.4.0]
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=7.0.100-preview.3.22179.4
  [Host]     : .NET 7.0.0 (7.0.22.17504), X64 RyuJIT
  Job-HKXHTD : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
  Job-RJNYWV : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT

PowerPlanMode=00000000-0000-0000-0000-000000000000  IterationTime=250.0000 ms  MaxIterationCount=20  
MinIterationCount=15  WarmupCount=1  
Method Job Toolchain NewLine LineLengthRange Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Allocated Alloc Ratio
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 0] 88.14 μs 0.994 μs 0.881 μs 87.85 μs 87.16 μs 89.97 μs 0.42 0.01 20.3488 - 3.27 KB 1.00
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 0] 208.48 μs 2.137 μs 1.999 μs 207.83 μs 205.67 μs 211.32 μs 1.00 0.00 19.7368 - 3.27 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 1024] 40.40 μs 0.574 μs 0.509 μs 40.28 μs 39.74 μs 41.52 μs 1.95 0.04 389.5202 - 62.2 KB 1.72
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 1024] 20.66 μs 0.396 μs 0.370 μs 20.47 μs 20.27 μs 21.41 μs 1.00 0.00 226.3580 - 36.21 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 1] 173.74 μs 1.316 μs 1.167 μs 173.95 μs 170.66 μs 175.22 μs 0.76 0.01 1223.3146 38.6236 195.27 KB 1.00
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 1] 228.94 μs 2.970 μs 2.480 μs 229.20 μs 224.52 μs 233.62 μs 1.00 0.00 1223.2143 38.3929 195.27 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 8] 128.25 μs 1.583 μs 1.403 μs 128.24 μs 126.02 μs 130.88 μs 1.01 0.01 704.3478 - 112.4 KB 1.03
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 8] 127.47 μs 1.316 μs 1.167 μs 127.33 μs 125.58 μs 129.45 μs 1.00 0.00 686.4919 - 109.57 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 9, 32] 53.04 μs 0.629 μs 0.588 μs 52.97 μs 52.24 μs 54.20 μs 1.23 0.02 352.8912 - 56.42 KB 1.07
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 9, 32] 43.19 μs 0.560 μs 0.524 μs 42.98 μs 42.62 μs 44.33 μs 1.00 0.00 331.1644 - 52.59 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 33, 128] 37.21 μs 0.503 μs 0.420 μs 37.21 μs 36.27 μs 37.87 μs 1.57 0.03 279.9171 - 44.7 KB 1.12
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 33, 128] 23.75 μs 0.357 μs 0.317 μs 23.79 μs 23.31 μs 24.21 μs 1.00 0.00 250.0000 - 39.94 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 129, 1024] 41.58 μs 0.566 μs 0.502 μs 41.49 μs 40.74 μs 42.43 μs 2.03 0.03 396.5000 - 63.39 KB 1.73
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 129, 1024] 20.51 μs 0.242 μs 0.227 μs 20.44 μs 20.22 μs 20.88 μs 1.00 0.00 229.1395 - 36.68 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [1025, 2048] 51.25 μs 1.038 μs 1.154 μs 51.06 μs 49.49 μs 53.12 μs 2.43 0.06 522.5244 3.2468 90.86 KB 2.44
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [1025, 2048] 21.13 μs 0.454 μs 0.505 μs 21.03 μs 20.41 μs 22.12 μs 1.00 0.00 230.6863 - 37.24 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 0] 57.56 μs 0.426 μs 0.398 μs 57.67 μs 56.94 μs 58.13 μs 0.53 0.01 20.2206 - 3.27 KB 1.00
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 0] 108.11 μs 1.553 μs 1.376 μs 108.07 μs 106.08 μs 110.73 μs 1.00 0.00 20.1199 - 3.27 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 1024] 39.82 μs 0.376 μs 0.333 μs 39.77 μs 39.23 μs 40.44 μs 1.96 0.03 383.2487 - 61.3 KB 1.70
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 1024] 20.29 μs 0.313 μs 0.278 μs 20.24 μs 19.85 μs 20.93 μs 1.00 0.00 224.9514 - 35.98 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 1] 127.01 μs 1.096 μs 0.972 μs 127.05 μs 124.83 μs 128.49 μs 0.81 0.01 831.3008 - 132.69 KB 1.01
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 1] 156.74 μs 0.827 μs 0.774 μs 156.71 μs 154.60 μs 157.91 μs 1.00 0.00 822.4010 - 131.28 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 8] 110.45 μs 1.216 μs 1.078 μs 110.47 μs 108.53 μs 112.30 μs 1.03 0.01 594.1840 - 94.88 KB 1.03
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 8] 107.33 μs 0.638 μs 0.533 μs 107.38 μs 106.41 μs 108.05 μs 1.00 0.00 576.6267 - 92.05 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 9, 32] 51.98 μs 0.670 μs 0.594 μs 51.89 μs 50.94 μs 53.17 μs 1.24 0.02 339.1970 - 54.08 KB 1.07
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 9, 32] 42.04 μs 0.300 μs 0.266 μs 41.98 μs 41.49 μs 42.57 μs 1.00 0.00 315.6667 - 50.4 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 33, 128] 37.00 μs 0.514 μs 0.456 μs 37.02 μs 36.30 μs 37.92 μs 1.53 0.02 281.2500 - 44.88 KB 1.14
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 33, 128] 24.13 μs 0.257 μs 0.240 μs 24.05 μs 23.70 μs 24.62 μs 1.00 0.00 248.2824 - 39.43 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 129, 1024] 40.00 μs 0.399 μs 0.373 μs 40.05 μs 39.20 μs 40.43 μs 1.96 0.03 381.6794 - 61.08 KB 1.67
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 129, 1024] 20.38 μs 0.256 μs 0.227 μs 20.35 μs 20.06 μs 20.79 μs 1.00 0.00 229.1667 - 36.68 KB 1.00
ReadLine Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [1025, 2048] 49.27 μs 0.412 μs 0.365 μs 49.40 μs 48.57 μs 49.69 μs 2.31 0.04 516.2109 3.5156 90.71 KB 2.44
ReadLine Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [1025, 2048] 21.31 μs 0.352 μs 0.312 μs 21.29 μs 20.74 μs 21.85 μs 1.00 0.00 230.7181 - 37.24 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 0] 1,030.78 μs 10.028 μs 8.373 μs 1,028.46 μs 1,021.56 μs 1,051.98 μs 0.81 0.01 7238.2813 - 1155.27 KB 1.00
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 0] 1,276.54 μs 16.725 μs 15.644 μs 1,268.77 μs 1,259.75 μs 1,304.99 μs 1.00 0.00 7236.1111 - 1155.27 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 1024] 57.57 μs 1.070 μs 0.894 μs 57.56 μs 56.39 μs 59.46 μs 2.29 0.04 404.6763 - 64.59 KB 1.67
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 1024] 25.12 μs 0.280 μs 0.249 μs 25.12 μs 24.67 μs 25.48 μs 1.00 0.00 241.3000 - 38.6 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 1] 682.83 μs 12.967 μs 12.736 μs 683.05 μs 666.01 μs 712.16 μs 0.90 0.02 4830.7292 - 771.27 KB 1.00
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 1] 761.50 μs 6.336 μs 5.290 μs 761.99 μs 751.02 μs 770.48 μs 1.00 0.00 4830.3571 - 771.27 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 8] 322.52 μs 2.809 μs 2.627 μs 323.21 μs 318.14 μs 326.82 μs 0.91 0.01 2153.7500 - 343.8 KB 1.01
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 8] 353.99 μs 3.487 μs 2.912 μs 353.33 μs 349.17 μs 360.92 μs 1.00 0.00 2136.1111 - 340.97 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 9, 32] 108.52 μs 1.174 μs 1.098 μs 108.47 μs 107.16 μs 110.63 μs 1.06 0.03 690.9014 - 110.28 KB 1.04
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 9, 32] 101.86 μs 2.547 μs 2.831 μs 100.87 μs 98.84 μs 107.87 μs 1.00 0.00 666.3961 - 106.45 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 33, 128] 61.08 μs 0.882 μs 0.782 μs 61.06 μs 60.00 μs 62.56 μs 1.38 0.02 370.6395 - 59.19 KB 1.09
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 33, 128] 44.16 μs 0.471 μs 0.418 μs 44.26 μs 43.44 μs 44.82 μs 1.00 0.00 340.8774 - 54.42 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 129, 1024] 57.89 μs 1.768 μs 1.965 μs 56.87 μs 55.98 μs 62.29 μs 2.31 0.07 408.9286 - 65.29 KB 1.69
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 129, 1024] 25.21 μs 0.310 μs 0.290 μs 25.21 μs 24.77 μs 25.77 μs 1.00 0.00 240.7000 - 38.58 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [1025, 2048] 65.63 μs 1.031 μs 0.914 μs 65.31 μs 64.20 μs 67.15 μs 2.77 0.04 476.7562 7.4897 91.63 KB 2.41
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [1025, 2048] 23.72 μs 0.250 μs 0.234 μs 23.66 μs 23.39 μs 24.13 μs 1.00 0.00 235.1235 - 38.02 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 0] 742.71 μs 183.783 μs 204.275 μs 670.49 μs 540.30 μs 1,246.29 μs 0.95 0.35 3629.1667 - 579.27 KB 1.00
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 0] 804.63 μs 84.780 μs 97.633 μs 809.55 μs 688.16 μs 978.83 μs 1.00 0.00 3628.9063 - 579.27 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 1024] 66.15 μs 5.430 μs 6.253 μs 67.25 μs 56.06 μs 78.91 μs 2.37 0.20 399.9110 - 63.63 KB 1.66
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 0, 1024] 26.77 μs 0.514 μs 0.429 μs 26.60 μs 26.40 μs 27.98 μs 1.00 0.00 239.0376 - 38.3 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 1] 492.95 μs 9.335 μs 10.375 μs 489.33 μs 478.22 μs 514.43 μs 0.90 0.02 3237.9032 - 516.74 KB 1.00
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 1] 547.55 μs 6.008 μs 5.326 μs 545.59 μs 541.14 μs 558.24 μs 1.00 0.00 3228.4483 - 515.33 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 8] 292.94 μs 5.747 μs 6.618 μs 292.36 μs 282.91 μs 306.74 μs 0.94 0.04 1808.9623 - 288.66 KB 1.01
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 1, 8] 312.51 μs 11.828 μs 12.147 μs 311.16 μs 298.71 μs 348.45 μs 1.00 0.00 1790.8654 - 285.83 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 9, 32] 119.37 μs 11.473 μs 12.753 μs 112.42 μs 109.09 μs 145.75 μs 1.01 0.23 661.2762 - 105.55 KB 1.04
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 9, 32] 125.96 μs 31.777 μs 36.594 μs 101.98 μs 100.05 μs 220.98 μs 1.00 0.00 638.0719 - 101.87 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 33, 128] 80.75 μs 11.853 μs 13.650 μs 81.97 μs 63.41 μs 109.29 μs 1.91 0.32 370.1717 - 59.15 KB 1.10
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 33, 128] 44.26 μs 0.255 μs 0.226 μs 44.25 μs 43.78 μs 44.61 μs 1.00 0.00 333.3333 - 53.7 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 129, 1024] 57.83 μs 0.426 μs 0.378 μs 57.74 μs 57.39 μs 58.73 μs 2.25 0.03 392.8571 - 62.98 KB 1.63
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [ 129, 1024] 25.74 μs 0.467 μs 0.414 μs 25.64 μs 25.14 μs 26.49 μs 1.00 0.00 240.5992 - 38.58 KB 1.00
ReadLineAsync Job-HKXHTD /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [1025, 2048] 66.35 μs 0.626 μs 0.523 μs 66.36 μs 65.48 μs 67.25 μs 2.78 0.04 491.4063 5.4688 91.48 KB 2.41
ReadLineAsync Job-RJNYWV /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun RN [1025, 2048] 23.90 μs 0.364 μs 0.304 μs 23.85 μs 23.41 μs 24.62 μs 1.00 0.00 235.1190 - 38.02 KB 1.00

@Trapov
Copy link
Author

Trapov commented May 11, 2022

We could fall-back to initial while loop if the line is short. We need to determine which is short tho. Something around 5-16 chars.

@danmoseley
Copy link
Member

We could fall-back to initial while loop if the line is short. We need to determine which is short tho. Something around 5-16 chars.

How do you know how long the line is without searching in the first place? It’s just an assumption we have to make that files are mostly content not newline characters.

@Trapov
Copy link
Author

Trapov commented May 12, 2022

We could fall-back to initial while loop if the line is short. We need to determine which is short tho. Something around 5-16 chars.

How do you know how long the line is without searching in the first place? It’s just an assumption we have to make that files are mostly content not newline characters.

Sorry, I meant running the search loop for 10 chars and then switching back to index of any

@Trapov
Copy link
Author

Trapov commented May 12, 2022

@danmoseley that's what I meant. My naive solution (didn't push it) but what I don't understand why it's allocating 74 kb like that.

        public override string? ReadLine()
        {
            ThrowIfDisposed();
            CheckAsyncTaskInProgress();

            if (_charPos == _charLen)
            {
                if (ReadBuffer() == 0)
                {
                    return null;
                }
            }

            // fast path
            (int indexStart, int length) = (_charPos, _charLen - _charPos);

            int indexOfNewLine = -1;
            int fastPathMaxLength = length % 9;
            Span<char> span = _charBuffer.AsSpan(indexStart, fastPathMaxLength);
            int index = fastPathMaxLength - 1;
            while (index > -1)
            {
                char @char = span[index];
                if (@char is '\r' or '\n')
                {
                    indexOfNewLine = index;
                    break;
                }
                index--;
            }

            if (indexOfNewLine != -1)
            {
                string s = new string(_charBuffer, indexStart, indexOfNewLine);
                char ch = _charBuffer[_charPos + indexOfNewLine];
                _charPos += indexOfNewLine + 1;

                // if it is two chars for the new-line character then inc the _charPos
                if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0))
                {
                    if (_charBuffer[_charPos] == '\n')
                    {
                        _charPos++;
                    }
                }
                return s;
            }

            using ValueStringBuilder sb = new(stackalloc char[256]);
            do
            {
                // Note the following common line feed chars:
                // \n - UNIX/Mac   \r\n - Windows
                (indexStart, length) = (_charPos, _charLen - _charPos);

                indexOfNewLine = _charBuffer.AsSpan(indexStart, length).IndexOfAny('\r', '\n');
                if (indexOfNewLine >= 0)
                {
                    string s;
                    if (sb.Length > 0)
                    {
                        sb.Append(_charBuffer.AsSpan(indexStart, indexOfNewLine));
                        s = sb.ToString();
                    }
                    else
                    {
                        s = new string(_charBuffer, indexStart, indexOfNewLine);
                    }
                    char ch = _charBuffer[_charPos + indexOfNewLine];
                    _charPos += indexOfNewLine + 1;

                    // if it is two chars for the new-line character then inc the _charPos
                    if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0))
                    {
                        if (_charBuffer[_charPos] == '\n')
                        {
                            _charPos++;
                        }
                    }
                    return s;
                }

                int totalRead = _charLen - _charPos;
                sb.Append(_charBuffer.AsSpan(_charPos, totalRead));
            } while (ReadBuffer() > 0);
            return sb.ToString();
        }
BenchmarkDotNet=v0.13.1.1786-nightly, OS=macOS Monterey 12.3.1 (21E258) [Darwin 21.4.0]
Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
.NET SDK=7.0.100-preview.3.22179.4
  [Host]     : .NET 7.0.0 (7.0.22.17504), X64 RyuJIT
  Job-QWLAYE : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT
  Job-PVIZJQ : .NET 7.0.0 (42.42.42.42424), X64 RyuJIT

PowerPlanMode=00000000-0000-0000-0000-000000000000  IterationTime=250.0000 ms  MaxIterationCount=20  
MinIterationCount=15  WarmupCount=1  
Method Job Toolchain NewLine LineLengthRange Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Allocated Alloc Ratio
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 0] 95.46 μs 0.791 μs 0.701 μs 95.26 μs 94.64 μs 96.88 μs 0.99 0.01 20.1982 - 3.27 KB 0.04
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 0] 96.53 μs 0.817 μs 0.764 μs 96.49 μs 95.33 μs 97.69 μs 1.00 0.00 466.4634 - 74.52 KB 1.00
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 1024] 41.34 μs 0.633 μs 0.592 μs 41.20 μs 40.14 μs 42.26 μs 2.04 0.03 389.4857 - 62.2 KB 1.72
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 0, 1024] 20.29 μs 0.259 μs 0.229 μs 20.28 μs 19.87 μs 20.59 μs 1.00 0.00 226.3446 - 36.21 KB 1.00
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 1] 173.59 μs 0.861 μs 0.672 μs 173.61 μs 171.97 μs 174.57 μs 1.43 0.01 1223.1105 38.5174 195.27 KB 1.78
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 1] 121.56 μs 1.225 μs 1.086 μs 121.45 μs 119.98 μs 123.56 μs 1.00 0.00 687.5000 - 109.77 KB 1.00
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 8] 127.14 μs 1.310 μs 1.022 μs 127.11 μs 125.24 μs 129.05 μs 0.89 0.01 704.2026 - 112.4 KB 1.08
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 1, 8] 142.68 μs 1.664 μs 1.556 μs 141.88 μs 141.10 μs 146.03 μs 1.00 0.00 649.5536 - 103.76 KB 1.00
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 9, 32] 52.95 μs 0.476 μs 0.422 μs 52.96 μs 52.08 μs 53.57 μs 0.96 0.02 352.9040 - 56.42 KB 1.07
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 9, 32] 55.26 μs 0.663 μs 0.620 μs 55.15 μs 54.46 μs 56.29 μs 1.00 0.00 331.1404 - 52.59 KB 1.00
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 33, 128] 37.09 μs 0.412 μs 0.385 μs 37.07 μs 36.51 μs 37.93 μs 1.35 0.03 279.9233 - 44.7 KB 1.12
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 33, 128] 27.41 μs 0.533 μs 0.499 μs 27.33 μs 26.54 μs 28.58 μs 1.00 0.00 250.0000 - 39.94 KB 1.00
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 129, 1024] 41.62 μs 0.685 μs 0.640 μs 41.56 μs 40.53 μs 42.85 μs 2.02 0.04 396.4793 - 63.39 KB 1.73
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [ 129, 1024] 20.58 μs 0.199 μs 0.176 μs 20.62 μs 20.23 μs 20.88 μs 1.00 0.00 229.1398 - 36.68 KB 1.00
ReadLine Job-QWLAYE /artifacts-main/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [1025, 2048] 50.90 μs 0.850 μs 0.710 μs 51.18 μs 49.68 μs 51.57 μs 2.47 0.04 522.3354 3.3307 90.86 KB 2.44
ReadLine Job-PVIZJQ /artifacts/bin/testhost/net7.0-OSX-Release-x64/shared/Microsoft.NETCore.App/7.0.0/corerun N [1025, 2048] 20.64 μs 0.240 μs 0.212 μs 20.65 μs 20.23 μs 21.02 μs 1.00 0.00 230.7441 - 37.24 KB 1.00

@danmoseley
Copy link
Member

I don't think you can have a faster path here without making shaky assumptions. I think just IndexOfAny is good.

@Trapov
Copy link
Author

Trapov commented May 13, 2022

@danmoseley Do we need something else in this PR or we still waiting on something? (I don't know what to change)

@Trapov
Copy link
Author

Trapov commented Jun 11, 2022

@danmoseley @adamsitnik any update on the PR?

@danmoseley
Copy link
Member

oops, overlooked this one. @stephentoub do you have any remaining concerns about the approach (I do think we need to stop returning to pool on array)

@stephentoub
Copy link
Member

@stephentoub do you have any remaining concerns about the approach

This is going to conflict with #69888. @GrabYourPitchforks' change is fixing some functional issues in addition to perf optimizations. I know this PR has been open longer, but I'd like to land @GrabYourPitchforks' change first and then revisit this.

@Trapov
Copy link
Author

Trapov commented Jun 12, 2022

I need to look into #69888 to merge it correctly then, I guess. Tag me, please, when it's going to be merged?

@Trapov
Copy link
Author

Trapov commented Aug 30, 2022

Any updates? @danmoseley

@danmoseley
Copy link
Member

@Trapov unfortunately #69888 was paused for a while as @GrabYourPitchforks was working on something else. That's been picked up again, although I see there's test failures. So unfortunately, this remains waiting..

@Trapov
Copy link
Author

Trapov commented Aug 30, 2022

No problem, just tag me please when it's going to be merged. @danmoseley

@stephentoub
Copy link
Member

@Trapov, thanks for your patience and for working on this. #69888 is about to be merged, but at this point I think it also covers most or all of what you were trying to accomplish in this PR. If there's anything additional to be done, once that's merged you can rebase and filter this down to just the incremental portions. Otherwise, we can probably close it. Again, thank you.

@Trapov
Copy link
Author

Trapov commented Nov 7, 2022

@stephentoub it covers mostly everything as you said. so probably better to close this PR.

@stephentoub
Copy link
Member

it covers mostly everything as you said. so probably better to close this PR.

Ok. Thanks very much, @Trapov.

@stephentoub stephentoub closed this Nov 7, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.IO community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants