admin管理员组

文章数量:1122879

I need to test how different ways of string formatting affect the performance. And I found a surprising result. Let me explain with two test cases:

// Test Case 2
for (int i = 0; i < 10000000; i++)
{
    var message = string.Format("Test Message{0}{1}{2}{3}{4}", int1, int2, int3, int4, int5);
}

// Test Case 3
for (int i = 0; i < 10000000; i++)
{
    var message = string.Format("Test Message{0}{1}{2}{3}{4}", new object[] { int1, int2, int3, int4, int5 });
}

Output

Test Case 2 - Elapsed Time: 1770 ms
Test Case 2 - Memory Used: 28528 bytes
Test Case 3 - Elapsed Time: 1685 ms
Test Case 3 - Memory Used: 360 bytes

The Question As you can see, memory usage is ~100 times more in Test Case 2 (where string.Format uses arguments directly) compared to Test Case 3 (where arguments are wrapped in an object[] explicitly).

But why?

As I understand, string.Format should implicitly convert arguments into an object[] anyway. What is happening here that causes such a drastic difference in memory usage?

I am expecting to see nearly equal memory usage.

Notes

  1. int1 = 1, int2 = 2 ...
  2. to track time I use stopWatch
  3. to track memory I use beforeMemory = GC.GetTotalMemory(true); and compare the values before and after the execution

Full code that I use for "benchmarking":

using System.Diagnostics;

internal class Program
{
    private static void Main(string[] args)
    {
        //var fileStream = new FileStream(
        //"C:\\Users\\osavytskyy\\Documents\\Projects\\TestScript\\file.txt",
        //FileMode.OpenOrCreate,
        //FileAccess.ReadWrite,
        //FileShare.None); // Prevents other processes from reading or writing

        //Console.Read();
        //fileStream.Dispose();

        var int1 = 1;
        var int2 = 2;
        var int3 = 3;
        var int4 = 4;
        var int5 = 5;

        var filestream = new LogStreamFile("C:\\Projects\\TestProj\\file.txt");

        Stopwatch stopwatch = new Stopwatch();

        // Test Case 1
        long beforeMemory = GC.GetTotalMemory(true);
        stopwatch.Start();
        for (int i = 0; i < 10000000; i++)
        {
            var message = string.Format("Test Message");
        }
        stopwatch.Stop();
        Console.WriteLine($"Test Case 1 - Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
        Console.WriteLine($"Test Case 1 - Memory Used: {GC.GetTotalMemory(true) - beforeMemory} bytes");
       
        // Test Case 2
        beforeMemory = GC.GetTotalMemory(true);
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < 10000000; i++)
        {
            var message = string.Format("Test Message{0}{1}{2}{3}{4}", int1, int2, int3, int4, int5);
        }
        stopwatch.Stop();
        Console.WriteLine($"Test Case 2 - Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
        Console.WriteLine($"Test Case 2 - Memory Used: {GC.GetTotalMemory(true) - beforeMemory} bytes");
        
        // Test Case 3
        beforeMemory = GC.GetTotalMemory(true);
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < 10000000; i++)
        {
            //var arg = new object[] { var1, var2, var3, var4, var5 };
            var message = string.Format("Test Message{0}{1}{2}{3}{4}", new object[] { int1, int2, int3, int4, int5 });
        }
        stopwatch.Stop();
        Console.WriteLine($"Test Case 3 - Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
        Console.WriteLine($"Test Case 3 - Memory Used: {GC.GetTotalMemory(true) - beforeMemory} bytes");
        

        // Test Case 4
        beforeMemory = GC.GetTotalMemory(true);
        stopwatch.Reset();
        stopwatch.Start();
        
        for (int i = 0; i < 10000000; i++)
        {
            var message = $"Test Message{int1}{int2}{int3}{int4}{int5}";
        }
        stopwatch.Stop();
        Console.WriteLine($"Test Case 4 - Elapsed Time: {stopwatch.ElapsedMilliseconds} ms");
        Console.WriteLine($"Test Case 4 - Memory Used: {GC.GetTotalMemory(true) - beforeMemory} bytes");
        
    }
}

本文标签: