Since its inception, .NET has provided several methods for string formatting, with string.Format being a primary tool. However, this method had its drawbacks, such as the need for parsing the composite format string on each call and the boxing allocations for value types. This approach, while functional, was not optimized for performance, especially in scenarios where the format string doesn’t change across invocations.

The Advent of String Interpolation

C# 6 introduced string interpolation as a syntactic sugar, simplifying the way developers could concatenate strings and expressions. Improved further in .NET 6 and C# 10, string interpolation moved the parsing to compile-time, significantly reducing runtime overhead and eliminating the need for boxing allocations. This was a considerable step forward, but it had its limitation: the format string needed to be known at compile-time.

Real-world applications often require dynamic string formatting where the format strings are known only at runtime, such as those pulled from resource files or external configurations. In such cases, string.Format remained the only option, until now.

Introducing CompositeFormat

.NET 8 introduces CompositeFormat, a class that brings the efficiency of compile-time parsing to runtime scenarios. It allows developers to parse a composite format string once and reuse the CompositeFormat instance multiple times. This approach significantly enhances performance, especially in scenarios where the format string is dynamic but used repeatedly.

Example Usage:

[MemoryDiagnoser]
public class CompositeFormatVsStringFormat
{
    private static readonly CompositeFormat s_format = CompositeFormat.Parse(SR.CurrentTime);

    [Benchmark(Baseline = true)]
    public string FormatString() => string.Format(null, SR.CurrentTime, DateTime.Now, 10);

    [Benchmark]
    public string FormatComposite() => string.Format(null, s_format, DateTime.Now, 10);
}

internal static class SR
{
    //suppose this was loaded from a resource file
    public static string CurrentTime => "The current time is {0:t}. My age is {1}";
}

MethodMeanErrorStdDevRatioGen0AllocatedAlloc Ratio
FormatString147.2 ns2.16 ns1.69 ns1.000.0181152 B1.00
FormatComposite130.4 ns1.85 ns1.81 ns0.890.0124104 B0.68
Benchmarked on .NET 8

Performance Advantages

Benchmarking the new approach reveals its advantages. A comparison between the traditional FormatString method and the new FormatComposite method shows a notable improvement in execution time and a reduction in memory allocation.

Practical Applications and Best Practices

The CompositeFormat class is particularly beneficial in scenarios where format strings are dynamically generated or fetched at runtime but used repetitively. This could include logging frameworks, localized string handling, or any situation where format strings vary based on runtime conditions. It is essential to balance the initial cost of parsing the composite format string against the performance benefits gained in repeated use.

Conclusion

The introduction of CompositeFormat in .NET 8 is a testament to the ongoing evolution of the .NET framework, continually adapting to the needs of modern software development. By addressing a key limitation in string formatting, CompositeFormat opens new avenues for optimizing performance in .NET applications. As developers, we now have one more powerful tool at our disposal, empowering us to write more efficient and performant code.

Leave a comment

Trending