C# Using Performance

Performance optimization

The using statement affects the performance of methods. In some places, using statements are not necessary because the objects do not touch managed resources. We explore this further. We benchmark using statements on objects that do not require them.

Method versions

Note

Here we present the different method versions: first, the version that has several using statements. Notice how this method receives a byte[] array, which is a managed type. All the streams are created from byte arrays, so they cannot be linked to unmanaged resources like the disk.

Decompress GZIP
Version with using statements [C#]

static byte[] Decompress1(byte[] gzip)
{
    using (MemoryStream mem = new MemoryStream(gzip))
    using (GZipStream stream = new GZipStream(mem, CompressionMode.Decompress))
    {
	const int size = 4096;
	byte[] buffer = new byte[size];
	using (MemoryStream memory = new MemoryStream())
	{
	    int count = 0;
	    do
	    {
		count = stream.Read(buffer, 0, size);
		if (count > 0)
		{
		    memory.Write(buffer, 0, count);
		}
	    }
	    while (count > 0);
	    return memory.ToArray();
	}
    }
}

Next, we look at the version that has those three using blocks removed. This should be safe because, as noted, they were not created using unmanaged resources. In the intermediate language, this eliminates a lot of complexity and three try/finally constructs.

Version without using statements [C#]

static byte[] Decompress2(byte[] gzip)
{
    GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress);
    const int size = 4096;
    byte[] buffer = new byte[size];
    MemoryStream memory = new MemoryStream();
    int count = 0;
    do
    {
	count = stream.Read(buffer, 0, size);
	if (count > 0)
	{
	    memory.Write(buffer, 0, count);
	}
    }
    while (count > 0);
    return memory.ToArray();
}
GZIP compression

Third, we look at the program that loads a GZIP file from the disk and stores in a byte array. Note that the File.ReadAllBytes method does use the using construct in it; this is necessary because it opens an unmanaged resource (a file). The harness simply decompresses the file 200,000 times. To test memory usage, one of the two Decompress method calls can be commented out.

Benchmark program [C#]

using System.IO;
using System.IO.Compression;

class Program
{
    const int _max = 200000;
    static void Main()
    {
	byte[] array = File.ReadAllBytes("C:\\t2\\_default.gz");
	Decompress1(array);
	Decompress2(array);

	for (int i = 0; i < _max; i++)
	{
	    Decompress1(array);
	}
	for (int i = 0; i < _max; i++)
	{
	    Decompress2(array);
	}
    }
}

Results. Here we see the results to the experiment. Each of the methods took about the same amount of time to execute (9 seconds). This is very dependent on the file size. However, the Decompress2 method used consistently less memory. Thus, we can conclude that the unneeded using statements ended up wasting memory. This could be explained by the method size bloat and the increased complexity.

Benchmark results

Decompress1: Has 3 using statements.
Decompress2: Has 0 using statements.

Decompress1 Memory: 4852 K, 4804 K, 4852 K
Decompress2 Memory: 4768 K, 4752 K, 4784 K

Summary

The C# programming language

Using statements are very important in some situations. They can drastically reduce memory usage and fix severe bugs in some programs. You should not just remove all of them. However, if you can determine which using statements are safe to remove (those that do not touch managed resources) you can improve performance.

Using Statement Calls Dispose .NET Framework Info
.NET