From 481d496641ebfd3d8c5428bcf2b8fcb85fabdae4 Mon Sep 17 00:00:00 2001 From: patrickdemooij9 Date: Mon, 28 Nov 2022 22:12:30 +0100 Subject: [PATCH] Use span in StripFileExtension to speed up and use less memory (#13101) (cherry picked from commit 475cea40a19112e5df9b52e3656fdbf5c89cbe98) --- .../Extensions/StringExtensions.cs | 9 ++- .../StringFileExtensionBenchmark.cs | 80 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 tests/Umbraco.Tests.Benchmarks/StringFileExtensionBenchmark.cs diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs index db3b8e5898..b284a071f9 100644 --- a/src/Umbraco.Core/Extensions/StringExtensions.cs +++ b/src/Umbraco.Core/Extensions/StringExtensions.cs @@ -83,18 +83,19 @@ public static class StringExtensions return fileName; } - var lastIndex = fileName.LastIndexOf('.'); + var spanFileName = fileName.AsSpan(); + var lastIndex = spanFileName.LastIndexOf('.'); if (lastIndex > 0) { - var ext = fileName.Substring(lastIndex); + var ext = spanFileName[lastIndex..]; // file extensions cannot contain whitespace - if (ext.Contains(" ")) + if (ext.Contains(' ')) { return fileName; } - return string.Format("{0}", fileName.Substring(0, fileName.IndexOf(ext, StringComparison.Ordinal))); + return new string(spanFileName[..lastIndex]); } return fileName; diff --git a/tests/Umbraco.Tests.Benchmarks/StringFileExtensionBenchmark.cs b/tests/Umbraco.Tests.Benchmarks/StringFileExtensionBenchmark.cs new file mode 100644 index 0000000000..0c9af70db9 --- /dev/null +++ b/tests/Umbraco.Tests.Benchmarks/StringFileExtensionBenchmark.cs @@ -0,0 +1,80 @@ +using System; +using BenchmarkDotNet.Attributes; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [QuickRunWithMemoryDiagnoserConfig] + public class StringFileExtensionBenchmark + { + [Arguments("smallText.txt")] + [Arguments("aVeryLongTextThatContainsALotOfCharacters.txt")] + [Arguments("NotEvenAFile")] + [Benchmark(Baseline = true)] + public string StringStrip(string fileName) + { + // filenames cannot contain line breaks + if (fileName.Contains(Environment.NewLine) || fileName.Contains("\r") || fileName.Contains("\n")) + { + return fileName; + } + + var lastIndex = fileName.LastIndexOf('.'); + if (lastIndex > 0) + { + var ext = fileName.Substring(lastIndex); + + // file extensions cannot contain whitespace + if (ext.Contains(" ")) + { + return fileName; + } + + return string.Format("{0}", fileName.Substring(0, fileName.IndexOf(ext, StringComparison.Ordinal))); + } + + return fileName; + } + + [Arguments("smallText.txt")] + [Arguments("aVeryLongTextThatContainsALotOfCharacters.txt")] + [Arguments("NotEvenAFile")] + [Benchmark] + public string SpanStrip(string fileName) + { + // filenames cannot contain line breaks + if (fileName.Contains(Environment.NewLine) || fileName.Contains("\r") || fileName.Contains("\n")) + { + return fileName; + } + + var spanFileName = fileName.AsSpan(); + var lastIndex = spanFileName.LastIndexOf('.'); + if (lastIndex > 0) + { + var ext = spanFileName[lastIndex..]; + + // file extensions cannot contain whitespace + if (ext.Contains(' ')) + { + return fileName; + } + + return new string(spanFileName[..lastIndex]); + } + + return fileName; + } + } + + //| Method | fileName | Mean | Error | StdDev | Ratio | Gen 0 | Allocated | + //|------------ |--------------------- |----------:|----------:|---------:|------:|-------:|----------:| + //| StringStrip | NotEvenAFile | 45.08 ns | 1.277 ns | 0.070 ns | 1.00 | - | - | + //| SpanStrip | NotEvenAFile | 45.13 ns | 6.131 ns | 0.336 ns | 1.00 | - | - | + //| | | | | | | | | + //| StringStrip | aVery(...)s.txt[45] | 234.10 ns | 28.303 ns | 1.551 ns | 1.00 | 0.0751 | 240 B | + //| SpanStrip | aVery(...)s.txt[45] | 98.37 ns | 14.839 ns | 0.813 ns | 0.42 | 0.0331 | 104 B | + //| | | | | | | | | + //| StringStrip | smallText.txt | 187.79 ns | 35.672 ns | 1.955 ns | 1.00 | 0.0348 | 112 B | + //| SpanStrip | smallText.txt | 62.46 ns | 13.795 ns | 0.756 ns | 0.33 | 0.0124 | 40 B | +}