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

Make stream buffer size configurable. #1286

Merged
merged 6 commits into from
Jul 26, 2020
Merged

Make stream buffer size configurable. #1286

merged 6 commits into from
Jul 26, 2020

Conversation

JimBobSquarePants
Copy link
Member

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Adds a new property Configuration.StreamProcessingBufferSize which allows the configuration of internal buffers used to reduce synchronization costs of network streaming. The value defaults to 8096 with a minimum of 128.

Fixes #1276

@codecov
Copy link

codecov bot commented Jul 24, 2020

Codecov Report

Merging #1286 into master will increase coverage by 0.02%.
The diff coverage is 85.71%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1286      +/-   ##
==========================================
+ Coverage   82.67%   82.70%   +0.02%     
==========================================
  Files         697      697              
  Lines       30659    30677      +18     
  Branches     3467     3470       +3     
==========================================
+ Hits        25347    25370      +23     
+ Misses       4612     4604       -8     
- Partials      700      703       +3     
Flag Coverage Δ
#unittests 82.70% <85.71%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/ImageSharp/Formats/Gif/GifDecoder.cs 50.00% <50.00%> (ø)
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs 48.38% <50.00%> (ø)
src/ImageSharp/Formats/Png/PngDecoder.cs 48.14% <50.00%> (ø)
src/ImageSharp/Formats/Tga/TgaDecoder.cs 46.42% <50.00%> (ø)
src/ImageSharp/Configuration.cs 100.00% <100.00%> (ø)
src/ImageSharp/Formats/Bmp/BmpDecoder.cs 88.88% <100.00%> (ø)
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs 92.67% <100.00%> (-0.44%) ⬇️
src/ImageSharp/Formats/Png/PngDecoderCore.cs 90.44% <100.00%> (-0.21%) ⬇️
src/ImageSharp/IO/BufferedReadStream.cs 92.42% <100.00%> (+6.82%) ⬆️
src/ImageSharp/Image.FromStream.cs 79.68% <100.00%> (ø)
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ef5e21b...6aa6472. Read the comment docs.

Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

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

One question, otherwise looks good if @TedStryker can confirm the changes work.

src/ImageSharp/Configuration.cs Outdated Show resolved Hide resolved
Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

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

I made the BufferedReadStreamTests parametric, which makes some of them fail:

https://gist.github.com/antonfirsov/304c2a9d008446c78f786ddbd161767c

We need a reasonable minimum value.
Also fails for some weird numbers. We don't need to work with those OFC, but then we need to add checks to ensure the provided value is divisible by some pow2 number.

get => this.streamProcessingBufferSize;
set
{
if (value <= 0)
Copy link
Member

Choose a reason for hiding this comment

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

Buffer size 1 and 2 will not work.

Copy link
Member Author

Choose a reason for hiding this comment

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

I’ll have a look at your tests and fix up

Copy link
Member Author

Choose a reason for hiding this comment

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

We're missing some guard sanitation on the Position property but the problem is really with the tests. They're expecting a minimum length.

Copy link
Member

@antonfirsov antonfirsov Jul 26, 2020

Choose a reason for hiding this comment

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

The main question: would Image.Load[Async] work with buffer sizes like 1, 2, 3, 503, 719 ? If not, the setter should throw.

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'll work with any length. Was just bad tests.

antonfirsov added a commit that referenced this pull request Jul 26, 2020
@JimBobSquarePants JimBobSquarePants requested review from antonfirsov and a team July 26, 2020 16:18
@JimBobSquarePants JimBobSquarePants merged commit 493e422 into master Jul 26, 2020
@JimBobSquarePants JimBobSquarePants deleted the js/fix-1276 branch July 26, 2020 21:40
@TedStryker
Copy link

Thx to you all, I'll do some benchmarks the next days and will keep you informed.

@antonfirsov
Copy link
Member

@TedStryker thanks! It's merged, so you can use our latest nightly build now.

@TedStryker
Copy link

TedStryker commented Aug 1, 2020

@antonfirsov I built my own (release/x64), hope that doesn't matter.

I did some more benchmarks locally and via SMB with a smaller picture (1MB) and a bigger one (4MB) and different values of StreamProcessingBufferSize.

tl;dr

If accessing resources via SMB (or network in general?), use .NET's default buffer size of 81920 for stream-to-stream copying (no, it's not 8192).
https://referencesource.microsoft.com/#mscorlib/system/io/stream.cs,f956b0c07e86df64
For everything else: just don't care, in case you don't load images in (tight) loops where feeding ImageSharp with await fs.ReadAsync(result, 0, (int) fs.Length) will be a bit faster.

Complete BenchmarkDotNet logs:
ConsoleApp1.BM-20200801-145121.log

Here are the benchmark results:

BenchmarkDotNet=v0.12.1, OS=Windows 8.1 (6.3.9600.0)
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
Frequency=3906254 Hz, Resolution=255.9997 ns, Timer=TSC
.NET Core SDK=3.1.400-preview-015203
  [Host]     : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT
  DefaultJob : .NET Core 3.1.5 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.27001), X64 RyuJIT

Method Mean Error StdDev
Teds_LoadAsync_1MBPic_SMB 171.3 ms 2.14 ms 2.00 ms
IS_LoadSync_1MBPic_SMB_Default 249.3 ms 3.48 ms 3.08 ms
IS_LoadAsync_1MBPic_SMB_Default 251.4 ms 4.71 ms 4.41 ms
IS_LoadSync_1MBPic_SMB_8192 250.6 ms 3.35 ms 3.13 ms
IS_LoadAsync_1MBPic_SMB_8192 248.5 ms 4.57 ms 4.05 ms
IS_LoadSync_1MBPic_SMB_81920 185.5 ms 2.47 ms 2.31 ms
IS_LoadAsync_1MBPic_SMB_81920 181.6 ms 3.32 ms 3.11 ms
IS_LoadSync_1MBPic_SMB_1048576 173.1 ms 1.55 ms 1.45 ms
IS_LoadAsync_1MBPic_SMB_1048576 170.4 ms 1.29 ms 1.20 ms
Teds_LoadAsync_4MBPic_SMB 262.7 ms 1.88 ms 1.76 ms
IS_LoadSync_4MBPic_SMB_Default 536.2 ms 9.20 ms 8.61 ms
IS_LoadAsync_4MBPic_SMB_Default 532.5 ms 4.75 ms 4.21 ms
IS_LoadSync_4MBPic_SMB_8192 522.4 ms 6.19 ms 5.49 ms
IS_LoadAsync_4MBPic_SMB_8192 520.9 ms 5.01 ms 4.44 ms
IS_LoadSync_4MBPic_SMB_81920 297.8 ms 4.56 ms 4.27 ms
IS_LoadAsync_4MBPic_SMB_81920 297.1 ms 3.15 ms 2.94 ms
IS_LoadSync_4MBPic_SMB_1048576 267.0 ms 2.13 ms 2.00 ms
IS_LoadAsync_4MBPic_SMB_1048576 269.2 ms 1.96 ms 1.84 ms
Teds_LoadAsync_1MBPic_Local 155.1 ms 2.57 ms 2.15 ms
IS_LoadSync_1MBPic_Local_Default 150.3 ms 1.10 ms 1.02 ms
IS_LoadAsync_1MBPic_Local_Default 150.5 ms 0.94 ms 0.88 ms
IS_LoadSync_1MBPic_Local_8192 151.6 ms 1.34 ms 1.25 ms
IS_LoadAsync_1MBPic_Local_8192 152.6 ms 0.91 ms 0.76 ms
IS_LoadSync_1MBPic_Local_81920 152.3 ms 0.73 ms 0.61 ms
IS_LoadAsync_1MBPic_Local_81920 151.2 ms 0.95 ms 0.84 ms
IS_LoadSync_1MBPic_Local_1048576 151.6 ms 0.95 ms 0.89 ms
IS_LoadAsync_1MBPic_Local_1048576 152.1 ms 1.64 ms 1.53 ms
Teds_LoadAsync_4MBPic_Local 233.1 ms 4.66 ms 4.98 ms
IS_LoadSync_4MBPic_Local_Default 225.7 ms 4.07 ms 4.52 ms
IS_LoadAsync_4MBPic_Local_Default 220.4 ms 1.19 ms 1.11 ms
IS_LoadSync_4MBPic_Local_8192 222.0 ms 2.32 ms 2.17 ms
IS_LoadAsync_4MBPic_Local_8192 219.9 ms 1.47 ms 1.31 ms
IS_LoadSync_4MBPic_Local_81920 219.3 ms 1.36 ms 1.20 ms
IS_LoadAsync_4MBPic_Local_81920 218.8 ms 1.25 ms 1.11 ms
IS_LoadSync_4MBPic_Local_1048576 220.3 ms 1.39 ms 1.16 ms
IS_LoadAsync_4MBPic_Local_1048576 220.0 ms 2.03 ms 1.90 ms

@TedStryker
Copy link

@JimBobSquarePants
Copy link
Member Author

@TedStryker Thanks. I might update our default value then. I wasn't aware the copy buffer default was as large as it was. The buffer default I used was from the BufferedStream class. I'll update ours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Image.Load and Image.LoadAsync is slow for network shares
3 participants