From 0d54c9884fc1f44539f4d149d50dcdac8860deb6 Mon Sep 17 00:00:00 2001 From: Sebastien Marichal Date: Mon, 6 Jan 2025 15:46:44 +0100 Subject: [PATCH 1/3] NET-934 Modify S1643: Add benchmarks --- rules/S1643/csharp/rule.adoc | 85 +++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/rules/S1643/csharp/rule.adoc b/rules/S1643/csharp/rule.adoc index 03f030c0d08..9d1cf0d46ea 100644 --- a/rules/S1643/csharp/rule.adoc +++ b/rules/S1643/csharp/rule.adoc @@ -1,30 +1,101 @@ == Why is this an issue? -``++StringBuilder++`` is more efficient than string concatenation, especially when the operator is repeated over and over as in loops. +Concatenating string literals or strings in a loop using the `+` operator creates a new string object for each concatenation. This can lead to a large number of intermediate string objects and can be inefficient. The `StringBuilder` class is more efficient than string concatenation, especially when the operator is repeated over and over as in loops. -=== Noncompliant code example +== How to fix it -[source,csharp] +Replace string concatenation with `StringBuilder`. + +=== Code examples + +==== Noncompliant code example + +[source,csharp,diff-id=1,diff-type=noncompliant] ---- string str = ""; -for (int i = 0; i < arrayOfStrings.Length ; ++i) +for (int i = 0; i < arrayOfStrings.Length ; ++i) { str = str + arrayOfStrings[i]; } ---- -=== Compliant solution +==== Compliant solution -[source,csharp] +[source,csharp,diff-id=1,diff-type=compliant] ---- StringBuilder bld = new StringBuilder(); -for (int i = 0; i < arrayOfStrings.Length; ++i) +for (int i = 0; i < arrayOfStrings.Length; ++i) { bld.Append(arrayOfStrings[i]); } string str = bld.ToString(); ---- +== Resources + +=== Documentation + +* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder[StringBuilder Class] + +=== Benchmarks + +[options="header"] +|=== +| Method | Runtime | Mean | Standard Deviation | Allocated +| StringConcatenation | .NET 8.0 | 17,523.51 us | 316.022 us | 370543.43 KB +| StringBuilder | .NET 8.0 | 33.39 us | 0.637 us | 155.69 KB +| StringConcatenation | .NET 9.0 | 17,808.63 us | 485.294 us | 370543.42 KB +| StringBuilder | .NET 9.0 | 30.51 us | 0.646 us | 155.69 KB +| StringConcatenation | .NET Framework 4.6.2 | 18,544.31 us | 201.981 us | 371066.30 KB +| StringBuilder | .NET Framework 4.6.2 | 388.64 us | 4.279 us | 539.58 KB +|=== + +==== Glossary + +* https://en.wikipedia.org/wiki/Arithmetic_mean[Mean] +* https://en.wikipedia.org/wiki/Standard_deviation[Standard Deviation] + +The results were generated by running the following snippet with https://github.com/dotnet/BenchmarkDotNet[BenchmarkDotNet]: + +[source,csharp] +---- +[Params(10_000)] +public int Iterations; + +[Benchmark] +public void StringConcatenation() +{ + string str = ""; + for (int i = 0; i < Iterations; i++) + { + str = str + i; + } +} + +[Benchmark] +public void StringBuilder() +{ + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < Iterations; i++) + { + builder.Append(i); + } + _ = builder.ToString(); +} +---- + +Hardware Configuration: + +[source] +---- +BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) +12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores + [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 + .NET 8.0 : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2 + .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 + .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 +---- + ifdef::env-github,rspecator-view[] ''' From 5d81cb371374ba5b4ec34a558e2f105f18192395 Mon Sep 17 00:00:00 2001 From: Sebastien Marichal Date: Tue, 7 Jan 2025 15:25:29 +0100 Subject: [PATCH 2/3] Update benchmark --- rules/S1643/csharp/rule.adoc | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/rules/S1643/csharp/rule.adoc b/rules/S1643/csharp/rule.adoc index 9d1cf0d46ea..5c895822137 100644 --- a/rules/S1643/csharp/rule.adoc +++ b/rules/S1643/csharp/rule.adoc @@ -42,14 +42,13 @@ string str = bld.ToString(); [options="header"] |=== | Method | Runtime | Mean | Standard Deviation | Allocated -| StringConcatenation | .NET 8.0 | 17,523.51 us | 316.022 us | 370543.43 KB -| StringBuilder | .NET 8.0 | 33.39 us | 0.637 us | 155.69 KB -| StringConcatenation | .NET 9.0 | 17,808.63 us | 485.294 us | 370543.42 KB -| StringBuilder | .NET 9.0 | 30.51 us | 0.646 us | 155.69 KB -| StringConcatenation | .NET Framework 4.6.2 | 18,544.31 us | 201.981 us | 371066.30 KB -| StringBuilder | .NET Framework 4.6.2 | 388.64 us | 4.279 us | 539.58 KB +| StringConcatenation | .NET 9.0 | 45,723.79 us | 4,589.713 us | 586280.56 KB +| StringBuilder | .NET 9.0 | 77.73 us | 1.221 us | 243.79 KB +| StringConcatenation | .NET Framework 4.6.2 | 33,922.61 us | 302.498 us | 586450.35 KB +| StringBuilder | .NET Framework 4.6.2 | 178.14 us | 9.010 us | 244.15 KB |=== + ==== Glossary * https://en.wikipedia.org/wiki/Arithmetic_mean[Mean] @@ -68,7 +67,7 @@ public void StringConcatenation() string str = ""; for (int i = 0; i < Iterations; i++) { - str = str + i; + str = str + "append"; } } @@ -78,7 +77,7 @@ public void StringBuilder() StringBuilder builder = new StringBuilder(); for (int i = 0; i < Iterations; i++) { - builder.Append(i); + builder.Append("append"); } _ = builder.ToString(); } @@ -91,7 +90,6 @@ Hardware Configuration: BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5247/22H2/2022Update) 12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores [Host] : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 - .NET 8.0 : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2 .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 ---- From a50678f9c3249f820b081c47b38740a659a596c9 Mon Sep 17 00:00:00 2001 From: Sebastien Marichal Date: Tue, 7 Jan 2025 16:23:47 +0100 Subject: [PATCH 3/3] Improving description --- rules/S1643/csharp/rule.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/S1643/csharp/rule.adoc b/rules/S1643/csharp/rule.adoc index 5c895822137..1d104e58863 100644 --- a/rules/S1643/csharp/rule.adoc +++ b/rules/S1643/csharp/rule.adoc @@ -1,6 +1,6 @@ == Why is this an issue? -Concatenating string literals or strings in a loop using the `+` operator creates a new string object for each concatenation. This can lead to a large number of intermediate string objects and can be inefficient. The `StringBuilder` class is more efficient than string concatenation, especially when the operator is repeated over and over as in loops. +Concatenating multiple string literals or strings using the `+` operator creates a new string object for each concatenation. This can lead to a large number of intermediate string objects and can be inefficient. The `StringBuilder` class is more efficient than string concatenation, especially when the operator is repeated over and over as in loops. == How to fix it