TheDeveloperBlog.com


C# Integer Append Optimization

Integer append. Appending an integer as a string is inefficient. This is the case with StringBuilder or HttpResponse in the .NET Framework. The implementation must convert the integer to a string. This is allocated upon the managed heap.


Example. To start, let's look at the benchmark harness itself, which tests the Append method on the StringBuilder type to the custom Digits method. Each StringBuilder is appended to with the numbers 0 - 19999.

Also, the program also shows the Digits extension method. This method receives an integer parameter. It extracts each digit from the integer at a time from left to right, and then appends it to the underlying buffer.

Int TypeStringBuilder Append
C# program that optimizes integer appends

using System;
using System.Diagnostics;
using System.Text;

class Program
{
    const int _max = 1000;
    static void Main()
    {
	GC.Collect();
	StringBuilder b1 = new StringBuilder();
	var s1 = Stopwatch.StartNew();
	for (int i = 0; i < _max; i++)
	{
	    for (int a = 0; a < 20000; a++)
	    {
		b1.Append(a);
	    }
	    b1.Length = 0;
	}
	s1.Stop();
	GC.Collect();
	StringBuilder b2 = new StringBuilder();
	var s2 = Stopwatch.StartNew();
	for (int i = 0; i < _max; i++)
	{
	    for (int a = 0; a < 20000; a++)
	    {
		b2.Digits(a);
	    }
	    b2.Length = 0;
	}
	s2.Stop();
	Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds) / _max).ToString("0.00 ms"));
	Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds) / _max).ToString("0.00 ms"));
	Console.Read();
    }
}

static class Extensions
{
    public static void Digits(this StringBuilder builder, int number)
    {
	if (number >= 100000000)
	{
	    // Use system ToString.
	    builder.Append(number.ToString());
	    return;
	}
	if (number < 0)
	{
	    // Negative.
	    builder.Append(number.ToString());
	    return;
	}
	int copy;
	int digit;
	if (number >= 10000000)
	{
	    // 8.
	    copy = number % 100000000;
	    digit = copy / 10000000;
	    builder.Append((char)(digit + 48));
	}
	if (number >= 1000000)
	{
	    // 7.
	    copy = number % 10000000;
	    digit = copy / 1000000;
	    builder.Append((char)(digit + 48));
	}
	if (number >= 100000)
	{
	    // 6.
	    copy = number % 1000000;
	    digit = copy / 100000;
	    builder.Append((char)(digit + 48));
	}
	if (number >= 10000)
	{
	    // 5.
	    copy = number % 100000;
	    digit = copy / 10000;
	    builder.Append((char)(digit + 48));
	}
	if (number >= 1000)
	{
	    // 4.
	    copy = number % 10000;
	    digit = copy / 1000;
	    builder.Append((char)(digit + 48));
	}
	if (number >= 100)
	{
	    // 3.
	    copy = number % 1000;
	    digit = copy / 100;
	    builder.Append((char)(digit + 48));
	}
	if (number >= 10)
	{
	    // 2.
	    copy = number % 100;
	    digit = copy / 10;
	    builder.Append((char)(digit + 48));
	}
	if (number >= 0)
	{
	    // 1.
	    copy = number % 10;
	    digit = copy / 1;
	    builder.Append((char)(digit + 48));
	}
    }
}

Results

2.97 ms
2.03 ms

We see the results are not dramatic. They are measured in milliseconds, not nanoseconds. The Digits method, in this case, performed the task nearly one millisecond faster—about one third faster.

Info: StringBuilder must allocate the integers in separate objects. But the Digits method does not require any allocations.

Object TypeConstructor Examples: Keywords and Syntax

Discussion. The Digits method doesn't handle negative numbers or numbers greater than eight digits. These restrictions could be relaxed. The bulk of the Digits method uses modulo division and regular division to extract digits.

The if-statements are chained so that the leftmost digits are printed first. As we go along, each digit is converted to a character by adding 48. This converts an integer to its integer character representation in ASCII.

Should you bother? This method may be worthwhile using in some cases, such as in an ASP.NET website that must print thousands of numbers to the HttpResponse. You could change the method so it receives an HttpResponse.

ASP.NET Tutorials: Requests and Responses

But: In most programs, particularly those where performance is not key, this optimization is not worthwhile.


Summary. It is possible to optimize the implementation of how integers are appended to an underlying buffer stream in the C# language and .NET Framework. It avoids intermediate allocations and representations.