Difference Between String Interpolation and String Concatenation

Difference between String interpolation and String concatenation

From a speed point of view, to differentiate concatenation (value1 + "-" + value2) and interpolation ("\(value1)-\(value2)"), results may depend on the number of operations done to obtain the final string.

My results on an iPhone 8 show that:

  • if there is roughly < 30 substrings to attach together, then concatenation is faster
  • if there is roughly > 30 substrings to attach together, then interpolation is faster

Thank you Sirens for figuring out that one wasn't always faster than the other!

Try it yourself (and don't forget to adapt the tested character set and iterations for your needs):

import UIKit

class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

DispatchQueue.global(qos: .default).async {
ViewController.buildDataAndTest()
}
}

private static func buildDataAndTest(times: Int = 1_000) {
let characterSet = CharacterSet.alphanumerics
characterSet.cacheAllCharacters()
let data: [(String, String)] = (0 ..< times).map { _ in
(characterSet.randomString(length: 50), characterSet.randomString(length: 20))
}
_ = testCIA(data)
_ = testInterpol(data)
print("concatenation: " + String(resultConcatenation))
print("interpolation: \(resultInterpolation)")
}

/// concatenation in array
static var resultConcatenation: CFTimeInterval = 0
private static func testCIA(_ array: [(String, String)]) -> String {
var foo = ""
let start = CACurrentMediaTime()
for (a, b) in array {
foo = foo + " " + a + "+" + b
}
resultConcatenation = CACurrentMediaTime() - start
return foo
}

/// interpolation
static var resultInterpolation: CFTimeInterval = 0
private static func testInterpol(_ array: [(String, String)]) -> String {
var foo = ""
let start = CACurrentMediaTime()
for (a, b) in array {
foo = "\(foo) \(a)+\(b)"
}
resultInterpolation = CACurrentMediaTime() - start
return foo
}
}

extension CharacterSet {
static var cachedCharacters: [Character] = []

public func cacheAllCharacters() {
CharacterSet.cachedCharacters = characters()
}

/// extracting characters
/// https://stackoverflow.com/a/52133647/1033581
public func characters() -> [Character] {
return codePoints().compactMap { UnicodeScalar($0) }.map { Character($0) }
}
public func codePoints() -> [Int] {
var result: [Int] = []
var plane = 0
for (i, w) in bitmapRepresentation.enumerated() {
let k = i % 8193
if k == 8192 {
plane = Int(w) << 13
continue
}
let base = (plane + k) << 3
for j in 0 ..< 8 where w & 1 << j != 0 {
result.append(base + j)
}
}
return result
}

// http://stackoverflow.com/a/42895178/1033581
public func randomString(length: Int) -> String {
let charArray = CharacterSet.cachedCharacters
let charArrayCount = UInt32(charArray.count)
var randomString = ""
for _ in 0 ..< length {
randomString += String(charArray[Int(arc4random_uniform(charArrayCount))])
}
return randomString
}
}

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.

Difference between interpolation and concatenation in JavaScript?

In Angular js interpolation helps in getting model render on the view. Where as concatenation is to allow the strings to be join together with '+' it may or may not include interpolation .

Example:-

{{}} --> interpolation

It allows your model to bind in view:-

Model:-  $scope.name="rachit";

View:- <div>{{name}}</div>

result in View after rendering :- <div>rachit</div>

'+'-->concatenation

It allows to concat the values either in view or in controller or even inside interpolation.

Controller:-
$scope.name="rachit"+"gulati";

View
<div>{{name+name}}</div> //Here interpolation also comes into picture.
result:-"<div>rachitgualtirachitgualti</div>"

UPDATE 1:-

EDIT: Is this interpolation or concatenation (I don't just put strings
together but an a variable is resolved here)?

var test1 = "Hello";
var number = 2 + 3;
console.log(test1 + " " + number);

It is simple concatenation in javascript here angular is not come into picture.

UPDATE 2:-

Hmm.... I guess whenever I use AngularJS library to combine some
expression, then it's interpolation, right?

Concatenation is general term for every programming to concat two strings together.Where as in angualr js interpolation is done by {{}} with the help of $interpolation service.

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 pre-NET6 we reduced the allocations a bit.

String concatenation vs. interpolation in Ruby

Whenever TIMTOWTDI (there is more than one way to do it), you should look for the pros and cons. Using "string interpolation" (the second) instead of "string concatenation" (the first):

Pros:

  • Is less typing
  • Automatically calls to_s for you
  • More idiomatic within the Ruby community
  • Faster to accomplish during runtime

Cons:

  • Automatically calls to_s for you (maybe you thought you had a string, and the to_s representation is not what you wanted, and hides the fact that it wasn't a string)
  • Requires you to use " to delimit your string instead of ' (perhaps you have a habit of using ', or you previously typed a string using that and only later needed to use string interpolation)

Is there have any difference between string concat and string interpolation in EF Core?

Interpolated strings

Interpolated strings can be translated into a call String.Format(). This happens when you store it in a string variable:

string sql1 = $"...";
var finds = db.JournalInfos.FromSql<JournalInfo>(sql1);

However, if you pass the interpolated string directly (like in your screenshot) it will be of type FormattableString according to the docs and method parameters:

var finds = db.JournalInfos.FromSql<JournalInfo>($"...");

In the second case, the database provider formats your query and escapes dangerous character to avoid SQL injection attacks. If you want to find out the exact difference you have to enable sensitive data logging in EF Core to see the actual queries.

LINQ approach

For this particular problem there might be an easier solution than using raw SQL queries. LINQ extension methods get compiled to SQL, are safe in terms of SQL injection and much easier to maintain if your database model changes.

DateTime date; // This is the same variable like in line 1603 on your screenshot

DateTime minInclusive = new DateTime(date.Year, date.Month, 1);
DateTime maxExclusive = minInclusive.AddMonths(1);

var finds = db.JournalInfos
.Where(journal => journal.Date >= minInclusive && journal < maxExclusive)
.OrderBy(journal => journal.Index)
.ToList();

Kotlin - String interpolation $ vs concatenate using plus-equals +=

The best way to find the difference is to look at the generated bytecode (I will use Kotlin 1.4.10).

Bytecode generated for name += " $someInt":

ALOAD 0
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
SWAP
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
BIPUSH 32
INVOKEVIRTUAL java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;
ILOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 0

Bytecode generated for name = "$name $someInt":

NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 0
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
BIPUSH 32
INVOKEVIRTUAL java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder;
ILOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 0

Results are pretty much the same, but in the first case there is an extra SWAP operation, cause argument for first append operation was loaded onto stack too early (before StringBuilder was created, and now they need to be swapped).

TL;DR

Turns out that:

  1. StringBuilder is created in both cases
  2. There are 3 appends (name, whitespace & someInt) in both cases
  3. Second one is slightly more effective.

Difference between String interpolation and String initializer in Swift

String interpolation "\(item)" gives you the result of calling description on the item. String(item) calls a String initializer and returns a String value, which frequently is the same as the String you would get from string interpolation, but it is not guaranteed.

Consider the following contrived example:

class MyClass: CustomStringConvertible {
var str: String

var description: String { return "MyClass - \(str)" }

init(str: String) {
self.str = str
}
}

extension String {
init(_ myclass: MyClass) {
self = myclass.str
}
}

let mc = MyClass(str: "Hello")
String(mc) // "Hello"
"\(mc)" // "MyClass - Hello"

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:



Leave a reply



Submit