Introduction
In the realm of multi-threaded programming, ensuring data consistency and thread safety is paramount. To address this challenge, .NET 8 introduces Frozen Collections, immutable data structures designed to provide thread-safe and efficient handling of collections in concurrent environments. These frozen collections offer developers a powerful toolset for building robust and reliable applications. Let's delve into the world of Frozen Collections in .NET 8 and explore their key features, benefits, and usage scenarios.
Frozen Collections is a new .NET 8 feature that can be used to create Dictionaries and Sets for faster read operations when you don’t need to make changes after the creation. In this article, I present how to work with these collections and demonstrate the performance difference when compared with other collections.
The new System.Collections.Frozen namespace includes the collection types FrozenDictionary<TKey,TValue> and FrozenSet<T>. These types are optimized for fast lookup operations. They take a bit more time during the creation, but the read operations are faster when compared with a Dictionary or a Set.
Syntax
To create a FrozenDictionary, you can use the ToFrozenDictionary method:
To create a FrozenSet, you can use the ToFrozenSet method:
Benchmark
For demonstration purposes, I created a series of methods that do three different operations for different collection types: Create the collection, execute the TryGetValue method, and execute a Lookup operation and to run the benchmark, I used the BenchmarkDotNet package.
To execute the benchmarks, you need to set the Visual Studio to Release and execute the project or use the following commands via the command line (When running via the command line, the results will be stored in a folder named BenchmarkDotNet.Artifacts):
In the benchmark result, you can see the following information:
- Method: the name of the benchmarked method.
- Mean: Arithmetic mean of all measurements.
- Error: Half of 99.9% confidence interval.
- StdDev: Standard deviation of all measurements. A lower standard deviation indicates more consistent results.
- Rank: the rank performance position (from fastest to slowest).
For the benchmark results, smaller values are better.
Create benchmark
In the class below, you can find the methods that create a series of collection types:
This is the benchmark result:
The creation of a FrozenDictionary and a FrozenSet are slower when compared with the creation of other collections, but as we are going to see in the next methods, the reading operation will be faster.
TryGetValue benchmark:
In the class below, you can find a series of collection types being initialized as private properties with 100.000 items. Each class method executes the TryGetValue method, searching for the key 500:
This is the benchmark result:
Executing the TryGetValue in a FrozenDictionary and in a FrozenSet were the fastest operations when compared with these other collections.
Lookup Benchmark
In the class below, you can find a series of collection types being initialized as private properties with 100.000 items. On each class method, there is a loop (using a for), and for each iteration, the method ContainsKey is executed:
This is the benchmark result:
As expected, the lookup in a FrozendDictionary and in a FrozenSet were the fastest when compared with the other collections.
Benefits of Frozen Collections:
- Thread Safety: Frozen Collections are inherently thread-safe, making them ideal for use in multi-threaded applications. Since their contents cannot be modified after creation, there is no risk of data corruption or race conditions due to concurrent access.
- Predictable Behavior: Immutable collections exhibit predictable behavior, as their contents remain constant throughout their lifetime. This predictability simplifies reasoning about program behavior and reduces the likelihood of bugs related to mutable state.
- Efficiency: Despite their immutability, Frozen Collections are designed to be efficient in terms of memory usage and performance. They leverage efficient internal data structures and algorithms to provide fast read access and minimal memory overhead.
- Functional Programming: Frozen Collections align with the principles of functional programming, emphasizing immutability and side-effect-free operations. This makes them well-suited for functional programming paradigms and immutable data modeling.
Usage Scenarios
- Configuration Data: Frozen Collections are ideal for storing configuration data that remains constant throughout the application's execution. Since configuration data is typically read-only, using immutable collections ensures its integrity and thread safety.
- Caching: Immutable collections are well-suited for caching scenarios where the cached data does not change frequently. By using Frozen Collections for caching, developers can eliminate the need for synchronization mechanisms and ensure consistent access to cached data across multiple threads.
- Functional Programming: Functional programming techniques, such as pure functions and immutable data structures, are gaining popularity in .NET development. Frozen Collections provide a natural fit for functional programming paradigms, enabling developers to write concise and predictable code.
Conclusion
Frozen Collections are collections optimized for situations where you have collections that will be frequently accessed, and you do not need to change the keys and values after creating. These collections are a bit slower during the creation, but reading operations are faster.