See: https://github.com/NuGet/NuGet.Client/pull/1590#discussion_r130262373 and https://github.com/aspnet/Mvc/issues/5362.
On short strings and only two char arrays, String.IndexOfAny is significantly slower than a hard-coded version, and on x86 (non-RyuJIT) even slower than a hand-written naive implementation where there is no matches.
Method | Job | Jit | Platform | Input | Mean | Error | StdDev | Scaled | ScaledSD |Faster?
------------------------------------------ |------------- |---------- |--------- |------------------------- |-----------:|------------:|------------:|-------:|---------:|:---:|
IndexOfAny_Baseline | LegacyJitX64 | LegacyJit | X64 | This is a short string. | 3,690.3 us | 43.6378 us | 36.4396 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | LegacyJitX64 | LegacyJit | X64 | This is a short string. | 2,276.0 us | 27.5134 us | 25.7360 us | 0.62 | 0.01 |โ
IndexOfAny_HandWrittenLoop | LegacyJitX64 | LegacyJit | X64 | This is a short string. | 6,550.1 us | 123.8535 us | 96.6966 us | 1.78 | 0.03 |
IndexOfAny_Baseline | LegacyJitX86 | LegacyJit | X86 | This is a short string. | 6,552.9 us | 65.9870 us | 61.7243 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | LegacyJitX86 | LegacyJit | X86 | This is a short string. | 2,314.6 us | 48.8886 us | 71.6603 us | 0.35 | 0.01 |โ
IndexOfAny_HandWrittenLoop | LegacyJitX86 | LegacyJit | X86 | This is a short string. | 5,143.8 us | 51.0904 us | 47.7900 us | 0.79 | 0.01 |โ
IndexOfAny_Baseline | RyuJitX64 | RyuJit | X64 | This is a short string. | 3,666.7 us | 61.5387 us | 57.5634 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | RyuJitX64 | RyuJit | X64 | This is a short string. | 2,400.2 us | 22.9174 us | 21.4370 us | 0.65 | 0.01 |โ
IndexOfAny_HandWrittenLoop | RyuJitX64 | RyuJit | X64 | This is a short string. | 6,126.5 us | 118.1990 us | 116.0872 us | 1.67 | 0.04 |
IndexOfAny_Baseline | LegacyJitX64 | LegacyJit | X64 | /This is a short string. | 1,196.7 us | 8.0287 us | 7.1172 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | LegacyJitX64 | LegacyJit | X64 | /This is a short string. | 206.3 us | 1.3898 us | 1.3000 us | 0.17 | 0.00 |โ
IndexOfAny_HandWrittenLoop | LegacyJitX64 | LegacyJit | X64 | /This is a short string. | 464.2 us | 8.2176 us | 7.2847 us | 0.39 | 0.01 |โ
IndexOfAny_Baseline | LegacyJitX86 | LegacyJit | X86 | /This is a short string. | 1,668.2 us | 11.5627 us | 10.8158 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | LegacyJitX86 | LegacyJit | X86 | /This is a short string. | 233.1 us | 0.7199 us | 0.6734 us | 0.14 | 0.00 |โ
IndexOfAny_HandWrittenLoop | LegacyJitX86 | LegacyJit | X86 | /This is a short string. | 350.4 us | 1.5306 us | 1.3568 us | 0.21 | 0.00 |โ
IndexOfAny_Baseline | RyuJitX64 | RyuJit | X64 | /This is a short string. | 1,344.7 us | 35.3044 us | 49.4918 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | RyuJitX64 | RyuJit | X64 | /This is a short string. | 236.6 us | 1.6766 us | 1.4001 us | 0.18 | 0.01 |โ
IndexOfAny_HandWrittenLoop | RyuJitX64 | RyuJit | X64 | /This is a short string. | 384.6 us | 5.8540 us | 5.1894 us | 0.29 | 0.01 |โ
IndexOfAny_Baseline | LegacyJitX64 | LegacyJit | X64 | This is a/short string. | 2,074.6 us | 14.7860 us | 13.1074 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | LegacyJitX64 | LegacyJit | X64 | This is a/short string. | 920.1 us | 14.5393 us | 12.1410 us | 0.44 | 0.01 |โ
IndexOfAny_HandWrittenLoop | LegacyJitX64 | LegacyJit | X64 | This is a/short string. | 3,059.0 us | 26.8631 us | 20.9729 us | 1.47 | 0.01 |
IndexOfAny_Baseline | LegacyJitX86 | LegacyJit | X86 | This is a/short string. | 3,821.6 us | 22.9191 us | 20.3171 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | LegacyJitX86 | LegacyJit | X86 | This is a/short string. | 1,256.0 us | 7.6582 us | 7.1635 us | 0.33 | 0.00 |โ
IndexOfAny_HandWrittenLoop | LegacyJitX86 | LegacyJit | X86 | This is a/short string. | 3,824.8 us | 36.8087 us | 34.4309 us | 1.00 | 0.01 |
IndexOfAny_Baseline | RyuJitX64 | RyuJit | X64 | This is a/short string. | 2,096.2 us | 15.8659 us | 14.8410 us | 1.00 | 0.00 |
IndexOfAny_HandWrittenLoop_HardcodedChars | RyuJitX64 | RyuJit | X64 | This is a/short string. | 1,045.3 us | 12.6123 us | 11.7975 us | 0.50 | 0.01 |โ
IndexOfAny_HandWrittenLoop | RyuJitX64 | RyuJit | X64 | This is a/short string. | 3,116.7 us | 62.2240 us | 103.9623 us | 1.49 | 0.05 |
``` C#
[MethodImpl(MethodImplOptions.NoInlining)]
public static int IndexOfAny(string value, char[] array)
{
return value.IndexOfAny(array);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static int IndexOfAny_HandWrittenLoop_HardcodedChars(string value)
{
for (int i = 0; i < value.Length; i++)
{
char c = value[i];
if (c == '/' || c == '\\')
{
return i;
}
}
return -1;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static int IndexOfAny_HandWrittenLoop(string value, char[] array)
{
if (array.Length == 0)
return 0;
for (int i = 0; i < value.Length; i++)
{
char c = value[i];
foreach (var ch in array)
{
if (c == ch)
return i;
}
}
return -1;
}
```
Benchmark is here (using BenchmarkDotNet): https://gist.github.com/davkean/dab5c068e3f907709f57a757a7e7fb3a.
@benaadams Seems to think that the code creates a probabilistic map is over kill for a two char array.
Updated: Add a few more test cases, and updated handwritten loop to handle empty chars.
I can have a look at this one if no one is on it. We should be able to get 2 chars working faster anyway.
Could have a private; overload and call into it when length == 2?
private int IndexOfAny(char value0, char value2)
Then raise api request to expose? Though as an string extension in corefx can Vectorize ๐ (api wise)
Though as an string extension in corefx can Vectorize
@mellinoe, when will corelib support vectorization?
@stephentoub We don't really have an exact timeline yet, but that piece is definitely in the plans.
One more data point, on a trace I took off an MSBuild design-time build, InitializeProbabilisticMap is 5.3% of the CPU time.

