Skip to content

Conversation

@TanayParikh
Copy link
Contributor

@TanayParikh TanayParikh commented Aug 3, 2020

1/2 PRs for https://github.com/dotnet/aspnetcore/issues/23170

  • I was running into quite a few hash-collisions so decided to revamp the hashing logic throughout the TagHelperDescriptor to minimize collisions.
  • Added (collision) testing.
  • Added a TagHelperDescriptor memory cache.

The other PR utilizes these changes: dotnet/razor#2307

1/2 PRs for https://github.com/dotnet/aspnetcore/issues/23170

The other PR will be in aspnetcore-tooling to utilize these changes.
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Copy link
Contributor Author

@TanayParikh TanayParikh Aug 4, 2020

Choose a reason for hiding this comment

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

Shared file update from tooling.

TanayParikh added a commit to dotnet/razor that referenced this pull request Aug 4, 2020
2/2 PRs for https://github.com/dotnet/aspnetcore/issues/23170

Other PR: dotnet/aspnetcore#24551

- Used the project for a short period of time
	- Created a file, navigated between files, made some edits.
	- I ran into 238 cache misses (unique taghelper allocations) and 4612 cache hits
	- 95%+ cache hit
	- Scales up incredibly well. Number of TagHelperDescriptors unlikely to change unless
		- New files added
		- New assemblies / packages added

### Benchmarks
|                   Method |     Mean |    Error |   StdDev |    Op/s | Allocated |
|------------------------- |---------:|---------:|---------:|--------:|----------:|
| 'TagHelpers GetHashCode' | 0.137 ms | 1.279 us | 1.196 us | 7,306.8 |  17.93 KB |

|                                    Method          |     Mean |     Error |    StdDev |  Op/s |   Gen 0 |   Gen 1 |   Gen 2 | Allocated |
|----------------------------------------------------|---------:|----------:|----------:|------:|--------:|--------:|--------:|----------:|
| 'Cached Razor TagHelper Roundtrip Serialization'   | 3.243 ms | 0.0634 ms | 0.0593 ms | 308.4 | 42.9688 | 39.0625 | 35.1563 |   1.95 MB |
| 'Uncached Razor TagHelper Roundtrip Serialization' | 4.891 ms | 0.1082 ms | 0.0959 ms | 204.4 | 78.1250 | 54.6875 | 39.0625 |   3.85 MB |

 Note `GetHashCode` has minimal perf overhead in comparison to the underlying serialization/deserialization cost. As a result we get 33% faster roundtrip serialization and half the memory utilization.

|                                    Method |     Mean |     Error |    StdDev |  Op/s |   Gen 0 |   Gen 1 |   Gen 2 | Allocated |
|------------------------------------------ |---------:|----------:|----------:|------:|--------:|--------:|--------:|----------:|
|    'Before Razor TagHelper Serialization' | 1.782 ms | 0.0353 ms | 0.0378 ms | 561.3 | 35.1563 | 35.1563 | 35.1563 |   1.11 MB |
|     'After Razor TagHelper Serialization' | 1.932 ms | 0.0385 ms | 0.0632 ms | 517.6 | 35.1563 | 35.1563 | 35.1563 |   1.13 MB |
|  'Before Razor TagHelper Deserialization' | 4.198 ms | 0.0173 ms | 0.0161 ms | 238.2 | 39.0625 | 15.6250 |       - |   3.28 MB |
|   'After Razor TagHelper Deserialization' | 4.279 ms | 0.0215 ms | 0.0201 ms | 233.7 | 39.0625 | 15.6250 |       - |   3.29 MB |

Note, marginally higher CPU/Memory for unidirectional serialization/deserialization due to cost of hashing. Note this benchmark would not benefit for caching.
@TanayParikh TanayParikh marked this pull request as ready for review August 4, 2020 01:17
@TanayParikh TanayParikh changed the title Razor TagHelperDescriptor Hashing and Caching Razor TagHelperDescriptor Hashing Aug 4, 2020
@ghost
Copy link

ghost commented Aug 4, 2020

Hello @TanayParikh!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@Pilchie Pilchie added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Aug 4, 2020
@TanayParikh TanayParikh merged commit 29ceed2 into master Aug 4, 2020
@TanayParikh TanayParikh deleted the taparik/taghelper_caching branch August 4, 2020 20:06
TanayParikh added a commit to dotnet/razor that referenced this pull request Aug 5, 2020
* TagHelperDescriptor Hashing and Caching

2/2 PRs for https://github.com/dotnet/aspnetcore/issues/23170

Other PR: dotnet/aspnetcore#24551

- Used the project for a short period of time
	- Created a file, navigated between files, made some edits.
	- I ran into 238 cache misses (unique taghelper allocations) and 4612 cache hits
	- 95%+ cache hit
	- Scales up incredibly well. Number of TagHelperDescriptors unlikely to change unless
		- New files added
		- New assemblies / packages added

### Benchmarks
|                   Method |     Mean |    Error |   StdDev |    Op/s | Allocated |
|------------------------- |---------:|---------:|---------:|--------:|----------:|
| 'TagHelpers GetHashCode' | 0.137 ms | 1.279 us | 1.196 us | 7,306.8 |  17.93 KB |

|                                    Method          |     Mean |     Error |    StdDev |  Op/s |   Gen 0 |   Gen 1 |   Gen 2 | Allocated |
|----------------------------------------------------|---------:|----------:|----------:|------:|--------:|--------:|--------:|----------:|
| 'Cached Razor TagHelper Roundtrip Serialization'   | 3.243 ms | 0.0634 ms | 0.0593 ms | 308.4 | 42.9688 | 39.0625 | 35.1563 |   1.95 MB |
| 'Uncached Razor TagHelper Roundtrip Serialization' | 4.891 ms | 0.1082 ms | 0.0959 ms | 204.4 | 78.1250 | 54.6875 | 39.0625 |   3.85 MB |

 Note `GetHashCode` has minimal perf overhead in comparison to the underlying serialization/deserialization cost. As a result we get 33% faster roundtrip serialization and half the memory utilization.

|                                    Method |     Mean |     Error |    StdDev |  Op/s |   Gen 0 |   Gen 1 |   Gen 2 | Allocated |
|------------------------------------------ |---------:|----------:|----------:|------:|--------:|--------:|--------:|----------:|
|    'Before Razor TagHelper Serialization' | 1.782 ms | 0.0353 ms | 0.0378 ms | 561.3 | 35.1563 | 35.1563 | 35.1563 |   1.11 MB |
|     'After Razor TagHelper Serialization' | 1.932 ms | 0.0385 ms | 0.0632 ms | 517.6 | 35.1563 | 35.1563 | 35.1563 |   1.13 MB |
|  'Before Razor TagHelper Deserialization' | 4.198 ms | 0.0173 ms | 0.0161 ms | 238.2 | 39.0625 | 15.6250 |       - |   3.28 MB |
|   'After Razor TagHelper Deserialization' | 4.279 ms | 0.0215 ms | 0.0201 ms | 233.7 | 39.0625 | 15.6250 |       - |   3.29 MB |

Note, marginally higher CPU/Memory for unidirectional serialization/deserialization due to cost of hashing. Note this benchmark would not benefit for caching.


* Add duplicate key safeguard in memory cache

https://dev.azure.com/dnceng/public/_build/results?buildId=757405&view=ms.vss-test-web.build-test-results-tab&runId=23507082&resultId=100079&paneView=debug

> System.ArgumentException : The key already existed in the dictionary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants