C# ToString Integer Optimization

String type

You want to optimize the ToString method when called on integers in the C# language. The ToString method actually allocates a new string each time it is called on an int variable; you can use an extension method to use a lookup table and avoid this allocation in some cases.

Example

Note

First we demonstrate code that introduces an extension method called ToStringLookup. This method can be used on any integer variable to use the ToString method only if the string is not found in the lookup table. Internally, the ToStringExtensions class stores a static string array that is the lookup table; it contains 200 string literals in the metadata. Then, the ToStringLookup extension method checks for values that are in the lookup table, and otherwise calls ToString.

This C# example program optimizes the ToString method on ints. It avoids allocations.

Extension method class file [C#]

using System.Globalization;

static class ToStringExtensions
{
    // Lookup table.
    static string[] _cache =
    {
	"0",
	"1",
	"2",
	"3",
	"4",
	"5",
	"6",
	"7",
	"8",
	"9",
	"10",
	"11",
	"12",
	"13",
	"14",
	"15",
	"16",
	"17",
	"18",
	"19",
	"20",
	"21",
	"22",
	"23",
	"24",
	"25",
	"26",
	"27",
	"28",
	"29",
	"30",
	"31",
	"32",
	"33",
	"34",
	"35",
	"36",
	"37",
	"38",
	"39",
	"40",
	"41",
	"42",
	"43",
	"44",
	"45",
	"46",
	"47",
	"48",
	"49",
	"50",
	"51",
	"52",
	"53",
	"54",
	"55",
	"56",
	"57",
	"58",
	"59",
	"60",
	"61",
	"62",
	"63",
	"64",
	"65",
	"66",
	"67",
	"68",
	"69",
	"70",
	"71",
	"72",
	"73",
	"74",
	"75",
	"76",
	"77",
	"78",
	"79",
	"80",
	"81",
	"82",
	"83",
	"84",
	"85",
	"86",
	"87",
	"88",
	"89",
	"90",
	"91",
	"92",
	"93",
	"94",
	"95",
	"96",
	"97",
	"98",
	"99",
	"100",
	"101",
	"102",
	"103",
	"104",
	"105",
	"106",
	"107",
	"108",
	"109",
	"110",
	"111",
	"112",
	"113",
	"114",
	"115",
	"116",
	"117",
	"118",
	"119",
	"120",
	"121",
	"122",
	"123",
	"124",
	"125",
	"126",
	"127",
	"128",
	"129",
	"130",
	"131",
	"132",
	"133",
	"134",
	"135",
	"136",
	"137",
	"138",
	"139",
	"140",
	"141",
	"142",
	"143",
	"144",
	"145",
	"146",
	"147",
	"148",
	"149",
	"150",
	"151",
	"152",
	"153",
	"154",
	"155",
	"156",
	"157",
	"158",
	"159",
	"160",
	"161",
	"162",
	"163",
	"164",
	"165",
	"166",
	"167",
	"168",
	"169",
	"170",
	"171",
	"172",
	"173",
	"174",
	"175",
	"176",
	"177",
	"178",
	"179",
	"180",
	"181",
	"182",
	"183",
	"184",
	"185",
	"186",
	"187",
	"188",
	"189",
	"190",
	"191",
	"192",
	"193",
	"194",
	"195",
	"196",
	"197",
	"198",
	"199"
    };

    // Lookup table last index.
    const int _top = 199;

    public static string ToStringLookup(this int value)
    {
	// See if the integer is in range of the lookup table.
	// ... If it is present, return the string literal element.
	if (value >= 0 &&
	    value <= _top)
	{
	    return _cache[value];
	}
	// Fall back to ToString method.
	return value.ToString(CultureInfo.InvariantCulture);
    }
}

Program that uses extension method [C#]

using System;

class Program
{
    static void Main()
    {
	// Use the ToString lookup extension method.
	Console.WriteLine((-100).ToStringLookup());
	Console.WriteLine(5.ToStringLookup());
	Console.WriteLine(199.ToStringLookup());
	Console.WriteLine(200.ToStringLookup());
    }
}

Output

-100
5
199
200

Using ToStringLookup method. The ToStringLookup method is introduced in the ToStringExtensions static class. It uses the extension method syntax. In the Main entry point, we call the ToStringLookup extension method on different instance expressions. The ToStringLookup method returns a string representation of each integer, in the same way as ToString would; however, it avoids an allocation for two of the calls.

Performance

Performance optimization

The ToString lookup method shown in this article is much faster when you call ToString on an integer of value 0 to 199. In that range, a string literal is simply returned; this avoids an entire heap allocation. In the .NET Framework, a string requires 20 bytes of memory as well as 2 bytes for each character. The ToStringLookup method avoids this overhead on multiple values of 0-199.

String Memory

Heuristics and metrics. The method shown above only stores the first 200 strings in the metadata as string literals. You can store many more strings, and this would also help performance. You could use a Dictionary, but this could introduce threading problems and might cause extreme memory usage. In my tests, a ToString call on a zero integer requires about 100 ns, while a ToStringLookup call on a zero requires about 3 ns.

Summary

The C# programming language

We looked at a cache class that can store common string representations of integers. The ToStringLookup method can then be used to optimize the conversion of integers to strings. This results in less overall memory usage, much faster performance, but additional complexity and greater initial memory usage.

ToString Usage String Type
.NET