Caching the map against array pointer might also help (with LRU cap e.g. max 8 kept) and reusing it rather than always recreating could also help?
I suspect a lot is coming from these two Path.Combine: http://referencesource.microsoft.com/#mscorlib/system/io/filesystemenumerable.cs,514. These don't even need to check the paths for invalid chars.
@davkean It doesn't look like the HasIllegalCharacters optimizations have been backported to desktop yet(?). That will be the best way to improve path combine.
I have an implementation for special casing a 2-char anyOf array that beats the minimal hard coded loop after about 4 characters.
I'll look at the mapping logic for larger arrays but I'm not very hopeful with that. It seems to be a good solution for a generic search without any prior domain knowledge. Caching of the map might be an option but there would be overhead determining if the anyOf array has changed.
Potential size of the anyOf array, length of strings, and whether you are expecting an early match are big factors in an optimal custom implementation.
Ok I can cut half of the time out of InitializeProbabilisticMap if the search chars are mostly ASCII which is highly likely. Need to run some variation tests.
Looking at Rosyln/MSBuild the main counts for IndexOfAny seem to be 2, 3, Lots. Might we worth special casing 3 also? Are all ASCII chars
@benaadams Agreed, doing a quick search across the entire Visual Studio tree internally, almost* all usage, takes 2 or 3 chars. I do think it's worth special casing those cases.
*Other usage I saw was using Path.GetInvalidPathChars() - looks like there's a missing feature in Path.
Here's another trace that I was looking at, this is exclusive time:

