TheDeveloperBlog.com

Home | Contact Us

CSharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript

C# string.Intern Method

This C# article covers the string.Intern method. It benchmarks the string intern pool and tests IsInterned.

String.Intern. The string.Intern method optimizes string memory and performance.

It allows you to put strings in the runtime's shared string pool. We call IsInterned to see if an interned string exists.

Example. The Intern static method can improve performance of string comparisons by more than two times. But it only rarely should be used. Intern puts strings in a common string pool maintained by the .NET runtime.

Static Method

The string pool is an optimization mainly for string literals, which are strings that you place directly in your code. They are surrounded by quotes. They are constant and immutable—meaning you can't change them.

String Literal

C# program that uses string.Intern

class Program
{
    static void Main()
    {
	// Regular string literal.
	string s1 = "I'm a string literal.";

	// Const string literal.
	const string s2 = "I'm also a string literal.";

	// Verbatim string literal.
	string s3 = @"I'm a string literal
with the verbatim syntax.";
    }
}

Example 2. You cannot change string literals. However, you can reassign the variable that points to them. This changes the reference, not the value. In this program, we demonstrate that the reference is only changed.

C# program that reassigns string literals

class Program
{
    static void Main()
    {
	string s = "I'm a string literal.";
	s = "I'm another literal.";
    }
}

Example 3. Intern takes a string you build up at runtime and basically turns it into a string literal. This makes comparisons much faster. MSDN says that the Intern method uses the intern pool to search for a string equal to the value.

And: If the string does not exist, a reference to str is added to the intern pool, then that reference is returned.

String.Intern: MSDN

C# program that in terns string

class Program
{
    static void Main()
    {
	// A.
	// String generated at runtime.
	// Is not unique in string pool
	string s1 = new StringBuilder().Append("cat").Append(" and dog").ToString();

	// B.
	// Interned string added at runtime.
	// Is unique in string pool.
	string s2 = string.Intern(s1);
    }
}

Equals. It is faster to compare interned strings because the method that C# implements string comparisons with always checks references first. If the references are equal, the comparison succeeds.

String Equals

Next: The Equals method is how the == operator is implemented in the .NET Framework. The first part tests the references for equality.

Also: EqualsHelper compares all the characters in a loop and is slower. It is called if the other conditions do not short-circuit.

Implementation of string.Equals: C#

public static bool Equals(string a, string b)
{
    return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b)));
}

Performance. My benchmark shows that the string.Intern improves performance by more than four times when the string comparison is always true. For the example, one comparison is false and one is true.

True, False

C# program that compares performance

using System;
using System.Text;

class Program
{
    static void Main()
    {
	// 1.
	// Create string at runtime.
	StringBuilder b = new StringBuilder().Append("cat ").Append(
	    "and ").Append("dog");
	// 2.
	// Get runtime string.
	string s1 = b.ToString();
	// 3.
	// Get string pool reference to string.
	string s2 = string.Intern(s1);

	// 4.
	// Three loops:
	// - Repeat benchmark 10 times
	// - Repeat inner loop 10000 times
	// - Repeat 2 tests 10000 times
	int m = 10000;
	for (int v = 0; v < 10; v++)
	{
	    int d = 0;
	    long t1 = Environment.TickCount;

	    // 5.
	    // Test regular string.
	    for (int i = 0; i < m; i++)
	    {
		for (int a = 0; a < m; a++)
		{
		    if (s1 == "cat and rabbit")
		    {
			d++; // false
		    }
		    if (s1 == "cat and dog")
		    {
			d--; // true
		    }
		}
	    }
	    long t2 = Environment.TickCount;

	    // 6.
	    // Test interned string.
	    for (int i = 0; i < m; i++)
	    {
		for (int a = 0; a < m; a++)
		{
		    if (s2 == "cat and rabbit")
		    {
			d++; // false
		    }
		    if (s2 == "cat and dog")
		    {
			d--; // true
		    }
		}
	    }
	    // 7.
	    // Write results.
	    long t3 = Environment.TickCount;
	    Console.Write((t2 - t1));
	    Console.WriteLine("," + (t3 - t2));
	}
	Console.ReadLine();
    }
}

Next, the tests were repeated 10 times and averaged. You can see benchmark results at the top. The string.Intern version is much faster when the strings being compared happen to be equal. When they are not, the performance is the same.

Intern method performance test results

No string.Intern:   1540 ms
With string.Intern:  736 ms [faster]

Discussion. The string.Intern method only speeds things up when two strings are equal. This only happens when an interned string is compared with another. Use string.Intern when the following characteristics are true.

More tips. Use string.Intern at startup. Usually using string.Intern is best at the start of your program. Use Intern when literals are compared. Use string.Intern only when two strings in the pool are compared.

Tip: Consider it when two Interned strings are compared, or one Interned string is compared with a literal.

Warning: Never use string.Intern without benchmarking unless you are experienced with it.

Benchmark

Also: Do not use it when you have fewer than tens of thousands of matches. Do not use it if the string comparisons will rarely be true.

IsInterned. With the string.IsInterned method, you can test whether a string is present in the internal table, without adding it if it is not. To begin, the string.IsInterned method receives a string reference and returns a string reference.

Info: If the internal string is found, that reference is returned. If no internal string is present, a null reference is returned.

Null

In the first part of this example, a string literal is tested. This reveals that string literals are always present in the intern table. On the other hand, a dynamically constructed string is not present in the intern table.

Note: IsInterned will return null in this case. It will not add the string to the intern table.

C# program that uses IsInterned method

using System;

class Program
{
    static void Main()
    {
	// See if a string literal is interned.
	string value1 = "cat";
	string value2 = string.IsInterned(value1);
	Console.WriteLine(value2);

	// See if a dynamically constructed string is interned.
	string value3 = "cat" + 1.ToString();
	string value4 = string.IsInterned(value3);
	Console.WriteLine(value4 == null);
    }
}

Output

cat
True

Usage. In most programs, you do not need to use the string.Intern or string.IsInterned methods. But if you have a program that uses the intern table in other places, you might want to use the string.IsInterned method.

Also, it could be used on string data that is possibly in the intern table, but that is not important enough to add if it is not already present. So IsInterned returns references to already existing data, but does not add anything.

In some programs: The string.IsInterned method can be used to reduce memory usage for string data.

Summary. String.Intern and IsInterned act on the string intern pool. Intern can make string comparisons four or more times faster. Only use it in specific situations. Always benchmark when using it. The string pool is an important optimization.