Add/use internal String.Concat overloads for spans by stephentoub · Pull Request #21766 · dotnet/coreclr (original) (raw)

In the common case where it need to replace a non-empty extension with a non-empty extension, it currently incurs a substring without the original extension prior to then concatenating with the new extension. This PR avoids that.

(As the Path implementation is in corelib, this uses string.FastAllocateString and then formats into it with a span; if we wanted to avoid that, string.Create could also be used. That could also be addressed with new String.Concat overloads that accept ReadOnlySpan<char>s.)

Benchmark:

[Benchmark] public string ChangeExtensionReplace() => Path.ChangeExtension(@"c:\this\is\a\path\hello.txt", ".dat"); [Benchmark] public string ChangeExtensionRemove() => Path.ChangeExtension(@"c:\this\is\a\path\hello.txt", null);

Before:

                 Method |     Mean |     Error |    StdDev |  Gen 0 | Allocated |
----------------------- |---------:|----------:|----------:|-------:|----------:|
 ChangeExtensionReplace | 41.39 ns | 1.4013 ns | 2.7985 ns | 0.0363 |     152 B |
  ChangeExtensionRemove | 18.57 ns | 0.2763 ns | 0.2449 ns | 0.0172 |      72 B |

After:

                 Method |     Mean |     Error |    StdDev |  Gen 0 | Allocated |
----------------------- |---------:|----------:|----------:|-------:|----------:|
 ChangeExtensionReplace | 18.66 ns | 0.1989 ns | 0.1764 ns | 0.0191 |      80 B |
  ChangeExtensionRemove | 18.81 ns | 0.2678 ns | 0.2505 ns | 0.0172 |      72 B |

cc: @JeremyKuhne, @jkotas