String Interpolation VS String.Format

String Interpolation vs String.Format

The answer is both yes and no. ReSharper is fooling you by not showing a third variant, which is also the most performant. The two listed variants produce equal IL code, but the following will indeed give a boost:

myString += $"{x.ToString("x2")}";

Full test code

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Diagnostics.Windows;
using BenchmarkDotNet.Running;

namespace StringFormatPerformanceTest
{
[Config(typeof(Config))]
public class StringTests
{
private class Config : ManualConfig
{
public Config() => AddDiagnoser(MemoryDiagnoser.Default, new EtwProfiler());
}

[Params(42, 1337)]
public int Data;

[Benchmark] public string Format() => string.Format("{0:x2}", Data);
[Benchmark] public string Interpolate() => $"{Data:x2}";
[Benchmark] public string InterpolateExplicit() => $"{Data.ToString("x2")}";
}

class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<StringTests>();
}
}
}

Test results

|              Method | Data |      Mean |  Gen 0 | Allocated |
|-------------------- |----- |----------:|-------:|----------:|
| Format | 42 | 118.03 ns | 0.0178 | 56 B |
| Interpolate | 42 | 118.36 ns | 0.0178 | 56 B |
| InterpolateExplicit | 42 | 37.01 ns | 0.0102 | 32 B |
| Format | 1337 | 117.46 ns | 0.0176 | 56 B |
| Interpolate | 1337 | 113.86 ns | 0.0178 | 56 B |
| InterpolateExplicit | 1337 | 38.73 ns | 0.0102 | 32 B |

New test results (.NET 6)

Re-ran the test on .NET 6.0.9.41905, X64 RyuJIT AVX2.

|              Method | Data |      Mean |   Gen0 | Allocated |
|-------------------- |----- |----------:|-------:|----------:|
| Format | 42 | 37.47 ns | 0.0089 | 56 B |
| Interpolate | 42 | 57.61 ns | 0.0050 | 32 B |
| InterpolateExplicit | 42 | 11.46 ns | 0.0051 | 32 B |
| Format | 1337 | 39.49 ns | 0.0089 | 56 B |
| Interpolate | 1337 | 59.98 ns | 0.0050 | 32 B |
| InterpolateExplicit | 1337 | 12.85 ns | 0.0051 | 32 B |

The InterpolateExplicit() method is faster since we now explicitly tell the compiler to use a string. No need to box the object to be formatted. Boxing is indeed very costly. Also note that NET 6 reduced both CPU and memory allocations - for all methods.

New test results (.NET 7)

Re-ran the test on .NET 7.0.122.56804, X64 RyuJIT AVX2.

|              Method | Data |      Mean |   Gen0 | Allocated |
|-------------------- |----- |----------:|-------:|----------:|
| Format | 42 | 41.04 ns | 0.0089 | 56 B |
| Interpolate | 42 | 65.82 ns | 0.0050 | 32 B |
| InterpolateExplicit | 42 | 12.19 ns | 0.0051 | 32 B |
| Format | 1337 | 41.02 ns | 0.0089 | 56 B |
| Interpolate | 1337 | 59.61 ns | 0.0050 | 32 B |
| InterpolateExplicit | 1337 | 13.28 ns | 0.0051 | 32 B |

No significant changes since .NET 6.

Which of one from string interpolation and string.format is better in performance?

Which of one from string interpolation and string.format is better in performance?

Neither of them is better since they are equal on run-time. String interpolation is rewritten by the compiler to string.Format, so both statements are exactly the same on run-time.

Also what are the best fit scenarios for use of them?

Use string interpolation if you have variables in scope which you want to use in your string formatting. String interpolation is safer since it has compile time checks on the validness of your string. Use string.Format if you load the text from an external source, like a resource file or database.

What is the point of string interpolation?

I think the answer is in the official reference docs on String Interpolation:

String interpolation provides a more readable and convenient syntax to create formatted strings than a string composite formatting feature.

So it's not a whole new language feature to replace string concatenation per se... it's more like syntactic sugar over existing string format functions. In other words, the strengths appear when you're actually formatting the elements in a string:

var birthdate = new DateTime(1990,9,22);

Console.WriteLine($"Your birthday is {birthdate:MMMM dd}!"); // string interpolation

Console.WriteLine("Your birthday is {0:MMMM dd}!", birthdate); // implicit String.Format

var message = "Your birthday is " +
String.Format("{0:MMMM dd}", birthdate) + "!"; // explicit String.Format

Console.WriteLine(message);

// Your birthday is September 22!

String formatting vs String Interpolation

String formatting is a rather general term of generating string content from data using some parameters. For example, creating date strings from date objects for a specific date format, number strings from numbers with a particular number of decimal digits or a number of leading spaces and zeroes etc. It can also involve templates, like in sprintf function present in C or many other languages, or e.g. str.format in Python. For example, in Ruby:

sprintf("%06.2f", 1.2) # float, length 6, 2 decimals, leading zeroes if needed
# => "001.20"

String interpolation is a much more restricted concept: evaluating expressions embedded inside string literals and replacing them with the result of such evaluation. For example, in Ruby:

"Two plus two is #{2+2}"
# => "Two plus two is 4"

Some languages can perform formatting inside interpolation. For example, in Python:

f"Six divided by five is {6/5:06.2f}"
# => "Six divided by five is 001.20"

The concepts are language-agnostic. However, it is not guaranteed that all programming languages will have a built-in capability for one or both of them. For example, C has no string interpolation, but it has string formatting, using the printf family of functions; and until somewhat recently, JavaScript did not have either, and any formatting was done in a low-tech way, using concatenation and substringing.

When is it better to use String.Format vs string concatenation?

Before C# 6

To be honest, I think the first version is simpler - although I'd simplify it to:

xlsSheet.Write("C" + rowIndex, null, title);

I suspect other answers may talk about the performance hit, but to be honest it'll be minimal if present at all - and this concatenation version doesn't need to parse the format string.

Format strings are great for purposes of localisation etc, but in a case like this concatenation is simpler and works just as well.

With C# 6

String interpolation makes a lot of things simpler to read in C# 6. In this case, your second code becomes:

xlsSheet.Write($"C{rowIndex}", null, title);

which is probably the best option, IMO.

Why C# string interpolation slower than regular string concat?

TLDR: Interpolated strings are overall the best and they only allocate more memory in your benchmarks because you are using old .Net and cached number strings

There's a lot to talk about here.

First off, a lot of people think string concatenation using + will always create a new string for every +. That might be the case in a loop, but if you use lots of + one after another, the compiler will actually replace those operators with a call to one string.Concat, making the complexity O(n), not O(n^2). Your DebugPrintConcat actually compiles to this:

public void DebugPrintConcat()
{
profiler.PrineDebug(string.Concat("sometext_", five.ToString(), "_", six.ToString()));
}

It should be noted that in your specific case, you are not benchmarking string allocation for the integers because .Net caches string instances for small numbers, so those .ToString() on five and six end up allocating nothing. The memory allocation would've been much different if you used bigger numbers or formatting (like .ToString("10:0000")).

The three ways of concating strings are +(that is, string.Concat()), string.Format() and interpolated strings. Interpolated strings used to be the exact same as string.Format(), as $"..." was just syntactic sugar for string.Format(), but that is not the case anymore since .Net 6 when they got a redesign via Interpolated String Handlers

Another myth I think I have to address is that people think that using string.Format() on structs will always lead to first boxing the struct, then creating an intermediate string by calling .ToString() on the boxed struct. That is false, for years now, all primitive types have implemented ISpanFormattable which allowed string.Format() to skip creating an intermediate string and write the string representation of the object directly into the internal buffer. ISpanFormattalbe has gone public with the release of .Net 6 so you can implement it for your own types, too (more on that at the end of this answer)

About memory characteristics of each approach, ordered from worst to best:

  • string.Concat() (the overloads accepting objects, not strings) is the worst because it will always box structs and create intermediate strings (source: decompilation using ILSpy)
  • + and string.Concat() (the overloads accepting strings, not objects) are slightly better than the previous, because while they do use intermediate strings, they don't box structs
  • string.Format() is generally better than previous because as mentioned earlier it does need to box structs, but not make an intermediate string if the structs implement ISpanFormattable (which was internal to .Net until not too long ago, but the performance benefit was there nevertheless). Furthermore, it is much more likely string.Format() won't need to allocate an object[] compared to previous methods
  • Interpolated strings are the best because with the release of .Net 6, they don't box structs, and they don't create intermediate strings for types implementing ISpanFormattable. The only allocation you will generally get with them is just the returned string and nothing else.

To support the claims above, I'm adding a benchmark class and benchmark results below, making sure to avoid the situation in the original post where + performs best only because strings are cached for small ints:

[MemoryDiagnoser]
[RankColumn]
public class ProfilerBench
{
private float pi = MathF.PI;
private double e = Math.E;
private int largeInt = 116521345;

[Benchmark(Baseline = true)]
public string StringPlus()
{
return "sometext_" + pi + "_" + e + "_" + largeInt + "...";
}

[Benchmark]
public string StringConcatStrings()
{
// the string[] overload
// the exact same as StringPlus()
return string.Concat("sometext_", pi.ToString(), "_", e.ToString(), "_", largeInt.ToString(), "...");
}

[Benchmark]
public string StringConcatObjects()
{
// the params object[] overload
return string.Concat("sometext_", pi, "_", e, "_", largeInt, "...");
}

[Benchmark]
public string StringFormat()
{
// the (format, object, object, object) overload
// note that the methods above had to allocate an array unlike string.Format()
return string.Format("sometext_{0}_{1}_{2}...", pi, e, largeInt);
}

[Benchmark]
public string InterpolatedString()
{
return $"sometext_{pi}_{e}_{largeInt}...";
}
}

Results are ordered by bytes allocated:





























































MethodMeanErrorStdDevRankGen 0Allocated
StringConcatObjects293.9 ns1.66 ns1.47 ns40.0386488 B
StringPlus266.8 ns2.04 ns1.91 ns20.0267336 B
StringConcatStrings278.7 ns2.14 ns1.78 ns30.0267336 B
StringFormat275.7 ns1.46 ns1.36 ns30.0153192 B
InterpolatedString249.0 ns1.44 ns1.35 ns10.0095120 B

A string with string interpolation format from database use it in C# code

The interpolated strings are interpreted by the compiler. I.e., for example in

string a = "fox";
string b = "jumps over";

// this line ...
string s = $"The quick brown {a} {b} the lazy dog";

... is converted to

string s = String.Format("The quick brown {0} {1} the lazy dog", a, b);

... by the compiler.

Therefore, you cannot use string interpolation at runtime with variable names in a (regular) string.

You must use String.Format at runtime:

string a = "The quick brown {0} {1} the lazy dog";
string b = "fox;jumps over";

string[] split = b.Split(';');
string c = String.Format(a, split[0], split[1]);
Console.Writeline(c):

Note that a runtime, the names of the local variables are not known. If you decompile a compiled C# programm, the decompiled code will contain generic names for local variables like l1, l2 etc. (depending on the decompiler).

Memory usage of concatenating strings using interpolated vs + operator

The author doesn't actually say that one makes better use of memory than the other. It says that the one method "makes good use of memory" in the abstract, which, by itself, doesn't really mean much of anything.

But regardless of what they said, the two methods aren't going to be meaningfully different in their implementation. Neither is going to be meaningfully different from the other in terms of memory or time.



Related Topics



Leave a reply



Submit