-
Notifications
You must be signed in to change notification settings - Fork 157
Performance improvements and more benchmarks #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
ef47838
more benchmarks including cold start
twcclegg 4e93aad
performance tweaks
twcclegg 13513e2
make aot compatible
twcclegg 4e43235
restore compression
twcclegg cbc6cb4
dash helper
twcclegg 28c19ca
add codecov settings
twcclegg f4f483f
address comments
twcclegg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| coverage: | ||
| status: | ||
| project: | ||
| default: | ||
| target: auto | ||
| # Allow overall coverage to drop by up to 1% without failing the build. | ||
| # Catches gradual rot without flagging tiny noise from refactors. | ||
| threshold: 1% | ||
| patch: | ||
| default: | ||
| # Patch coverage on changed lines must be at least 90%. Catches PRs that | ||
| # ship large uncovered blocks while tolerating a few hard-to-test lines | ||
| # (resource-loader plumbing, AOT-only branches, etc.). | ||
| target: 90% |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
csharp/PhoneNumbers.PerformanceTest/Benchmarks/AsYouTypeFormatterBenchmark.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Jobs; | ||
|
|
||
| namespace PhoneNumbers.PerformanceTest.Benchmarks | ||
| { | ||
| [MemoryDiagnoser] | ||
| [SimpleJob(RuntimeMoniker.Net48)] | ||
| [SimpleJob(RuntimeMoniker.Net80)] | ||
| [SimpleJob(RuntimeMoniker.Net90)] | ||
| public class AsYouTypeFormatterBenchmark | ||
| { | ||
| #if NETFRAMEWORK | ||
| private PhoneNumberUtil _phoneNumberUtil = null; | ||
| private PhoneNumberBenchmarkCase[] _phoneNumbers = null; | ||
| #else | ||
| private PhoneNumberUtil _phoneNumberUtil = null!; | ||
| private PhoneNumberBenchmarkCase[] _phoneNumbers = null!; | ||
| #endif | ||
|
|
||
| [Params(1000, 10000)] | ||
| public int PhoneNumberCount { get; set; } | ||
|
|
||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| _phoneNumberUtil = PhoneNumberUtil.GetInstance(); | ||
| _phoneNumbers = PhoneNumberBenchmarkData.Create(_phoneNumberUtil, PhoneNumberCount); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public int InputDigitPerKeystroke() | ||
| { | ||
| var checksum = 0; | ||
|
|
||
| for (var i = 0; i < _phoneNumbers.Length; i++) | ||
| { | ||
| var phoneNumber = _phoneNumbers[i]; | ||
| var formatter = _phoneNumberUtil.GetAsYouTypeFormatter(phoneNumber.DefaultRegion); | ||
|
|
||
| var input = phoneNumber.NumberToParse; | ||
| for (var c = 0; c < input.Length; c++) | ||
| checksum += formatter.InputDigit(input[c]).Length; | ||
| } | ||
|
|
||
| return checksum; | ||
| } | ||
| } | ||
| } |
91 changes: 91 additions & 0 deletions
91
csharp/PhoneNumbers.PerformanceTest/Benchmarks/ColdStartBenchmark.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Engines; | ||
| using BenchmarkDotNet.Jobs; | ||
|
|
||
| namespace PhoneNumbers.PerformanceTest.Benchmarks | ||
| { | ||
| /// <summary> | ||
| /// Cold-start measurements. Each invocation builds a fresh <see cref="PhoneNumberUtil"/> so the | ||
| /// embedded-resource metadata cache is empty — this is the cost a consumer pays on their first | ||
| /// use of the library, before any region metadata has been loaded. | ||
| /// </summary> | ||
| [MemoryDiagnoser] | ||
| [SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.Net48, launchCount: 1, warmupCount: 1, iterationCount: 20, invocationCount: 1)] | ||
| [SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 20, invocationCount: 1)] | ||
| [SimpleJob(RunStrategy.ColdStart, RuntimeMoniker.Net90, launchCount: 1, warmupCount: 1, iterationCount: 20, invocationCount: 1)] | ||
| public class ColdStartBenchmark | ||
| { | ||
| // The country-code-to-region map and one fresh PhoneNumberUtil are kept around so the | ||
| // FirstRegionLookup benchmark has a pre-constructed util whose region cache has NOT been | ||
| // touched for the target region (we pick a region we never look up during setup). | ||
| #if NETFRAMEWORK | ||
| private PhoneNumberUtil _warmInstance = null; | ||
| private string[] _supportedRegions = null; | ||
| #else | ||
| private PhoneNumberUtil _warmInstance = null!; | ||
| private string[] _supportedRegions = null!; | ||
| #endif | ||
|
|
||
| // Region selected for FirstRegionLookup. Chosen as a small-but-real region so its metadata | ||
| // payload size is representative of the average region rather than an outlier like US/CN. | ||
| private const string TargetRegion = "CH"; | ||
|
|
||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| // Force JIT of the metadata-loading path so we measure steady-state cold-start cost | ||
| // rather than first-ever-invocation JIT noise. We deliberately use a different region | ||
| // than TargetRegion so the per-region cache stays cold for that one in FirstRegionLookup. | ||
| _warmInstance = PhoneNumberUtil.GetInstance(); | ||
| _supportedRegions = new string[_warmInstance.GetSupportedRegions().Count]; | ||
| _warmInstance.GetSupportedRegions().CopyTo(_supportedRegions); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Bare construction: builds the country-code map and runs the constructor. No region | ||
| /// metadata is loaded — that all happens lazily on first <see cref="PhoneNumberUtil.Parse"/>. | ||
| /// </summary> | ||
| [Benchmark] | ||
| public PhoneNumberUtil CreateInstance() | ||
| { | ||
| return new PhoneNumberUtil( | ||
| new EmbeddedResourceMetadataLoader(), | ||
| CountryCodeToRegionCodeMap.GetCountryCodeToRegionCodeMap()); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Construct + force-load every region's metadata. Represents a long-running process that | ||
| /// will eventually touch every region — the total cold cost they pay across their lifetime. | ||
| /// </summary> | ||
| [Benchmark] | ||
| public int CreateInstanceAndLoadAllRegions() | ||
| { | ||
| var util = new PhoneNumberUtil( | ||
| new EmbeddedResourceMetadataLoader(), | ||
| CountryCodeToRegionCodeMap.GetCountryCodeToRegionCodeMap()); | ||
|
|
||
| var checksum = 0; | ||
| for (var i = 0; i < _supportedRegions.Length; i++) | ||
| { | ||
| var meta = util.GetMetadataForRegion(_supportedRegions[i]); | ||
| if (meta != null) | ||
| checksum++; | ||
| } | ||
| return checksum; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Isolated per-region lazy load against a pre-constructed instance. Builds one fresh util | ||
| /// per invocation so <see cref="PhoneNumberUtil.GetMetadataForRegion"/> hits the binary | ||
| /// loader instead of the in-memory cache. | ||
| /// </summary> | ||
| [Benchmark] | ||
| public PhoneMetadata FirstRegionLookup() | ||
| { | ||
| var util = new PhoneNumberUtil( | ||
| new EmbeddedResourceMetadataLoader(), | ||
| CountryCodeToRegionCodeMap.GetCountryCodeToRegionCodeMap()); | ||
| return util.GetMetadataForRegion(TargetRegion); | ||
| } | ||
| } | ||
| } |
58 changes: 58 additions & 0 deletions
58
csharp/PhoneNumbers.PerformanceTest/Benchmarks/ParsingHelpersBenchmark.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Jobs; | ||
|
|
||
| namespace PhoneNumbers.PerformanceTest.Benchmarks | ||
| { | ||
| [MemoryDiagnoser] | ||
| [SimpleJob(RuntimeMoniker.Net48)] | ||
| [SimpleJob(RuntimeMoniker.Net80)] | ||
| [SimpleJob(RuntimeMoniker.Net90)] | ||
| public class ParsingHelpersBenchmark | ||
| { | ||
| #if NETFRAMEWORK | ||
| private string[] _inputs = null; | ||
| private string[] _inputsWithLeadingJunk = null; | ||
| #else | ||
| private string[] _inputs = null!; | ||
| private string[] _inputsWithLeadingJunk = null!; | ||
| #endif | ||
|
|
||
| [Params(1000, 10000)] | ||
| public int PhoneNumberCount { get; set; } | ||
|
|
||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| var phoneNumberUtil = PhoneNumberUtil.GetInstance(); | ||
| var cases = PhoneNumberBenchmarkData.Create(phoneNumberUtil, PhoneNumberCount); | ||
|
|
||
| _inputs = new string[cases.Length]; | ||
| _inputsWithLeadingJunk = new string[cases.Length]; | ||
| for (var i = 0; i < cases.Length; i++) | ||
| { | ||
| _inputs[i] = cases[i].NumberToParse; | ||
| // Forces ExtractPossibleNumber to actually slice (the common "clean input" case | ||
| // is measured separately by _inputs). | ||
| _inputsWithLeadingJunk[i] = "abc " + cases[i].NumberToParse; | ||
| } | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public int ExtractPossibleNumber_CleanInput() | ||
| { | ||
| var checksum = 0; | ||
| for (var i = 0; i < _inputs.Length; i++) | ||
| checksum += PhoneNumberUtil.ExtractPossibleNumber(_inputs[i]).Length; | ||
| return checksum; | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public int ExtractPossibleNumber_WithLeadingJunk() | ||
| { | ||
| var checksum = 0; | ||
| for (var i = 0; i < _inputsWithLeadingJunk.Length; i++) | ||
| checksum += PhoneNumberUtil.ExtractPossibleNumber(_inputsWithLeadingJunk[i]).Length; | ||
| return checksum; | ||
| } | ||
| } | ||
| } |
70 changes: 70 additions & 0 deletions
70
csharp/PhoneNumbers.PerformanceTest/Benchmarks/PhoneNumberMatcherBenchmark.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| using System.Text; | ||
| using BenchmarkDotNet.Attributes; | ||
| using BenchmarkDotNet.Jobs; | ||
|
|
||
| namespace PhoneNumbers.PerformanceTest.Benchmarks | ||
| { | ||
| [MemoryDiagnoser] | ||
| [SimpleJob(RuntimeMoniker.Net48)] | ||
| [SimpleJob(RuntimeMoniker.Net80)] | ||
| [SimpleJob(RuntimeMoniker.Net90)] | ||
| public class PhoneNumberMatcherBenchmark | ||
| { | ||
| // Filler text interleaved between embedded numbers so the matcher has to skip non-number | ||
| // content. Kept short to keep total input length proportional to PhoneNumberCount. | ||
| private const string Filler = " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Call "; | ||
|
|
||
| #if NETFRAMEWORK | ||
| private PhoneNumberUtil _phoneNumberUtil = null; | ||
| private string _defaultRegion = null; | ||
| private string _text = null; | ||
| #else | ||
| private PhoneNumberUtil _phoneNumberUtil = null!; | ||
| private string _defaultRegion = null!; | ||
| private string _text = null!; | ||
| #endif | ||
|
|
||
| [Params(100, 1000)] | ||
| public int PhoneNumberCount { get; set; } | ||
|
|
||
| [GlobalSetup] | ||
| public void Setup() | ||
| { | ||
| _phoneNumberUtil = PhoneNumberUtil.GetInstance(); | ||
| var cases = PhoneNumberBenchmarkData.Create(_phoneNumberUtil, PhoneNumberCount); | ||
|
|
||
| // FindNumbers takes a single default region. Pick the most common one in the seed | ||
| // set so a meaningful share of the numbers parse against region-local formats. | ||
| _defaultRegion = cases[0].DefaultRegion; | ||
|
|
||
| var sb = new StringBuilder(PhoneNumberCount * (Filler.Length + 16)); | ||
| for (var i = 0; i < cases.Length; i++) | ||
| { | ||
| sb.Append(Filler); | ||
| sb.Append(cases[i].NumberToParse); | ||
| } | ||
| _text = sb.ToString(); | ||
| } | ||
|
|
||
| [Benchmark] | ||
| public int FindNumbers_Valid() | ||
| { | ||
| var checksum = 0; | ||
| foreach (var match in _phoneNumberUtil.FindNumbers(_text, _defaultRegion)) | ||
| checksum += match.RawString.Length; | ||
| return checksum; | ||
| } | ||
|
|
||
| // STRICT_GROUPING exercises AllNumberGroupsRemainGrouped, which the default VALID leniency | ||
| // does not. Useful to measure the matcher's group-formatting validation path. | ||
| [Benchmark] | ||
| public int FindNumbers_StrictGrouping() | ||
| { | ||
| var checksum = 0; | ||
| foreach (var match in _phoneNumberUtil.FindNumbers(_text, _defaultRegion, | ||
| PhoneNumberUtil.Leniency.STRICT_GROUPING, long.MaxValue)) | ||
| checksum += match.RawString.Length; | ||
| return checksum; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.