1


Performance comparison, AsSpan() vs Substring()




Date Added (UTC):

08 May 2024 @ 15:17

Date Updated (UTC):

08 May 2024 @ 15:17


.NET Version(s):

.NET 8

Tag(s):

#StringManipulation #Strings


Added By:
Profile Image

Blog   
Nigeria    
.NET developer|Systems engineer|Databases(SQL)

Benchmark Results:





Benchmark Code:



 [SimpleJob(RuntimeMoniker.Net80)]
 [MemoryDiagnoser]
 public class PerformanceCheck
 {
     [Benchmark]
     public ReadOnlySpan<char> AsSpan()
     {
         string text = "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32.";

         return text.AsSpan(15, 150);
     }

     [Benchmark(Baseline = true)]
     public string SubString()
     {
         string text = "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32.";

         return text.Substring(15, 150);
     }
 }

// .NET 8
public ReadOnlySpan<char> AsSpan()
{
    return MemoryExtensions.AsSpan("Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32.", 15, 150);
}
// .NET 8
public string SubString()
{
    return "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32.".Substring(15, 150);
}

// .NET 8
.method public hidebysig 
    instance valuetype [System.Runtime]System.ReadOnlySpan`1<char> AsSpan () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 10 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2050
    // Code size 18 (0x12)
    .maxstack 8

    // sequence point: (line 19, col 10) to (line 19, col 794) in _
    IL_0000: ldstr "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32."
    // sequence point: (line 21, col 10) to (line 21, col 38) in _
    IL_0005: ldc.i4.s 15
    IL_0007: ldc.i4 150
    IL_000c: call valuetype [System.Runtime]System.ReadOnlySpan`1<char> [System.Memory]System.MemoryExtensions::AsSpan(string, int32, int32)
    IL_0011: ret
}
// .NET 8
.method public hidebysig 
    instance string SubString () cil managed 
{
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
        01 00 01 00 00
    )
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 18 00 00 00 01 5f 01 00 54 02 08 42 61 73
        65 6c 69 6e 65 01
    )
    // Method begins at RVA 0x2063
    // Code size 18 (0x12)
    .maxstack 8

    // sequence point: (line 27, col 10) to (line 27, col 794) in _
    IL_0000: ldstr "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32."
    // sequence point: (line 29, col 10) to (line 29, col 41) in _
    IL_0005: ldc.i4.s 15
    IL_0007: ldc.i4 150
    IL_000c: callvirt instance string [System.Runtime]System.String::Substring(int32, int32)
    IL_0011: ret
}

// .NET 8 (X64)
AsSpan()
    L0000: mov rax, 0x169cf4654c8
    L000a: mov rax, [rax]
    L000d: add rax, 0x2a
    L0011: mov [rdx], rax
    L0014: mov dword ptr [rdx+8], 0x96
    L001b: mov rax, rdx
    L001e: ret
// .NET 8 (X64)
SubString()
    L0000: push rbx
    L0001: sub rsp, 0x20
    L0005: mov ecx, 0x96
    L000a: call System.String.FastAllocateString(Int32)
    L000f: mov rbx, rax
    L0012: cmp [rbx], bl
    L0014: lea rcx, [rbx+0xc]
    L0018: mov rdx, 0x169cf4654c8
    L0022: mov rdx, [rdx]
    L0025: add rdx, 0x2a
    L0029: mov r8d, 0x12c
    L002f: call qword ptr [0x7ff987ac5b78]
    L0035: mov rax, rbx
    L0038: add rsp, 0x20
    L003c: pop rbx
    L003d: ret


Benchmark Description:


AsSpan() is much faster than Substring. AsSpan requires no or less memory allocation than Substring.

The benchmark setup provided is designed to measure and compare the performance of two different methods for extracting a substring from a larger string in C#. The benchmarks are configured to run on the .NET 8.0 runtime, as indicated by the `[SimpleJob(RuntimeMoniker.Net80)]` attribute. This ensures that the benchmarks are executed using the latest optimizations and runtime features available in .NET 8.0. The `[MemoryDiagnoser]` attribute is used to enable the collection and reporting of memory allocation statistics, which is crucial for understanding the memory efficiency of the compared methods. ### Benchmark Methods Overview #### 1. `AsSpan()` Method **Rationale and Performance Aspect:** The `AsSpan()` method is designed to test the performance of creating a `ReadOnlySpan<char>` from a substring of a larger string without actually allocating a new string for the substring. This method uses the `AsSpan` extension method on a string, which provides a type-safe, memory-efficient view of the characters in the string. The rationale behind testing this method is to evaluate how efficiently one can perform substring operations without the overhead of additional memory allocations associated with creating a new string. **What It Measures:** This benchmark measures the time it takes to create a `ReadOnlySpan<char>` representing a portion of the original string and the memory efficiency of this operation. It's important because it reflects the performance characteristics of substring operations in scenarios where memory allocation overhead is critical, such as in high-performance or memory-constrained environments. **Expected Results/Insights:** Running this benchmark should provide insights into how much faster and memory-efficient `ReadOnlySpan<char>` is compared to traditional substring methods that involve allocations. Lower memory usage and faster execution times are expected outcomes due to the avoidance of heap allocations. #### 2. `SubString()` Method (Baseline) **Rationale and Performance Aspect:** The `SubString()` method serves as the baseline for comparison and is designed to test the traditional approach of extracting a substring, which involves creating a new string object that contains a portion of the original string. This method uses the `Substring` method of the string class, which allocates memory for the new string. The rationale behind including this method is to have a baseline that represents the common but more memory-intensive way of handling substrings. **What It Measures:** This benchmark measures the time and memory allocations involved in extracting a substring using the `Substring` method. It's an important measure because it reflects the cost of substring operations in terms of both execution time and memory usage in a typical .NET application. **Expected Results/Insights:** From running this benchmark, one should expect to see how much additional memory is allocated and possibly a slower execution time compared to the `AsSpan` method. This method serves as a reference point to understand the benefits of using `ReadOnlySpan<char>` for substring operations in terms of performance and memory efficiency. ### Conclusion By comparing the results of these two benchmarks, developers can make informed decisions about which method to use for substring operations based on their specific performance and memory allocation requirements. The `AsSpan` method is anticipated to be more efficient, especially in scenarios where minimizing memory usage is crucial, while the `SubString` method provides a straightforward but potentially more costly approach in terms of memory.


Benchmark Comments: