akamsteeg / AtleX.HaveIBeenPwned

A fully async .NET Standard client library for the API of HaveIBeenPwned.com
https://www.nuget.org/packages/AtleX.HaveIBeenPwned/
MIT License
5 stars 0 forks source link

HaveIBeenPwnedClient.IsPwnedPasswordAsync() uses an excessive amount of memory #47

Closed akamsteeg closed 4 years ago

akamsteeg commented 4 years ago

In HaveIBeenPwnedClient.IsPwnedPasswordAsync(), or actually HaveIBeenPwnedClient.IsPwnedPasswordInternalAsync(), we use an excessive amount of memory. We create a StreamReader to read the response stream fully into a new string, and then do a Contains() on that.

https://github.com/akamsteeg/AtleX.HaveIBeenPwned/blob/e6a9126f745c8776a1afb04b1f48ca02770e2e1b/src/AtleX.HaveIBeenPwned/HaveIBeenPwnedClient.cs#L350..L355

This requires around 110KB of memory for every request.

Suggestion: Skip the stream and StreamReader. Just use ReadAsStringAsync() on the response to read directly to a string.

akamsteeg commented 4 years ago

Old:

Method Job Toolchain breachMode Mean Error StdDev Median Ratio RatioSD Gen 0 Gen 1 Gen 2 Allocated
IsPwnedPasswordAsync Job-TEGKPY .NET Core 2.1 ? 71.118 μs 1.4146 μs 3.3893 μs 71.100 μs 1.23 0.05 25.6348 - - 118.14 KB
IsPwnedPasswordAsync Job-GJVZXI .NET Core 3.1 ? 55.144 μs 1.0935 μs 1.1701 μs 55.018 μs 1.00 0.00 25.2686 4.2114 - 116.58 KB
IsPwnedPasswordAsync Job-VWBFQF net48 ? 103.477 μs 1.9824 μs 2.2829 μs 102.961 μs 1.88 0.05 26.6113 3.5400 - 123.41 KB

New:

Method Job Toolchain breachMode Mean Error StdDev Median Ratio RatioSD Gen 0 Gen 1 Gen 2 Allocated
IsPwnedPasswordAsync Job-VQTSNE .NET Core 2.1 ? 65.549 μs 1.2758 μs 1.1934 μs 64.890 μs 1.38 0.03 14.1602 0.9766 - 65.59 KB
IsPwnedPasswordAsync Job-BHPDQD .NET Core 3.1 ? 47.402 μs 0.6711 μs 0.5949 μs 47.286 μs 1.00 0.00 14.1602 0.9766 - 65.49 KB
IsPwnedPasswordAsync Job-MCEJQP net48 ? 151.673 μs 2.4148 μs 2.2588 μs 152.235 μs 3.20 0.07 20.7520 - - 96.95 KB

The performance increase (time) is negligable, but the memory usage on the .NET Core TFMs is only ~60% of what is was. Profit! 🎉