Unfortunately, the stacks are broken around it so I can't see if this is just the excessive GetFiles/GetDirectories calls or not which I already removed from the trace.
And another one:

@davkean was that x86 or x64 clr? I'm seeing huge differences with ProbablyContains extracting it to a c++ test app.
Other usage I saw was using Path.GetInvalidPathChars() - looks like there's a missing feature in Path
The path handling was significantly refactored and improved for .NET Core. There are no IndexOfAny calls in Path handling code on .NET Core anymore.
What I mean by that @jkotas is that I see usages throughout Visual Studio doing an IndexOfAny on GetInvalidPathChars - which means that they seem to be missing an API from Path - ie ContainsInvalidPathsChars.
@bbowyersmyth These are all x86, based on below results it looks like overhead is a lot less on x64.
I wrote another benchmark designed to mimic some of the inputs that MSBuild is handling (IndexOfAny is the baseline) and what appears to be the biggest overhead apart from file I/O paths:
Method | Job | Jit | Platform | InputIndex | Mean | Error | StdDev | Scaled | ScaledSD |
------------------------------------------------------- |------------- |---------- |--------- |----------- |------------:|------------:|------------:|-------:|---------:|
Hand written loop over array | LegacyJitX64 | LegacyJit | X64 |"FooBar.cs"| 9,743.4 us | 193.9627 us | 190.4973 us | 3.58 | 0.07 |
IndexOfAny | LegacyJitX64 | LegacyJit | X64 |"FooBar.cs"| 2,725.1 us | 22.1683 us | 19.6516 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |"FooBar.cs"| 1,052.5 us | 6.3929 us | 5.9799 us | 0.39 | 0.00 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |"FooBar.cs"| 10,574.5 us | 45.1972 us | 42.2775 us | 2.10 | 0.02 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |"FooBar.cs"| 5,041.5 us | 56.0237 us | 46.7823 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |"FooBar.cs"| 1,227.3 us | 9.9751 us | 9.3307 us | 0.24 | 0.00 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |"FooBar.cs"| 6,777.3 us | 19.3900 us | 18.1375 us | 2.49 | 0.04 |
IndexOfAny| RyuJitX64 | RyuJit | X64 |"FooBar.cs"| 2,718.8 us | 53.0445 us | 47.0226 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |"FooBar.cs"| 1,428.9 us | 11.4744 us | 10.1718 us | 0.53 | 0.01 |
Hand written loop over array| LegacyJitX64 | LegacyJit | X64 |"Folder\Bar\Foo\Bar\FooBar.cs"| 28,432.7 us | 206.1150 us | 192.8001 us | 5.27 | 0.08 |
IndexOfAny| LegacyJitX64 | LegacyJit | X64 |"Folder\Bar\Foo\Bar\FooBar.cs"| 5,391.4 us | 82.2404 us | 72.9040 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |"Folder\Bar\Foo\Bar\FooBar.cs"| 3,514.0 us | 24.2079 us | 18.9000 us | 0.65 | 0.01 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |"Folder\Bar\Foo\Bar\FooBar.cs"| 32,192.6 us | 183.2648 us | 171.4259 us | 3.47 | 0.12 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |"Folder\Bar\Foo\Bar\FooBar.cs"| 9,277.9 us | 371.3607 us | 347.3710 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |"Folder\Bar\Foo\Bar\FooBar.cs"| 3,788.8 us | 35.7124 us | 31.6581 us | 0.41 | 0.01 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |"Folder\Bar\Foo\Bar\FooBar.cs"| 18,447.0 us | 63.9169 us | 59.7879 us | 3.46 | 0.02 |
IndexOfAny | RyuJitX64 | RyuJit | X64 |"Folder\Bar\Foo\Bar\FooBar.cs"| 5,330.4 us | 23.6916 us | 22.1611 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |"Folder\Bar\Foo\Bar\FooBar.cs"| 4,215.7 us | 11.4736 us | 10.1711 us | 0.79 | 0.00 |
Hand written loop over array| LegacyJitX64 | LegacyJit | X64 |"$(FooBar)"| 759.8 us | 3.9822 us | 3.7250 us | 0.29 | 0.00 |
IndexOfAny| LegacyJitX64 | LegacyJit | X64 |"$(FooBar)"| 2,640.9 us | 39.8305 us | 37.2575 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |"$(FooBar)"| 256.3 us | 2.4061 us | 2.2506 us | 0.10 | 0.00 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |"$(FooBar)"| 622.5 us | 7.0951 us | 6.2896 us | 0.21 | 0.00 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |"$(FooBar)"| 2,941.9 us | 15.2091 us | 14.2266 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |"$(FooBar)"| 263.1 us | 1.7976 us | 1.6815 us | 0.09 | 0.00 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |"$(FooBar)"| 554.9 us | 3.0040 us | 2.6630 us | 0.24 | 0.00 |
IndexOfAny| RyuJitX64 | RyuJit | X64 |"$(FooBar)"| 2,336.2 us | 17.4339 us | 15.4547 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |"$(FooBar)"| 233.0 us | 0.7912 us | 0.7013 us | 0.10 | 0.00 |
Hand written loop over array| LegacyJitX64 | LegacyJit | X64 |"\.cs"| 354.2 us | 2.5846 us | 2.2911 us | 0.16 | 0.00 |
IndexOfAny| LegacyJitX64 | LegacyJit | X64 |"\.cs"| 2,182.8 us | 16.2082 us | 15.1612 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |"\.cs"| 262.8 us | 12.4714 us | 12.8072 us | 0.12 | 0.01 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |"\.cs"| 349.7 us | 1.6481 us | 1.5416 us | 0.13 | 0.00 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |"\.cs"| 2,786.9 us | 11.0037 us | 9.7545 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |"\.cs"| 262.3 us | 1.7582 us | 1.6446 us | 0.09 | 0.00 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |"\.cs"| 393.2 us | 8.2556 us | 8.1081 us | 0.18 | 0.00 |
IndexOfAny| RyuJitX64 | RyuJit | X64 |"\.cs"| 2,161.5 us | 7.0953 us | 6.6370 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |"*\.cs"| 233.0 us | 0.9070 us | 0.8040 us | 0.11 | 0.00 |
Hand written loop over array| LegacyJitX64 | LegacyJit | X64 |".cs"| 356.0 us | 5.1805 us | 4.8458 us | 0.16 | 0.00 |
IndexOfAny| LegacyJitX64 | LegacyJit | X64 |".cs"| 2,162.4 us | 14.1367 us | 13.2235 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |".cs"| 254.6 us | 0.9145 us | 0.8555 us | 0.12 | 0.00 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |".cs"| 350.6 us | 1.9620 us | 1.7392 us | 0.13 | 0.00 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |".cs"| 2,802.9 us | 21.4114 us | 20.0282 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |".cs"| 266.8 us | 6.1866 us | 7.8241 us | 0.10 | 0.00 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |".cs"| 387.7 us | 7.6097 us | 7.4737 us | 0.18 | 0.00 |
IndexOfAny| RyuJitX64 | RyuJit | X64 |".cs"| 2,208.2 us | 43.8530 us | 48.7424 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |"*.cs"| 232.7 us | 0.9832 us | 0.8716 us | 0.11 | 0.00 |
Hand written loop over array| LegacyJitX64 | LegacyJit | X64 |"Bar.cs;Foo.cs"| 8,037.4 us | 99.7138 us | 93.2724 us | 2.50 | 0.06 |
IndexOfAny| LegacyJitX64 | LegacyJit | X64 |"Bar.cs;Foo.cs"| 3,216.4 us | 73.7421 us | 81.9641 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |"Bar.cs;Foo.cs"| 874.3 us | 3.0919 us | 2.8922 us | 0.27 | 0.01 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |"Bar.cs;Foo.cs"| 8,235.2 us | 63.2064 us | 56.0308 us | 1.78 | 0.01 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |"Bar.cs;Foo.cs"| 4,623.4 us | 22.2813 us | 20.8419 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |"Bar.cs;Foo.cs"| 1,109.0 us | 6.3642 us | 5.9530 us | 0.24 | 0.00 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |"Bar.cs;Foo.cs"| 5,481.2 us | 24.7627 us | 21.9515 us | 1.72 | 0.01 |
IndexOfAny| RyuJitX64 | RyuJit | X64 |"Bar.cs;Foo.cs"| 3,179.5 us | 11.0161 us | 9.7655 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |"Bar.cs;Foo.cs"| 1,260.8 us | 10.7964 us | 10.0989 us | 0.40 | 0.00 |
Hand written loop over array| LegacyJitX64 | LegacyJit | X64 |";Bar.cs"| 1,079.1 us | 4.7609 us | 4.4534 us | 0.37 | 0.00 |
IndexOfAny| LegacyJitX64 | LegacyJit | X64 |";Bar.cs"| 2,879.7 us | 9.1202 us | 8.0848 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |";Bar.cs"| 256.0 us | 1.9406 us | 1.8153 us | 0.09 | 0.00 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |";Bar.cs"| 852.3 us | 5.6177 us | 5.2548 us | 0.27 | 0.01 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |";Bar.cs"| 3,142.6 us | 74.4552 us | 88.6336 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |";Bar.cs"| 261.7 us | 1.2020 us | 1.1244 us | 0.08 | 0.00 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |";Bar.cs"| 779.6 us | 3.6939 us | 3.4553 us | 0.31 | 0.00 |
IndexOfAny| RyuJitX64 | RyuJit | X64 |";Bar.cs"| 2,543.1 us | 15.2274 us | 13.4987 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |";Bar.cs"| 263.1 us | 2.1317 us | 1.9940 us | 0.10 | 0.00 |
Hand written loop over array| LegacyJitX64 | LegacyJit | X64 |"\Bar.cs"| 7,741.7 us | 28.0283 us | 24.8464 us | 3.07 | 0.02 |
IndexOfAny| LegacyJitX64 | LegacyJit | X64 |"\Bar.cs"| 2,519.0 us | 12.5039 us | 11.6962 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX64 | LegacyJit | X64 |"\Bar.cs"| 874.9 us | 3.7346 us | 3.4933 us | 0.35 | 0.00 |
Hand written loop over array| LegacyJitX86 | LegacyJit | X86 |"\Bar.cs"| 7,827.0 us | 47.0993 us | 44.0567 us | 1.69 | 0.01 |
IndexOfAny| LegacyJitX86 | LegacyJit | X86 |"\Bar.cs"| 4,629.2 us | 32.5734 us | 27.2002 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | LegacyJitX86 | LegacyJit | X86 |"\Bar.cs"| 1,030.0 us | 6.3604 us | 4.9658 us | 0.22 | 0.00 |
Hand written loop over array| RyuJitX64 | RyuJit | X64 |"\Bar.cs"| 5,142.2 us | 128.7788 us | 126.4780 us | 2.11 | 0.05 |
IndexOfAny| RyuJitX64 | RyuJit | X64 |"\Bar.cs"| 2,432.0 us | 12.4901 us | 11.6832 us | 1.00 | 0.00 |
Hand written loop over Handcoded chars | RyuJitX64 | RyuJit | X64 |"\Bar.cs"| 1,105.0 us | 4.6880 us | 4.3852 us | 0.45 | 0.00 |
What's surprising is that while RyuJIT beats the old JIT on "Hand written loop over array", JIT64 beats RyuJIT in the hard coded char loops.
The other thing it shows, is that if there's a high chance that your match is in one of the first few characters, there's a huge win by checking that up-front by a lot even for a >3 element array.
@davkean now the IndexOfAny change went in, what else are you looking for here in corefx/coreclr? Do we need a proposal for Path.ContainsInvalidPathsChars() ? Or is this tracking code gen improvements, ? Or..
If you need something for your CLI perf I would like to make sure we take a look.
The problem is none of these changes actually affect where we are - .NET Framework.
@davkean right but do we believe that porting what we have in .NET Core today (including dotnet/coreclr#13219 and maybe others) will sufficiently address the issue, or is there more to do here?
If all that remains is to track porting to .NET Framework I believe we close this and mark as "netfx-port-consider". @AlexGhiondea
@davkean what would be awesome is if you could profile against .NET Core to see whether there is more to do there. That would help inner loop on Linux, plus validate we are ready to port to desktop.
I'm going to assume that all of the aforementioned fixes addressed the cited issues on .NET Core. If issues still remain here for .NET Core, please re-open (or open new issues). Thanks.
Most helpful comment
The problem is none of these changes actually affect where we are - .NET Framework.