String repeat method for C#
C# doesn’t have built-in function to repeat a string. There are different version for string.Repeat() available in internet and it’s up to reader to find out which version works better. Here is the list of most popular implementations I found across the web. I list my findings here with the results of simple performance test.
String repeat implementations
I found these six implementations to be most popular:
- ForLoop – string is repeated in regular for loop
- PadLeft – string is repeated using empty string, PadLef() method and Replace() method
- Replace – string is repeated using String constructor and Replace() method
- Concat – string is repeated using Concat() method on Enumerable.Repeat()
- StringBuilderInsert – sring is repeated using Insert() method on StringBuilder()
- StringBuilderAppend – string is repeated using AppendJoin() method on StingBuilder()
The last one is a little bit rare actually but I added it here as it gave also great results.
String repeat methods
Here is the source code of all six methods. I left out sanity checks and other bullet-proofing to keep methods clean of all additional functionalities.
static string RepeatForLoop(string s, int n)
{
var result = s;
for (var i = 0; i < n - 1; i++)
{
result += s;
}
return result;
}
static string RepeatPadLeft(string s, int n)
{
return "".PadLeft(n, 'X').Replace("X", s);
}
static string RepeatReplace(string s, int n)
{
return new String('X', n).Replace("X", s);
}
static string RepeatConcat(string s, int n)
{
return String.Concat(Enumerable.Repeat(s, n));
}
static string RepeatStringBuilderInsert(string s, int n)
{
return new StringBuilder(s.Length * n)
.Insert(0, s, n)
.ToString();
}
static string RepeatStringBuilderAppend(string s, int n)
{
return new StringBuilder(s.Length * n)
.AppendJoin(s, new string[n+1])
.ToString();
}
Performance of string repeat methods
I ran simple performance tests on all those methods. Here is what I exactly did:
- every method was run one million times,
- test string was simply “Test!”,
- test string was repeated by all methods 130 times,
- during tests all programs and services with more impact on machine performance were closed,
- test code was built on latest .NET Core in Release mode and run on console,
- I measured time that takes for each method to run one million times.
Here are my performance test results. Times are given in milliseconds (divide by thousand to get seconds).
We can clearly see that ForLoop is total overkill. It doesn’t have any optimizations and it results in big number of allocations and buffer copies. It is out of question to use this method in production. But be warned – ForLoop works fast and doesn’t show any signs of performance problems if it is not used intensively.
Other methods are approximately up to 6.5 times faster. In all my tests StringBuilderInsert and StringBuilderAppend gave the best results. StringBuilderAppend was on all test few hundred milliseconds better that StringBuilderInsert. As it was the winner I present it here as extension method you can add to some library.
public static class StringExtensions
{
public static string Repeat(this string s, int n)
{
return new StringBuilder(s.Length * n)
.AppendJoin(s, new string[n+1])
.ToString();
}
}
Wrapping up
There are many implementations available for string repeat method and not all of them are equal from performance perspective. We saw that most primitive version using for-loop has awful performance while more cumbersome method using StringBuilder and AppendJoin was the best. If there is more than one implementation available then it’s good idea to make some tests to see what is best peforming implementation and go with this.
String.Join(null, Enumerable.Repeat(myString, aBigInt32));
Forgot new String(“-“, 100);
I think this would be faster, but no imperical evidence.
@Justin Tubbs
That doesn’t compile in .Net Framework as far as I know. I think you mean: new String (‘-‘, 100);
The restriction there is that you can only pass a character as the first parameter so couldn’t create: “ABABABAB”
Is there a method using the new Span that would be even faster?
Your for loop is slow per your adding to a string which recreates the char array every time you add to the string (string == char[]). A classic reason to use StringBuilder. My bet if you still had the for loop and used StringBuilder Append you would have a better measurement of the for loop. Actually I am curious if it would be slower or about the same as the other methods.
To Dave’s point, I bet the best you can do is to use String.Create. Basically, you only do one allocation and then fill an Span in the not-yet-constructed String with a delegate:
static String RepeatSpan(String s, Int32 n)
{
return String.Create(s.Length * n,
s,
(chars, srcStr) =>
{
ReadOnlySpan src = srcStr.AsSpan();
while (src.TryCopyTo(chars))
{
chars = chars.Slice(src.Length);
}
});
}
If you don’t want to depend on the latest runtime, StringBuilder.Append is faster than Insert (because you don’t need to shift the remainder), so you can improve RepeatStringBuilderInsert
static String RepeatStringBuilderAppend(String s, Int32 n)
{
StringBuilder sb = new StringBuilder(s.Length * n);
for (Int32 i = 0; i < n; i ++)
{
sb.Append(s);
}
return sb.ToString();
}
Use case? I can honestly say that in a couple of decades working as a developer I can’t recall an occasion when I’ve needed to initialize a string with a large number of repetitions of a smaller string.. :-)
Are you aware of the constructor overload for a string that takes a string and an int? If the to of my head it’s new string(text, int). Should do what your looking for
>not testing Array.Copy()
use stringbuilder inside the for loop and append, giving it the label “for loop is the slowest” is very unfair, every programmer knows string deletion and creation and then copy is a very slow operation, why make a loop look bad about what the programmer didnt know?
“for loop” here has its own context due to one method where it is used.
Pingback:Dew Drop - July 24, 2018 (#2772) - Morning Dew
Seriously, give String.Join a go in your benchmark: it uses the internal StringBuilderCache in addition to following the technique of your fastest contender so far, AND it’s built in to most implementations of the framework:
https://referencesource.microsoft.com/mscorlib/a.html#881354baa56fc358
David, I tried it out. It got 2870 millisecond as an average. It makes use of StringBuilder but it is not so fast.
It would be nice to note that StringBuilder.AppendJoin is only available in .Net Core and not the regular .Net Framework.
I wonder if StringBuilder.AppendJoin(s, Enumerable.Repeat(String.Empty, n+1)) would be better than allocating an empty string array.