Skip to content

Use a binary search when trying to find source mappings#9291

Merged
davidwengier merged 1 commit intodotnet:mainfrom
davidwengier:DocumentMappingPerf
Sep 16, 2023
Merged

Use a binary search when trying to find source mappings#9291
davidwengier merged 1 commit intodotnet:mainfrom
davidwengier:DocumentMappingPerf

Conversation

@davidwengier
Copy link
Member

@davidwengier davidwengier commented Sep 15, 2023

Last one before I go :)

Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1886121 which is the first non-compiler stack when searching PRISM for "Razor" 😁

The benchmark results look pretty good, but are actually probably worse than real world, as the benchmark code has the number of locations being mapped increases with the size of the file. In the real world, I expect large files will get a huge speed boost in normal scenarios, which just map one or two positions. I could write another benchmark that just maps one location, but that feels unnecssary.

Method Job Toolchain Blocks Mean Error StdDev Median Min Max Allocated
Loop Job-EJEXYW .NET 7.0 1 121.41 ns 1.919 ns 1.971 ns 121.31 ns 119.08 ns 126.19 ns -
BinarySearch Job-EJEXYW .NET 7.0 1 28.79 ns 0.599 ns 1.064 ns 28.43 ns 27.52 ns 31.64 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 1 142.07 ns 1.635 ns 1.529 ns 141.90 ns 140.08 ns 144.52 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 1 94.94 ns 0.402 ns 0.376 ns 94.80 ns 94.52 ns 95.58 ns -
Loop Job-EJEXYW .NET 7.0 2 449.88 ns 4.307 ns 3.818 ns 448.83 ns 443.90 ns 457.20 ns -
BinarySearch Job-EJEXYW .NET 7.0 2 55.77 ns 1.152 ns 1.183 ns 55.39 ns 54.30 ns 57.89 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 2 497.05 ns 9.147 ns 8.556 ns 496.95 ns 480.13 ns 512.26 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 2 163.87 ns 1.271 ns 1.127 ns 163.58 ns 162.38 ns 166.17 ns -
Loop Job-EJEXYW .NET 7.0 3 1,011.49 ns 11.453 ns 10.153 ns 1,008.33 ns 995.00 ns 1,034.09 ns -
BinarySearch Job-EJEXYW .NET 7.0 3 97.23 ns 0.657 ns 0.582 ns 97.06 ns 96.46 ns 98.49 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 3 996.09 ns 4.405 ns 3.904 ns 995.91 ns 991.04 ns 1,004.37 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 3 285.21 ns 1.583 ns 1.403 ns 285.13 ns 283.20 ns 287.96 ns -
Loop Job-EJEXYW .NET 7.0 4 1,405.87 ns 16.051 ns 13.403 ns 1,406.58 ns 1,382.90 ns 1,435.57 ns -
BinarySearch Job-EJEXYW .NET 7.0 4 183.43 ns 1.812 ns 1.606 ns 183.30 ns 180.03 ns 185.58 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 4 1,416.94 ns 7.961 ns 6.216 ns 1,417.48 ns 1,403.39 ns 1,425.49 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 4 433.39 ns 4.250 ns 3.549 ns 433.57 ns 422.44 ns 436.62 ns -
Loop Job-EJEXYW .NET 7.0 5 2,202.38 ns 25.393 ns 22.510 ns 2,193.64 ns 2,162.36 ns 2,251.37 ns -
BinarySearch Job-EJEXYW .NET 7.0 5 250.77 ns 1.980 ns 1.852 ns 250.79 ns 247.58 ns 254.48 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 5 2,357.81 ns 23.776 ns 21.076 ns 2,354.44 ns 2,333.86 ns 2,397.57 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 5 595.56 ns 4.201 ns 3.508 ns 593.86 ns 591.79 ns 602.14 ns -
Loop Job-EJEXYW .NET 7.0 10 9,368.65 ns 49.970 ns 44.297 ns 9,355.35 ns 9,315.63 ns 9,442.22 ns -
BinarySearch Job-EJEXYW .NET 7.0 10 480.49 ns 3.440 ns 2.873 ns 479.57 ns 476.41 ns 485.98 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 10 9,078.03 ns 74.415 ns 69.608 ns 9,066.05 ns 8,993.93 ns 9,217.77 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 10 1,314.53 ns 24.266 ns 54.772 ns 1,295.18 ns 1,251.01 ns 1,462.78 ns -
Loop Job-EJEXYW .NET 7.0 20 39,055.48 ns 774.517 ns 1,825.628 ns 38,457.61 ns 36,896.37 ns 43,645.76 ns -
BinarySearch Job-EJEXYW .NET 7.0 20 1,127.51 ns 21.360 ns 18.935 ns 1,121.38 ns 1,106.39 ns 1,182.33 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 20 35,422.53 ns 220.508 ns 184.134 ns 35,422.35 ns 35,079.98 ns 35,730.04 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 20 2,840.21 ns 16.334 ns 13.639 ns 2,841.09 ns 2,820.72 ns 2,868.34 ns -
Loop Job-EJEXYW .NET 7.0 30 82,537.95 ns 468.859 ns 415.631 ns 82,460.77 ns 81,912.99 ns 83,465.56 ns -
BinarySearch Job-EJEXYW .NET 7.0 30 2,014.56 ns 24.341 ns 22.768 ns 2,016.66 ns 1,970.81 ns 2,055.29 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 30 78,869.85 ns 797.527 ns 706.987 ns 78,931.62 ns 76,675.37 ns 79,690.09 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 30 4,575.58 ns 28.790 ns 25.522 ns 4,567.62 ns 4,535.05 ns 4,623.71 ns -
Loop Job-EJEXYW .NET 7.0 40 143,284.01 ns 1,612.976 ns 1,346.908 ns 142,920.01 ns 141,638.59 ns 146,687.81 ns -
BinarySearch Job-EJEXYW .NET 7.0 40 2,748.96 ns 51.230 ns 52.610 ns 2,738.13 ns 2,693.60 ns 2,875.02 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 40 137,274.99 ns 1,108.642 ns 982.782 ns 137,014.59 ns 135,858.52 ns 139,426.73 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 40 6,500.32 ns 63.546 ns 56.332 ns 6,502.47 ns 6,352.88 ns 6,585.41 ns -
Loop Job-EJEXYW .NET 7.0 100 900,451.51 ns 3,086.598 ns 2,887.205 ns 899,910.60 ns 896,588.23 ns 906,437.74 ns -
BinarySearch Job-EJEXYW .NET 7.0 100 9,992.34 ns 170.209 ns 150.886 ns 9,979.46 ns 9,794.22 ns 10,284.17 ns -
Loop Job-DSHCRS .NET Framework 4.7.2 100 935,231.61 ns 4,127.210 ns 3,860.595 ns 935,746.78 ns 926,698.73 ns 942,571.48 ns -
BinarySearch Job-DSHCRS .NET Framework 4.7.2 100 20,033.10 ns 221.366 ns 184.851 ns 19,969.20 ns 19,801.28 ns 20,436.52 ns -

