KEMBAR78
Optimize allocations in HttpUtility.UrlDecodeToBytes for smaller inputs. by TrayanZapryanov · Pull Request #102753 · dotnet/runtime · GitHub
Skip to content

Conversation

@TrayanZapryanov
Copy link
Contributor

Optimize byte[] allocation when:

  1. input string is less than 256 chars(might change after review)
  2. maximum encoded bytes according Encoding is less than 512(might change after review)

If a "happy" path is taken, we will have allocations equals to the returned byte[].
Drawback: method will be a bit slower as we need to calculate Encoding.GetMaxByteCount(xxx)

@ghost ghost added the area-System.Net label May 28, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label May 28, 2024
@dotnet-policy-service
Copy link
Contributor

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

@TrayanZapryanov
Copy link
Contributor Author

Benchmark:

[MemoryDiagnoser]
public class HttpUtilityBenchmarks
{
	[Benchmark(Baseline = true)]
	public byte[] UrlDecodeToBytes() => HttpUtility.UrlDecodeToBytes("http://127.0.0.1:8080/app%%Dir/page.aspx?foo=b%%r");

	[Benchmark]
	public byte[] UrlDecodeToBytes_PR() => MyHttpUtility.UrlDecodeToBytes("http://127.0.0.1:8080/app%%Dir/page.aspx?foo=b%%r");
}

Results:

Method Mean Error StdDev Ratio Gen0 Allocated Alloc Ratio
UrlDecodeToBytes 90.45 ns 1.436 ns 1.343 ns 1.00 0.0191 160 B 1.00
UrlDecodeToBytes_PR 80.87 ns 0.632 ns 0.560 ns 0.89 0.0095 80 B 0.50

Copy link
Member

@MihaZupan MihaZupan left a comment

Choose a reason for hiding this comment

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

Thank you for looking into making improvements here.
Is the existing test coverage here sufficient to catch any potential regressions?

@EgorBo
Copy link
Member

EgorBo commented May 28, 2024

@EgorBot -intel -arm64 -profiler

using BenchmarkDotNet.Attributes;
using System.Web;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<HttpUtilityBenchmarks>(args: args);

public class HttpUtilityBenchmarks
{
    [Benchmark]
    public byte[] UrlDecodeToBytes() => 
        HttpUtility.UrlDecodeToBytes(
            "http://127.0.0.1:8080/app%%Dir/page.aspx?foo=b%%r");
}

@EgorBot
Copy link

EgorBot commented May 28, 2024

BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 16 logical and 8 physical cores
  Job-PTRDNU : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  Job-JRGPMR : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Method Toolchain Mean Error Ratio
UrlDecodeToBytes Main 128.22 ns 1.150 ns 1.00
UrlDecodeToBytes PR 89.45 ns 0.144 ns 0.70
More data

See BDN_Artifacts.zip for details.

Flame graphs: Main vs PR 🔥
Hot asm: Main vs PR
Hot functions: Main vs PR

For clean perf results, make sure you have just one [Benchmark] in your app.

@EgorBot
Copy link

EgorBot commented May 28, 2024

BenchmarkDotNet v0.13.12, Ubuntu 22.04.4 LTS (Jammy Jellyfish)
Unknown processor
  Job-BUHYYP : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
  Job-KRDBSO : .NET 9.0.0 (42.42.42.42424), Arm64 RyuJIT AdvSIMD
Method Toolchain Mean Error Ratio
UrlDecodeToBytes Main 176.4 ns 0.21 ns 1.00
UrlDecodeToBytes PR 170.5 ns 0.15 ns 0.97
More data

See BDN_Artifacts.zip for details.

Flame graphs: Main vs PR 🔥
Hot asm: Main vs PR
Hot functions: Main vs PR

For clean perf results, make sure you have just one [Benchmark] in your app.


[return: NotNullIfNotNull(nameof(bytes))]
public static byte[]? UrlDecodeToBytes(byte[]? bytes) => bytes == null ? null : UrlDecodeToBytes(bytes, 0, bytes.Length);
public static byte[]? UrlDecodeToBytes(byte[]? bytes) => bytes == null ? null : HttpEncoder.UrlDecode(bytes.AsSpan(0, bytes.Length));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public static byte[]? UrlDecodeToBytes(byte[]? bytes) => bytes == null ? null : HttpEncoder.UrlDecode(bytes.AsSpan(0, bytes.Length));
public static byte[]? UrlDecodeToBytes(byte[]? bytes) => bytes == null ? null : HttpEncoder.UrlDecode(bytes.AsSpan());

For future reference (not worth restarting CI on this PR)

@MihaZupan MihaZupan added this to the 9.0.0 milestone May 29, 2024
@MihaZupan MihaZupan merged commit 3ff4051 into dotnet:main May 29, 2024
@TrayanZapryanov TrayanZapryanov deleted the optimize_httputility_urldecodetobytes branch May 29, 2024 19:12
Ruihan-Yin pushed a commit to Ruihan-Yin/runtime that referenced this pull request May 30, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Jun 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Net 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