c# string array span

string.Create

Matt 2021/11/05 17:45:29
1260

Span<T>

What is Span? Span is a value type that enables us to work and manage any type that represents a contiguous chunk of memory. Meaning, it provides type-safe access to a contiguous area of memory.

Span<T> is struct, it will NOT cause heap allocation. It's very useful for performance.

The difference between heap and stack.

\ HEAP STACK
Memory Allocated in random block Allocated in continuous block
Allocate and Deallocate Manually Automatically
Cost More, access Slower Less, access Faster
Thread safe Nope Yes, it is
Flexibility Dynamic length, can be resized Fixed-length
Data structure Hierarchical Linear

String.Create Code Demo

Creates a new string with a specific length and initializes it after creation by using the specified callback. said from docs.

In another words, String.Create will help us to operate a string in a fancy way.

Now, that's take an easy example. We'll generate a random string by using for loop, a very basic try out.

    public class StringMethods
    {
        private char[] _charsPool = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890".ToCharArray();
        private Random _rnd = new();
        private const int N = 20;

        public string NormalStringAddForLoop()
        {
            var ret = "";
            for (int i = 0; i < N; i++)
            {
                ret += _charsPool[_rnd.Next(_charsPool.Length)];
            }
            return ret;
        }

        public string ForLoopWithStringBuilder()
        {
            var ret = new StringBuilder();
            for (var i = 0; i < N; i++)
            {
                ret.Append(_charsPool, _rnd.Next(_charsPool.Length), 1);
            }
            return ret.ToString();
        }

        public string ForLoopWithStringBuilderInFixedLength()
        {
            var ret = new StringBuilder(N);
            for (var i = 0; i < N; i++)
            {
                ret.Append(_charsPool, _rnd.Next(_charsPool.Length), 1);
            }
            return ret.ToString();
        }

        public string ForLoopWithStringCreate()
        {
            return string.Create(N, _charsPool, (ret, _) =>
            {
                for (var i = 0; i < N; i++)
                {
                    ret[i] = _charsPool[_rnd.Next(_charsPool.Length)];
                }
            });
        }
    }
  • First, as the code said, NormalStringAddForLoop, a basic way to join string, piece by piece.

  • Next, ForLoopWithStringBuilder, a better way to join string with dynamic length StringBuilder, while in loop.

  • The next, ForLoopWithStringBuilderInFixedLength, using a fixed-length StringBuilder will be more effective than dynamic one.

  • Last one, ForLoopWithStringCreate, the way that will catch your eyes, a very effective way than others.

For the goal purpose, we will run some diagnostic by using BenchmarkDotNet to exam functions we created.

To measure the functions, the project need to compile on RELEASE mode.

dotnet run -c RELEASE

Here are results run from two different machines. Take a look at these beautiful magic numbers.

Intel Core i7-4500U CPU 1.80GHz (Haswell), 1 CPU, 4 logical and 2 physical cores, full-power worked

img "string.Create.vaio.png"

the complete result shown blow.

Intel Core i7-10875H CPU 2.30GHz, 1 CPU, 16 logical and 8 physical cores, worked under 70% performance, default profile

img "string.Create.razer.png"

the complete result shown blow.

Legends

Mean : Arithmetic mean of all measurements

Allocated: Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)

A higher performance machine will run more quickly than a lower one, and, the StringCreate method always had a greater performance, besides, the Allocated memory always has a very small size.

That told us one thing important, if possible, using StringCreate to deal with array of string (or any other objects), would let our app run in better performance.

Here is why.

Explain Why

Span<T>, in this case it was Span<string>

Span<string>

img "span_string.gif"

A String.Create will directly work with each part of its value, without creating new a new instance (one-word string, or char). About that, our application's performance could be enhanced, and wasted less memory usage.

Conclusion

Above all, we could use this powerful API to make:

  • A string that had known length, such as an Serial Order String

  • A string that would be generated as an Unique Id (maybe same as 1.)

  • A string that need to deal or formatted with each character

Choose correct way to make our works smarter.

Enjoy it.


Background image is from unsplash


References:

String.Create Method

Span<T> Struct

StringBuilder

Deep Dive on .NET Core String.Create Performance

Matt