@davidwengier davidwengier requested review from a team as code owners September 15, 2023 06:58
@ToddGrun
Copy link
Contributor

    foreach (var mapping in generatedDocument.SourceMappings)

Being greedy here, but can this one be binary-searched too?


Refers to: src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorDocumentMappingService.cs:362 in da79973. [](commit_id = da79973, deletion_comment = False)

@ToddGrun
Copy link
Contributor

    for (var i = generatedDocument.SourceMappings.Count - 1; i >= 0; i--)

and here?


Refers to: src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorDocumentMappingService.cs:718 in da79973. [](commit_id = da79973, deletion_comment = False)

@davidwengier
Copy link
Member Author

Being greedy here, but can this one be binary-searched too?

That loop checks the mapping.OriginalSpan and hence the array is not ordered. It might be beneficial to sort then binary search, but I don't know. Maybe based on a heuristic of file size?

We could also change how we store the mappings, and store a reverse map, but need to work out if that's worth it, since that loop didn't show up in PRISM (or shows up lower?). Also if we're changing how the compiler produces the data, we can change to allow us to use the built in binary search method, and not need the lambda, but again that might not be worth it.

TL;DR I'm OOF for two weeks from Monday and ran out of time 😁

Copy link
Member

@DustinCampbell DustinCampbell left a comment

Choose a reason for hiding this comment

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

Very nice!

Comment on lines +126 to +134
if (generatedDocument is null)
{
throw new ArgumentNullException(nameof(generatedDocument));
}

if (generatedDocument.CodeDocument is not { } codeDocument)
{
throw new InvalidOperationException("Cannot use document mapping service on a generated document that has a null CodeDocument.");
}
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to benchmark the argument checking?

Copy link
Member Author

Choose a reason for hiding this comment

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

Do we need to benchmark "Is a binary search better than a loop?"? 😛

@davidwengier davidwengier merged commit a464b97 into dotnet:main Sep 16, 2023
@davidwengier davidwengier deleted the DocumentMappingPerf branch September 16, 2023 00:10
@ghost ghost added this to the Next milestone Sep 16, 2023
@davidwengier
Copy link
Member Author

Logged #9293 to follow up on some of your suggestions @ToddGrun

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants