Skip to content

Add fast path in string.Concat#130361

Merged
MihaZupan merged 3 commits into
dotnet:mainfrom
prozolic:stringconcat
Jul 10, 2026
Merged

Add fast path in string.Concat#130361
MihaZupan merged 3 commits into
dotnet:mainfrom
prozolic:stringconcat

Conversation

@prozolic

@prozolic prozolic commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

This PR adds a fast path for string.Concat(IEnumerable<string?> values) when the input value is List<string?> or string?[]. This fast path is based on the pattern already used in Join(string? separator, IEnumerable<string?> values).
I have also added tests for the fast path and fallback path.

Benchmark

This Change is expected to improve performance when the argument of string.Concat(IEnumerable<string?> values) is List<string?> or string?[].

Method Job Toolchain Size Mean Error StdDev Median Min Max Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
ConcatString_Enumerable Job-QIDXWP \main\corerun.exe 10 150.22 ns 7.503 ns 8.640 ns 152.11 ns 137.70 ns 165.26 ns 1.00 0.00 0.1197 - - 752 B 1.00
ConcatString_Enumerable Job-TLPBEL \pr\corerun.exe 10 133.46 ns 3.512 ns 3.903 ns 131.45 ns 129.67 ns 142.99 ns 0.89 0.06 0.1195 - - 752 B 1.00
ConcatString_Array Job-QIDXWP \main\corerun.exe 10 54.16 ns 1.042 ns 0.813 ns 54.30 ns 52.53 ns 55.19 ns 1.00 0.00 0.0407 - - 256 B 1.00
ConcatString_Array Job-TLPBEL \pr\corerun.exe 10 37.43 ns 1.786 ns 1.985 ns 36.62 ns 34.19 ns 41.63 ns 0.69 0.04 0.0356 - - 224 B 0.88
ConcatString_List Job-QIDXWP \main\corerun.exe 10 62.35 ns 5.475 ns 6.085 ns 58.71 ns 57.42 ns 76.39 ns 1.00 0.00 0.0419 - - 264 B 1.00
ConcatString_List Job-TLPBEL \pr\corerun.exe 10 38.78 ns 1.355 ns 1.506 ns 38.22 ns 37.06 ns 41.77 ns 0.63 0.06 0.0357 - - 224 B 0.85
ConcatString_Enumerable Job-QIDXWP \main\corerun.exe 100 1,384.76 ns 109.124 ns 125.668 ns 1,338.98 ns 1,240.39 ns 1,560.94 ns 1.00 0.00 1.0524 - - 6608 B 1.00
ConcatString_Enumerable Job-TLPBEL \pr\corerun.exe 100 1,304.29 ns 23.435 ns 19.569 ns 1,302.74 ns 1,283.53 ns 1,343.32 ns 0.95 0.08 1.0494 - - 6608 B 1.00
ConcatString_Array Job-QIDXWP \main\corerun.exe 100 409.49 ns 7.974 ns 7.832 ns 407.95 ns 398.56 ns 424.99 ns 1.00 0.00 0.3218 - - 2024 B 1.00
ConcatString_Array Job-TLPBEL \pr\corerun.exe 100 341.10 ns 21.791 ns 25.095 ns 337.12 ns 310.58 ns 396.64 ns 0.83 0.06 0.3174 - - 1992 B 0.98
ConcatString_List Job-QIDXWP \main\corerun.exe 100 517.63 ns 8.515 ns 7.548 ns 515.12 ns 508.14 ns 533.71 ns 1.00 0.00 0.3227 - - 2032 B 1.00
ConcatString_List Job-TLPBEL \pr\corerun.exe 100 309.35 ns 4.453 ns 3.718 ns 309.90 ns 302.72 ns 314.76 ns 0.60 0.01 0.3173 - - 1992 B 0.98
ConcatString_Enumerable Job-QIDXWP \main\corerun.exe 1000 14,219.06 ns 221.613 ns 185.057 ns 14,227.31 ns 13,951.98 ns 14,659.81 ns 1.00 0.00 10.4576 - - 65856 B 1.00
ConcatString_Enumerable Job-TLPBEL \pr\corerun.exe 1000 13,544.69 ns 124.313 ns 103.807 ns 13,573.65 ns 13,323.62 ns 13,683.07 ns 0.95 0.01 10.4680 - - 65856 B 1.00
ConcatString_Array Job-QIDXWP \main\corerun.exe 1000 4,299.72 ns 82.261 ns 68.692 ns 4,300.00 ns 4,195.10 ns 4,404.01 ns 1.00 0.00 3.1547 - - 19928 B 1.00
ConcatString_Array Job-TLPBEL \pr\corerun.exe 1000 2,999.08 ns 47.892 ns 39.992 ns 3,002.73 ns 2,940.33 ns 3,077.51 ns 0.70 0.01 3.1615 - - 19896 B 1.00
ConcatString_List Job-QIDXWP \main\corerun.exe 1000 5,252.69 ns 75.591 ns 63.122 ns 5,252.78 ns 5,124.19 ns 5,374.56 ns 1.00 0.00 3.1667 - - 19936 B 1.00
ConcatString_List Job-TLPBEL \pr\corerun.exe 1000 3,070.12 ns 60.962 ns 54.042 ns 3,077.02 ns 2,953.34 ns 3,134.09 ns 0.58 0.01 3.1615 - - 19896 B 1.00
ConcatString_Enumerable Job-QIDXWP \main\corerun.exe 10000 305,850.16 ns 9,171.318 ns 9,813.208 ns 307,086.58 ns 280,512.72 ns 325,177.59 ns 1.00 0.00 123.9224 61.4224 61.4224 658830 B 1.00
ConcatString_Enumerable Job-TLPBEL \pr\corerun.exe 10000 297,325.54 ns 9,746.004 ns 10,428.116 ns 302,198.41 ns 272,057.94 ns 308,274.79 ns 0.97 0.05 123.9407 61.4407 61.4407 658830 B 1.00
ConcatString_Array Job-QIDXWP \main\corerun.exe 10000 108,964.71 ns 4,410.330 ns 4,902.070 ns 110,349.45 ns 97,951.58 ns 116,218.28 ns 1.00 0.00 62.1044 62.1044 62.1044 199543 B 1.00
ConcatString_Array Job-TLPBEL \pr\corerun.exe 10000 82,938.57 ns 2,698.977 ns 2,887.876 ns 83,646.74 ns 75,325.70 ns 86,369.22 ns 0.76 0.04 62.2066 62.2066 62.2066 199511 B 1.00
ConcatString_List Job-QIDXWP \main\corerun.exe 10000 123,694.56 ns 2,888.695 ns 3,090.871 ns 122,749.77 ns 118,993.66 ns 131,228.33 ns 1.00 0.00 62.0438 62.0438 62.0438 199551 B 1.00
ConcatString_List Job-TLPBEL \pr\corerun.exe 10000 92,988.47 ns 3,075.460 ns 3,290.708 ns 92,794.81 ns 87,439.32 ns 100,264.49 ns 0.75 0.03 62.0567 62.0567 62.0567 199511 B 1.00
    [BenchmarkCategory(Categories.Libraries, Categories.Runtime)]
    public class Perf_StringConcatForString
    {
        private int[] array;
#pragma warning disable CS8632 // '#nullable' 
        private IEnumerable<string?> _stringArray;
        private IEnumerable<string?> _stringList;
        private IEnumerable<string?> _stringEnumerable;
#pragma warning restore CS8632

        [Params(10, 100, 1000, 10000)]
        public int Size;

        [GlobalSetup]
        public void Setup()
        {
            var r = new Random(42);
            array = Enumerable.Range(0, Size).Select(_ => r.Next(int.MinValue, int.MaxValue)).ToArray();
            _stringArray = array.Select(i => i.ToString()).ToArray();
            _stringList = array.Select(i => i.ToString()).ToList();
            _stringEnumerable = array.Select(i => i.ToString());
        }

        [Benchmark]
        public string ConcatString_Enumerable()
        {
            return string.Concat(_stringEnumerable);
        }

        [Benchmark]
        public string ConcatString_Array()
        {
            return string.Concat(_stringArray);
        }

        [Benchmark]
        public string ConcatString_List()
        {
            return string.Concat(_stringList);
        }

    }

This PR adds a fast path for string.Concat when the input value is
List<string?> or string?[]. This fast path is based on the pattern
already used in Join(string? separator, IEnumerable<string?>
values).
I have also added tests for the fast path and fallback path.
Copilot AI review requested due to automatic review settings July 8, 2026 14:06
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Jul 8, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the System.String.Concat(IEnumerable<string?> values) overload in System.Private.CoreLib by adding type-specialized fast paths for common inputs (arrays and List<T>), and extends existing String.Concat tests to cover these additional enumerable shapes.

Changes:

  • Add a fast path for List<string?> inputs by extracting a span via CollectionsMarshal.AsSpan and forwarding to the span-based Concat implementation.
  • Add a fast path for string?[] inputs by forwarding to the span-based Concat implementation.
  • Extend StringTests.Concat_String to validate results for a non-array/non-list enumerable (LINQ Select) and for a List<string?>.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Adds List/array fast paths for Concat(IEnumerable<string?>) by forwarding to span-based concat.
src/libraries/Common/tests/Tests/System/StringTests.cs Adds test coverage for the new fast-path inputs and a fallback enumerable shape.

Comment thread src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
Comment thread src/libraries/Common/tests/Tests/System/StringTests.cs
@prozolic
prozolic marked this pull request as ready for review July 8, 2026 14:20
Comment thread src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs Outdated
…ation.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
Copilot AI review requested due to automatic review settings July 8, 2026 16:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

@MihaZupan MihaZupan modified the milestones: 12.0.0, 11.0.0 Jul 8, 2026
@MihaZupan
MihaZupan enabled auto-merge (squash) July 8, 2026 17:16
@MihaZupan MihaZupan added the tenet-performance Performance related issue label Jul 8, 2026
@MihaZupan

Copy link
Copy Markdown
Member

Thanks

Copilot AI review requested due to automatic review settings July 9, 2026 13:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@prozolic

Copy link
Copy Markdown
Contributor Author

@MihaZupan CI seems to be failing due to a timeout. Could you check the current status?

@MihaZupan
MihaZupan merged commit 7a4bdd9 into dotnet:main Jul 10, 2026
140 of 142 checks passed
@prozolic

Copy link
Copy Markdown
Contributor Author

Thank you!

@prozolic
prozolic deleted the stringconcat branch July 10, 2026 12:28
@dotnet-milestone-bot dotnet-milestone-bot Bot modified the milestones: 11.0.0, 11.0-preview7 Jul 11, 2026
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Runtime community-contribution Indicates that the PR has been added by a community member tenet-performance Performance related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants