SlideShare a Scribd company logo
www.xedotnet.org
Mirco Vanini
@MircoVanini
Span<T>
Optimising code using Span<T>,
Memory<T>, ReadOnlySpan<T>, …
Agenda
• Back to Basics
• The problem
• What is Span<T>?
• What is Memory<T>?
20/09/2019 2
Back to Basics
• .NET is a managed platform, that means the memory access
and management is safe and automatic. All types are fully
managed by .NET, it allocates memory either on the
execution stacks, or managed heaps.
20/09/2019 3
Back to Basics
• .NET is a managed platform, that means the memory access
and management is safe and automatic. All types are fully
managed by .NET, it allocates memory either on the
execution stacks, or managed heaps.
• In .NET world, there are three types of memory:
• Managed heap memory, such as an array;
• Stack memory, such as objects created by stackalloc;
• Native memory, such as a native pointer reference.
20/09/2019 3
Back to Basics
20/09/2019 4
Reference Types
• class keyword
• Allocated on heap
• A variable for a reference type is a
reference to the thing on the heap
not the object
• Passed around by reference
Assignment is a copy of the
reference,
Value Types
• struct keyword
• Allocated on stack or embedded
into a reference object
• A variable for a value type is the
value itself, e.g. integer
• Passed around by value (i.e.
copied)
• Assignment is a copy of the whole
value
Back to Basics
20/09/2019 5
Staff on the Stack
Back to Basics
20/09/2019 5
Staff on the Stack
Staff on the Managed Heap
Back to Basics
In C# by default Value Types are passed to
methods by value
20/09/2019 6
Back to Basics
In C# all parameters are passed to methods
by value by default
20/09/2019 6
DEMO
20/09/2019 7
ref returns and ref locals
The problem
• In many applications, the most CPU consuming operations
are string operations. If you run a profiler session against
your application, you may find the fact that 95% of the CPU
time is used to call string and related functions.
20/09/2019 8
The problem
• In many applications, the most CPU consuming operations
are string operations. If you run a profiler session against
your application, you may find the fact that 95% of the CPU
time is used to call string and related functions.
• Trim() or SubString() returns a new string object that is part of the original string,
this is unnecessary if there is a way to slice and return a portion of the original
string to save one copy.
20/09/2019 8
The problem
• In many applications, the most CPU consuming operations
are string operations. If you run a profiler session against
your application, you may find the fact that 95% of the CPU
time is used to call string and related functions.
• Trim() or SubString() returns a new string object that is part of the original string,
this is unnecessary if there is a way to slice and return a portion of the original
string to save one copy.
• IsNullOrWhiteSpace() takes a string object that needs a memory copy (because
string is immutable.)
20/09/2019 8
The problem
• In many applications, the most CPU consuming operations
are string operations. If you run a profiler session against
your application, you may find the fact that 95% of the CPU
time is used to call string and related functions.
• Trim() or SubString() returns a new string object that is part of the original string,
this is unnecessary if there is a way to slice and return a portion of the original
string to save one copy.
• IsNullOrWhiteSpace() takes a string object that needs a memory copy (because
string is immutable.)
• Specifically, string concatenation is expensive, it takes n string objects,
makes n copy, generate n - 1 temporary string objects, and return a final string
object, the n – 1 copies can be eliminated if there is a way to get direct access to
the return string memory and perform sequential writes.
20/09/2019 8
GC Managed Heap (Workstation mode)
• Mark
• Starting from the roots, mark all reachable objects as alive
• In Background
• Sweep
• Turning all non-reachable objects into a free space
• Blocking
• Compact
• To prevent fragmentation
• Not always
• Blocking
20/09/2019 9
GC Managed Heap (Workstation mode)
• Mark
• Starting from the roots, mark all reachable objects as alive
• In Background
• Sweep
• Turning all non-reachable objects into a free space
• Blocking
• Compact
• To prevent fragmentation
• Not always
• Blocking
20/09/2019 9
GC Managed Heap (Workstation mode)
• Mark
• Starting from the roots, mark all reachable objects as alive
• In Background
• Sweep
• Turning all non-reachable objects into a free space
• Blocking
• Compact
• To prevent fragmentation
• Not always
• Blocking
20/09/2019 9
GC Managed Heap (Workstation mode)
• Mark
• Starting from the roots, mark all reachable objects as alive
• In Background
• Sweep
• Turning all non-reachable objects into a free space
• Blocking
• Compact
• To prevent fragmentation
• Not always
• Blocking
20/09/2019 9
GC Managed Heap (Workstation mode)
• Mark
• Starting from the roots, mark all reachable objects as alive
• In Background
• Sweep
• Turning all non-reachable objects into a free space
• Blocking
• Compact
• To prevent fragmentation
• Not always
• Blocking
20/09/2019 9
Middle Ground between Server and Workstation GC by Maoni
Running with Server GC in a Small Container Scenario Part 0 by Maoni
Reference Semantics with Value Types (C# 7.2)
• inparameters
• Pass value type by reference. Called method cannot modify it
• ref locals and ref returns (C# 7.0)
• ref readonly returns
• Return a read only value type by reference
• readonly struct
• Immutable value types
• ref struct
• Stack only value types
20/09/2019 10
Write safe and efficient C# code
What is Span<T>?
20/09/2019 11
“System.Span<T> is a new value type at the
heart of .NET [that] enables the representation
of contiguous regions of arbitrary memory.”
“Span<T> is a small, but critical, building block
for a much larger effort to provide .NET APIs to
enable development of high scalability server
applications.”
Stephen Toub, MSDN Magazine, January 2018
dotnet/corefxlab
What is Span<T>?
20/09/2019 12
“System.Span<T> is a new value type at the
heart of .NET [that] enables the representation
of contiguous regions of arbitrary memory.”
What is Span<T>?
20/09/2019 12
“System.Span<T> is a new value type at the
heart of .NET [that] enables the representation
of contiguous regions of arbitrary memory.”
• Struct
• Part of System.Memory, available on Nuget
• .NET Standard 1.1 so can be used in .NET 4.5+
• Slow Span (.NET Standard)
• Fast Span (.NET Core)
• C# 7.2-ish .NET API Catalog
What is Span<T>?
20/09/2019 13
“System.Span<T> is a new value type at the
heart of .NET [that] enables the representation
of contiguous regions of arbitrary memory.”
What is Span<T>?
20/09/2019 13
“System.Span<T> is a new value type at the
heart of .NET [that] enables the representation
of contiguous regions of arbitrary memory.”
• High performance O(1), low (no) overhead
• Framework, CLR, JIT and GC support
• Provides memory and type safety
• Avoids the need for unsafe code
• Value Type
Span design document
What is Span<T>?
20/09/2019 14
“System.Span<T> is a new value type at the
heart of .NET [that] enables the representation
of contiguous regions of arbitrary memory.”
What is Span<T>?
20/09/2019 14
“System.Span<T> is a new value type at the
heart of .NET [that] enables the representation
of contiguous regions of arbitrary memory.”
• Native/unmanaged memory (P/Invoke)
• Managed memory (.NET types)
• Stack memory (stackalloc)
Span<byte> stackMemory = stackalloc byte[256]; // C# 7.2
IntPtr unmanagedHandle = Marshal.AllocHGlobal(256);
Span<byte> unmanaged = new Span<byte>(unmanagedHandle.ToPointer(), 256);
char[] array = new char[] {'i','m','p','l','i','c','i','t’ };
Span<char> fromArray = array; // implicit cast
ReadOnlySpan<char> fromString = "Spanification".AsSpan();
What is Span<T>?
20/09/2019 15
0x01
871
Stack
871
YGG871
Heap
0x01
0x02
0x03
0x04
0x05
0x06plate
num
string plate = “YGG871”;
plate = plate.Substring(3);
int num = int.Parse(plate);
What is Span<T>?
20/09/2019 15
0x01
871
Stack
871
YGG871
Heap
0x01
0x02
0x03
0x04
0x05
0x06
num
0x02
string plate = “YGG871”;
plate = plate.Substring(3);
int num = int.Parse(plate);
plate
span
What is Span<T>?
20/09/2019 16
0x01
0x01
0->3
6
871
Stack
871
YGG871
Heap
0x01
0x02
0x03
0x04
0x05
0x06plate
num
length
offset
pointer
string plate = “YGG871”;
ReadOnlySpan<char> s = plate.AsSpan();
s = s.Slice(3);
int num = int.Parse(s);
.NET Framework 4.5+ - slow span
span
What is Span<T>?
20/09/2019 16
0x01
0x01
0->3
6
871
Stack
871
YGG871
Heap
0x01
0x02
0x03
0x04
0x05
0x06plate
num
length
offset
pointer
string plate = “YGG871”;
ReadOnlySpan<char> s = plate.AsSpan();
s = s.Slice(3);
int num = int.Parse(s);
span
0x01+3
6->3length
pointer
.NET Framework 4.5+ - slow span.NET Core 2.0+ – fast span
DEMO
20/09/2019 17
Span<T>
* Legends *
ItemCount : Value of the 'ItemCount' parameter
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Ratio : Mean of the ratio distribution ([Current]/[Baseline])
RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline])
Rank : Relative position of current benchmark mean among all benchmarks (Arabic style)
Gen 0 : GC Generation 0 collects per 1000 operations
Gen 1 : GC Generation 1 collects per 1000 operations
Gen 2 : GC Generation 2 collects per 1000 operations
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
1 ms : 1 Millisecond (0.001 sec)
Benchmark
20/09/2019 18
| Method | Mean | Error | StdDev | Ratio | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------------------- |----------:|----------:|----------:|------:|-----:|-------:|------:|------:|----------:|
| GetLastNameWithSpan | 16.61 ns | 0.0222 ns | 0.0185 ns | 0.09 | 1 | - | - | - | - |
| GetLastNameUsingSubstring | 35.86 ns | 0.3395 ns | 0.3010 ns | 0.20 | 2 | 0.0127 | - | - | 40 B |
| GetLastName | 180.47 ns | 0.9540 ns | 0.8924 ns | 1.00 | 3 | 0.0458 | - | - | 144 B |
| Method | CharactersCount | Mean | Error | StdDev | Ratio | RatioSD | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------- |---------------- |-----------:|-----------:|-----------:|-------:|--------:|-----:|-------:|------:|------:|----------:|
| Slice | 10 | 5.090 ns | 0.0138 ns | 0.0108 ns | 1.00 | 0.00 | 1 | - | - | - | - |
| Substring | 10 | 12.858 ns | 0.1095 ns | 0.0971 ns | 2.53 | 0.02 | 2 | 0.0102 | - | - | 32 B |
| | | | | | | | | | | | |
| Slice | 10000 | 5.078 ns | 0.0084 ns | 0.0070 ns | 1.00 | 0.00 | 1 | - | - | - | - |
| Substring | 10000 | 699.687 ns | 13.8632 ns | 22.3865 ns | 137.44 | 3.65 | 2 | 3.1843 | - | - | 10024 B |
BenchmarkDotNet=v0.11.5, OS=macOS Mojave 10.14.6 (18G95) [Darwin 18.7.0]
Intel Core i7-4980HQ CPU 2.80GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview7-012821
[Host] : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT
DefaultJob : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT3
| Method | ItemCount | Mean | Error | StdDev | Ratio | RatioSD | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------------- |---------- |---------:|----------:|----------:|------:|--------:|-----:|---------:|---------:|---------:|----------:|
| StringParseSum | 10 | NA | NA | NA | ? | ? | ? | - | - | - | - |
| StringParseSumWithSpan | 10 | NA | NA | NA | ? | ? | ? | - | - | - | - |
| | | | | | | | | | | | |
| StringParseSumWithSpan | 100000 | 2.799 ms | 0.0363 ms | 0.0322 ms | 0.32 | 0.01 | 1 | - | - | - | - |
| StringParseSum | 100000 | 8.742 ms | 0.1738 ms | 0.3666 ms | 1.00 | 0.00 | 2 | 671.8750 | 453.1250 | 218.7500 | 4139057 B |
When do mere mortals use it?
20/09/2019 19
var str = "EGOR 3.14 1234 7/3/2018";
string name = str.Substring(0, 4);
float pi = float.Parse(str.Substring(5, 4));
int number = int.Parse(str.Substring(10, 4));
DateTime date = DateTime.Parse(str.Substring(15, 8));
When do mere mortals use it?
20/09/2019 19
var str = "EGOR 3.14 1234 7/3/2018";
string name = str.Substring(0, 4);
float pi = float.Parse(str.Substring(5, 4));
int number = int.Parse(str.Substring(10, 4));
DateTime date = DateTime.Parse(str.Substring(15, 8));
Allocated on heap: 168 bytes
var str = "EGOR 3.14 1234 7/3/2018".AsSpan();
string name = str.Slice(0, 4);
float pi = float.Parse(str.Slice(5, 4));
int number = int.Parse(str.Slice(10, 4));
DateTime date = DateTime.Parse(str.Slice(15, 8));
Allocated on heap: 0 bytes
Span - The Limitations
20/09/2019 20
• Can only live on the stack
• Implemented as a ref struct
• Can only be contained by ref structs
ref structs
Structs that can exist only on the stack. New in C# 7.2
• Can’t implement interfaces
• Can’t be used as generic type arguments
• Can’t be boxed to object
• Can’t be passed in to - or used in places inside - of async
methods, iterators, nested functions or query expressions
Memory<T>
20/09/2019 21
• “Normal” struct
• Not as performant as Span
• Can be used in more places than Span (ie, doesn’t have the
limitations)
• Use the Span property to access the underlying memory
async Task DoSomethingAsync(Span<byte> buffer) // <-- error
{
buffer[0] = 0;
await Something(); // Bang!
buffer[0] = 1;
}
async Task DoSomethingAsync(Memory<byte> buffer)
{
buffer.Span[0] = 0;
await Something(); // Totally fine
buffer.Span[0] = 1;
}
DEMO
20/09/2019 22
Memory<T>
System.Buffers Namespace
20/09/2019 23
Recap
20/09/2019 24
• Span<T> makes it easy and safe to use any
kind of memory
• System.Memory package, C# 7.2
• Memory<T> has no stack-only limitations
• Don’t copy memory! Slice it!
• Prefer read-only versions over mutable ones
• Prefer safe managed memory over native
memory
Recap
Performance is a Feature!
20/09/2019 24
20/09/2019 25
Who I am
www.proxsoft.it
info@proxsoft.it
@MircoVanini
Mirco Vanini
Microsoft® MVP Windows Development
AllSeen Alliance - AllJoyn® Ambassador
Open Connectivity Foundation - OCF® Ambassador
Is C# a low-level language?
What language features of C#/F#/VB.NET or
BCL / Runtime functionality enable ‘low-level’
programming ?
20/09/2019 26
Is C# a low-level language?
What language features of C#/F#/VB.NET or
BCL / Runtime functionality enable ‘low-level’
programming ?
20/09/2019 26
Any C# developer is going to have a different
idea of what ‘low-level’ means, these features
would be taken for granted by C++ or Rust
programmers.
Is C# a low-level language?
• ref returns and ref locals: pass and return by reference to avoid
large struct copying. It can be even faster than unsafe ! link
• Unsafe code in .NET: is the part of the program that runs outside
the control of the Common Language Runtime of the .NET
frameworks. link
• Managed pointers in .NET: It could be defined as a more general
type of reference, which may point to other locations than just the
beginning of an object. link
• Span<T> and universal memory management: this presentation…
link
• Interoperability: interoperability with unmanaged code through
platform invoke services, C/C++ interoperability, and COM
interoperability (COM interop). link
20/09/2019 27

More Related Content

Optimising code using Span<T>

  • 1. www.xedotnet.org Mirco Vanini @MircoVanini Span<T> Optimising code using Span<T>, Memory<T>, ReadOnlySpan<T>, …
  • 2. Agenda • Back to Basics • The problem • What is Span<T>? • What is Memory<T>? 20/09/2019 2
  • 3. Back to Basics • .NET is a managed platform, that means the memory access and management is safe and automatic. All types are fully managed by .NET, it allocates memory either on the execution stacks, or managed heaps. 20/09/2019 3
  • 4. Back to Basics • .NET is a managed platform, that means the memory access and management is safe and automatic. All types are fully managed by .NET, it allocates memory either on the execution stacks, or managed heaps. • In .NET world, there are three types of memory: • Managed heap memory, such as an array; • Stack memory, such as objects created by stackalloc; • Native memory, such as a native pointer reference. 20/09/2019 3
  • 5. Back to Basics 20/09/2019 4 Reference Types • class keyword • Allocated on heap • A variable for a reference type is a reference to the thing on the heap not the object • Passed around by reference Assignment is a copy of the reference, Value Types • struct keyword • Allocated on stack or embedded into a reference object • A variable for a value type is the value itself, e.g. integer • Passed around by value (i.e. copied) • Assignment is a copy of the whole value
  • 6. Back to Basics 20/09/2019 5 Staff on the Stack
  • 7. Back to Basics 20/09/2019 5 Staff on the Stack Staff on the Managed Heap
  • 8. Back to Basics In C# by default Value Types are passed to methods by value 20/09/2019 6
  • 9. Back to Basics In C# all parameters are passed to methods by value by default 20/09/2019 6
  • 11. The problem • In many applications, the most CPU consuming operations are string operations. If you run a profiler session against your application, you may find the fact that 95% of the CPU time is used to call string and related functions. 20/09/2019 8
  • 12. The problem • In many applications, the most CPU consuming operations are string operations. If you run a profiler session against your application, you may find the fact that 95% of the CPU time is used to call string and related functions. • Trim() or SubString() returns a new string object that is part of the original string, this is unnecessary if there is a way to slice and return a portion of the original string to save one copy. 20/09/2019 8
  • 13. The problem • In many applications, the most CPU consuming operations are string operations. If you run a profiler session against your application, you may find the fact that 95% of the CPU time is used to call string and related functions. • Trim() or SubString() returns a new string object that is part of the original string, this is unnecessary if there is a way to slice and return a portion of the original string to save one copy. • IsNullOrWhiteSpace() takes a string object that needs a memory copy (because string is immutable.) 20/09/2019 8
  • 14. The problem • In many applications, the most CPU consuming operations are string operations. If you run a profiler session against your application, you may find the fact that 95% of the CPU time is used to call string and related functions. • Trim() or SubString() returns a new string object that is part of the original string, this is unnecessary if there is a way to slice and return a portion of the original string to save one copy. • IsNullOrWhiteSpace() takes a string object that needs a memory copy (because string is immutable.) • Specifically, string concatenation is expensive, it takes n string objects, makes n copy, generate n - 1 temporary string objects, and return a final string object, the n – 1 copies can be eliminated if there is a way to get direct access to the return string memory and perform sequential writes. 20/09/2019 8
  • 15. GC Managed Heap (Workstation mode) • Mark • Starting from the roots, mark all reachable objects as alive • In Background • Sweep • Turning all non-reachable objects into a free space • Blocking • Compact • To prevent fragmentation • Not always • Blocking 20/09/2019 9
  • 16. GC Managed Heap (Workstation mode) • Mark • Starting from the roots, mark all reachable objects as alive • In Background • Sweep • Turning all non-reachable objects into a free space • Blocking • Compact • To prevent fragmentation • Not always • Blocking 20/09/2019 9
  • 17. GC Managed Heap (Workstation mode) • Mark • Starting from the roots, mark all reachable objects as alive • In Background • Sweep • Turning all non-reachable objects into a free space • Blocking • Compact • To prevent fragmentation • Not always • Blocking 20/09/2019 9
  • 18. GC Managed Heap (Workstation mode) • Mark • Starting from the roots, mark all reachable objects as alive • In Background • Sweep • Turning all non-reachable objects into a free space • Blocking • Compact • To prevent fragmentation • Not always • Blocking 20/09/2019 9
  • 19. GC Managed Heap (Workstation mode) • Mark • Starting from the roots, mark all reachable objects as alive • In Background • Sweep • Turning all non-reachable objects into a free space • Blocking • Compact • To prevent fragmentation • Not always • Blocking 20/09/2019 9 Middle Ground between Server and Workstation GC by Maoni Running with Server GC in a Small Container Scenario Part 0 by Maoni
  • 20. Reference Semantics with Value Types (C# 7.2) • inparameters • Pass value type by reference. Called method cannot modify it • ref locals and ref returns (C# 7.0) • ref readonly returns • Return a read only value type by reference • readonly struct • Immutable value types • ref struct • Stack only value types 20/09/2019 10 Write safe and efficient C# code
  • 21. What is Span<T>? 20/09/2019 11 “System.Span<T> is a new value type at the heart of .NET [that] enables the representation of contiguous regions of arbitrary memory.” “Span<T> is a small, but critical, building block for a much larger effort to provide .NET APIs to enable development of high scalability server applications.” Stephen Toub, MSDN Magazine, January 2018 dotnet/corefxlab
  • 22. What is Span<T>? 20/09/2019 12 “System.Span<T> is a new value type at the heart of .NET [that] enables the representation of contiguous regions of arbitrary memory.”
  • 23. What is Span<T>? 20/09/2019 12 “System.Span<T> is a new value type at the heart of .NET [that] enables the representation of contiguous regions of arbitrary memory.” • Struct • Part of System.Memory, available on Nuget • .NET Standard 1.1 so can be used in .NET 4.5+ • Slow Span (.NET Standard) • Fast Span (.NET Core) • C# 7.2-ish .NET API Catalog
  • 24. What is Span<T>? 20/09/2019 13 “System.Span<T> is a new value type at the heart of .NET [that] enables the representation of contiguous regions of arbitrary memory.”
  • 25. What is Span<T>? 20/09/2019 13 “System.Span<T> is a new value type at the heart of .NET [that] enables the representation of contiguous regions of arbitrary memory.” • High performance O(1), low (no) overhead • Framework, CLR, JIT and GC support • Provides memory and type safety • Avoids the need for unsafe code • Value Type Span design document
  • 26. What is Span<T>? 20/09/2019 14 “System.Span<T> is a new value type at the heart of .NET [that] enables the representation of contiguous regions of arbitrary memory.”
  • 27. What is Span<T>? 20/09/2019 14 “System.Span<T> is a new value type at the heart of .NET [that] enables the representation of contiguous regions of arbitrary memory.” • Native/unmanaged memory (P/Invoke) • Managed memory (.NET types) • Stack memory (stackalloc) Span<byte> stackMemory = stackalloc byte[256]; // C# 7.2 IntPtr unmanagedHandle = Marshal.AllocHGlobal(256); Span<byte> unmanaged = new Span<byte>(unmanagedHandle.ToPointer(), 256); char[] array = new char[] {'i','m','p','l','i','c','i','t’ }; Span<char> fromArray = array; // implicit cast ReadOnlySpan<char> fromString = "Spanification".AsSpan();
  • 28. What is Span<T>? 20/09/2019 15 0x01 871 Stack 871 YGG871 Heap 0x01 0x02 0x03 0x04 0x05 0x06plate num string plate = “YGG871”; plate = plate.Substring(3); int num = int.Parse(plate);
  • 29. What is Span<T>? 20/09/2019 15 0x01 871 Stack 871 YGG871 Heap 0x01 0x02 0x03 0x04 0x05 0x06 num 0x02 string plate = “YGG871”; plate = plate.Substring(3); int num = int.Parse(plate); plate
  • 30. span What is Span<T>? 20/09/2019 16 0x01 0x01 0->3 6 871 Stack 871 YGG871 Heap 0x01 0x02 0x03 0x04 0x05 0x06plate num length offset pointer string plate = “YGG871”; ReadOnlySpan<char> s = plate.AsSpan(); s = s.Slice(3); int num = int.Parse(s); .NET Framework 4.5+ - slow span
  • 31. span What is Span<T>? 20/09/2019 16 0x01 0x01 0->3 6 871 Stack 871 YGG871 Heap 0x01 0x02 0x03 0x04 0x05 0x06plate num length offset pointer string plate = “YGG871”; ReadOnlySpan<char> s = plate.AsSpan(); s = s.Slice(3); int num = int.Parse(s); span 0x01+3 6->3length pointer .NET Framework 4.5+ - slow span.NET Core 2.0+ – fast span
  • 32. DEMO 20/09/2019 17 Span<T> * Legends * ItemCount : Value of the 'ItemCount' parameter Mean : Arithmetic mean of all measurements Error : Half of 99.9% confidence interval StdDev : Standard deviation of all measurements Ratio : Mean of the ratio distribution ([Current]/[Baseline]) RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline]) Rank : Relative position of current benchmark mean among all benchmarks (Arabic style) Gen 0 : GC Generation 0 collects per 1000 operations Gen 1 : GC Generation 1 collects per 1000 operations Gen 2 : GC Generation 2 collects per 1000 operations Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 ms : 1 Millisecond (0.001 sec)
  • 33. Benchmark 20/09/2019 18 | Method | Mean | Error | StdDev | Ratio | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated | |-------------------------- |----------:|----------:|----------:|------:|-----:|-------:|------:|------:|----------:| | GetLastNameWithSpan | 16.61 ns | 0.0222 ns | 0.0185 ns | 0.09 | 1 | - | - | - | - | | GetLastNameUsingSubstring | 35.86 ns | 0.3395 ns | 0.3010 ns | 0.20 | 2 | 0.0127 | - | - | 40 B | | GetLastName | 180.47 ns | 0.9540 ns | 0.8924 ns | 1.00 | 3 | 0.0458 | - | - | 144 B | | Method | CharactersCount | Mean | Error | StdDev | Ratio | RatioSD | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated | |---------- |---------------- |-----------:|-----------:|-----------:|-------:|--------:|-----:|-------:|------:|------:|----------:| | Slice | 10 | 5.090 ns | 0.0138 ns | 0.0108 ns | 1.00 | 0.00 | 1 | - | - | - | - | | Substring | 10 | 12.858 ns | 0.1095 ns | 0.0971 ns | 2.53 | 0.02 | 2 | 0.0102 | - | - | 32 B | | | | | | | | | | | | | | | Slice | 10000 | 5.078 ns | 0.0084 ns | 0.0070 ns | 1.00 | 0.00 | 1 | - | - | - | - | | Substring | 10000 | 699.687 ns | 13.8632 ns | 22.3865 ns | 137.44 | 3.65 | 2 | 3.1843 | - | - | 10024 B | BenchmarkDotNet=v0.11.5, OS=macOS Mojave 10.14.6 (18G95) [Darwin 18.7.0] Intel Core i7-4980HQ CPU 2.80GHz (Haswell), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=3.0.100-preview7-012821 [Host] : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT DefaultJob : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT3 | Method | ItemCount | Mean | Error | StdDev | Ratio | RatioSD | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated | |----------------------- |---------- |---------:|----------:|----------:|------:|--------:|-----:|---------:|---------:|---------:|----------:| | StringParseSum | 10 | NA | NA | NA | ? | ? | ? | - | - | - | - | | StringParseSumWithSpan | 10 | NA | NA | NA | ? | ? | ? | - | - | - | - | | | | | | | | | | | | | | | StringParseSumWithSpan | 100000 | 2.799 ms | 0.0363 ms | 0.0322 ms | 0.32 | 0.01 | 1 | - | - | - | - | | StringParseSum | 100000 | 8.742 ms | 0.1738 ms | 0.3666 ms | 1.00 | 0.00 | 2 | 671.8750 | 453.1250 | 218.7500 | 4139057 B |
  • 34. When do mere mortals use it? 20/09/2019 19 var str = "EGOR 3.14 1234 7/3/2018"; string name = str.Substring(0, 4); float pi = float.Parse(str.Substring(5, 4)); int number = int.Parse(str.Substring(10, 4)); DateTime date = DateTime.Parse(str.Substring(15, 8));
  • 35. When do mere mortals use it? 20/09/2019 19 var str = "EGOR 3.14 1234 7/3/2018"; string name = str.Substring(0, 4); float pi = float.Parse(str.Substring(5, 4)); int number = int.Parse(str.Substring(10, 4)); DateTime date = DateTime.Parse(str.Substring(15, 8)); Allocated on heap: 168 bytes var str = "EGOR 3.14 1234 7/3/2018".AsSpan(); string name = str.Slice(0, 4); float pi = float.Parse(str.Slice(5, 4)); int number = int.Parse(str.Slice(10, 4)); DateTime date = DateTime.Parse(str.Slice(15, 8)); Allocated on heap: 0 bytes
  • 36. Span - The Limitations 20/09/2019 20 • Can only live on the stack • Implemented as a ref struct • Can only be contained by ref structs ref structs Structs that can exist only on the stack. New in C# 7.2 • Can’t implement interfaces • Can’t be used as generic type arguments • Can’t be boxed to object • Can’t be passed in to - or used in places inside - of async methods, iterators, nested functions or query expressions
  • 37. Memory<T> 20/09/2019 21 • “Normal” struct • Not as performant as Span • Can be used in more places than Span (ie, doesn’t have the limitations) • Use the Span property to access the underlying memory async Task DoSomethingAsync(Span<byte> buffer) // <-- error { buffer[0] = 0; await Something(); // Bang! buffer[0] = 1; } async Task DoSomethingAsync(Memory<byte> buffer) { buffer.Span[0] = 0; await Something(); // Totally fine buffer.Span[0] = 1; }
  • 40. Recap 20/09/2019 24 • Span<T> makes it easy and safe to use any kind of memory • System.Memory package, C# 7.2 • Memory<T> has no stack-only limitations • Don’t copy memory! Slice it! • Prefer read-only versions over mutable ones • Prefer safe managed memory over native memory
  • 41. Recap Performance is a Feature! 20/09/2019 24
  • 42. 20/09/2019 25 Who I am www.proxsoft.it info@proxsoft.it @MircoVanini Mirco Vanini Microsoft® MVP Windows Development AllSeen Alliance - AllJoyn® Ambassador Open Connectivity Foundation - OCF® Ambassador
  • 43. Is C# a low-level language? What language features of C#/F#/VB.NET or BCL / Runtime functionality enable ‘low-level’ programming ? 20/09/2019 26
  • 44. Is C# a low-level language? What language features of C#/F#/VB.NET or BCL / Runtime functionality enable ‘low-level’ programming ? 20/09/2019 26 Any C# developer is going to have a different idea of what ‘low-level’ means, these features would be taken for granted by C++ or Rust programmers.
  • 45. Is C# a low-level language? • ref returns and ref locals: pass and return by reference to avoid large struct copying. It can be even faster than unsafe ! link • Unsafe code in .NET: is the part of the program that runs outside the control of the Common Language Runtime of the .NET frameworks. link • Managed pointers in .NET: It could be defined as a more general type of reference, which may point to other locations than just the beginning of an object. link • Span<T> and universal memory management: this presentation… link • Interoperability: interoperability with unmanaged code through platform invoke services, C/C++ interoperability, and COM interoperability (COM interop). link 20/09/2019 27