Skip to content

Commit

Permalink
Optimize HTTP2 HPack huffman decoding (dotnet#43603)
Browse files Browse the repository at this point in the history
Optimized to use 8 bits lookup tables tree with result of about 0.35 CPU utilization as oppose to former version.
Decoding table is lazy generated as ushort[].
  • Loading branch information
rokonec authored Nov 27, 2020
1 parent be0efd3 commit 7171407
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 146 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ AutoTest.Net/
# Web workbench (sass)
.sass-cache/

# BenchmarkDotNet
BenchmarkDotNet.Artifacts/

# Installshield output folder
[Ee]xpress/

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ private static IEnumerable<byte[]> InvalidEncodingData()

// Sequences that uncovered errors
{ new byte[] { 0xb6, 0xb9, 0xac, 0x1c, 0x85, 0x58, 0xd5, 0x20, 0xa4, 0xb6, 0xc2, 0xad, 0x61, 0x7b, 0x5a, 0x54, 0x25, 0x1f }, Encoding.ASCII.GetBytes("upgrade-insecure-requests") },
{ new byte[] { 0xfe, 0x53 }, Encoding.ASCII.GetBytes("\"t") }
{ new byte[] { 0xfe, 0x53 }, Encoding.ASCII.GetBytes("\"t") },
{ new byte[] { 0xff, 0xff, 0xf6, 0xff, 0xff, 0xfd, 0x68 }, new byte[] { 0xcf, 0xf0, 0x73 } },
{ new byte[] { 0xff, 0xff, 0xf9, 0xff, 0xff, 0xfd, 0x86 }, new byte[] { 0xd5, 0xc7, 0x69 } },
};

[Theory]
Expand Down Expand Up @@ -354,29 +356,6 @@ public void HuffmanEncode(int code, uint expectedEncoded, int expectedBitLength)
Assert.Equal(expectedBitLength, bitLength);
}

[Theory]
[MemberData(nameof(HuffmanData))]
public void HuffmanDecode(int code, uint encoded, int bitLength)
{
Assert.Equal(code, Huffman.DecodeValue(encoded, bitLength, out int decodedBits));
Assert.Equal(bitLength, decodedBits);
}

[Theory]
[MemberData(nameof(HuffmanData))]
public void HuffmanEncodeDecode(
int code,
// Suppresses the warning about an unused theory parameter because
// this test shares data with other methods
#pragma warning disable xUnit1026
uint encoded,
#pragma warning restore xUnit1026
int bitLength)
{
Assert.Equal(code, Huffman.DecodeValue(Huffman.Encode(code).encoded, bitLength, out int decodedBits));
Assert.Equal(bitLength, decodedBits);
}

public static TheoryData<int, uint, int> HuffmanData
{
get
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<StringResourcesPath>../../../src/Resources/Strings.resx</StringResourcesPath>
<Nullable>enable</Nullable>
<LangVersion>9.0</LangVersion>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(CommonPath)System\Net\Http\aspnetcore\Http2\Hpack\Huffman.cs"
Link="Common\System\Net\Http\aspnetcore\Http2\Hpack\Huffman.cs" />
<Compile Include="$(CommonPath)System\Net\Http\aspnetcore\Http2\Hpack\HuffmanDecodingException.cs"
Link="Common\System\Net\Http\aspnetcore\Http2\Hpack\HuffmanDecodingException.cs" />
<Compile Include="Program.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30709.64
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HPackHuffmanBenchmark", "HPackHuffmanBenchmark.csproj", "{FBC03CC3-4D63-4C17-8C09-ACE6E698B3C8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FBC03CC3-4D63-4C17-8C09-ACE6E698B3C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FBC03CC3-4D63-4C17-8C09-ACE6E698B3C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FBC03CC3-4D63-4C17-8C09-ACE6E698B3C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBC03CC3-4D63-4C17-8C09-ACE6E698B3C8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1CD6E426-3019-4B48-B2DB-1A6679912054}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Net.Http.HPack;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace System.Net.Http.Tests.Performance
{
public class HPackHuffmanBenchmark
{
private List<byte[]> _data;
private byte[] _buffer;

public HPackHuffmanBenchmark()
{
_data = new List<byte[]> {

// www.example.com
new byte[] { 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff },
// no-cache
new byte[] { 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf },
// upgrade-insecure-requests
new byte[] { 0xb6, 0xb9, 0xac, 0x1c, 0x85, 0x58, 0xd5, 0x20, 0xa4, 0xb6, 0xc2, 0xad, 0x61, 0x7b, 0x5a, 0x54, 0x25, 0x1f },
// mEO7bfwFStBMwJWfW4pmg2XL25AswjrVlfcfYbxkcS2ssduZmiKoipMH9XwoTGkb+Qnq9bcjwWbwDQzsea/vMQ==
Convert.FromBase64String("pwanY5fGHcm7o8ZeUvJqumYX5nE3Cjx0s40Skl5x+epNwkIkt/aTZjmr0Y3/zwffi6x/72Vdn4ydPHKPxf2e0FGx30bIIA=="),
};
_buffer = new byte[10000];
}

[Params(0, 1, 2, 3)]
public int DataIndex { get; set; }

[Benchmark]
public void Decode() => Huffman.Decode(_data[DataIndex], ref _buffer);
}

public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run(typeof(Program).Assembly);
}
}
}

0 comments on commit 7171407

Please sign in to comment.