Part 2 - Performance & Concurrency Essentials in C#: Memory, Async, and High-Performance Primitives
Source: Dev.to
Excerpt
This post covers the C# performance essentials for enterprise backends: memory semantics (stack vs heap, value vs reference, GC), async/await with cancellation, streaming via IAsyncEnumerable, concurrency primitives, Span, pooling, and pitfalls with boxing and large structs.
Memory model: Stack vs Heap, Value vs Reference, GC
Diagram: Memory at a glance
+---------------------------+ +--------------------------------+
| Stack | | Heap |
|---------------------------| |--------------------------------|
| int x = 42 | | new User() { Name = "Alice" } |
| Point p (struct inline) | ---> | reference held on the stack |
| local frames, by-value | | objects, arrays, strings |
+---------------------------+ +--------------------------------+
Essentials
- Value types (
struct,record struct) are copied by value and often stored inline; cheap for tiny models. - Reference types (
class,record) live on the heap; managed by the GC.
Best practices
- Reduce boxing/unboxing (e.g., avoid storing
structinobject; prefer generic collections). - Use
StringBuilderfor heavy concatenation; preferArrayPoolandSpan/Memoryfor parsing and allocations.
Boxing pitfall
struct Counter { public int Value; }
object o = new Counter { Value = 5 }; // boxing (allocates)
Counter c = (Counter)o; // unboxing (copy)
ArrayPool & Span for parsing
using System.Buffers;
var pool = ArrayPool.Shared;
byte[] buffer = pool.Rent(4096);
try
{
// Fill buffer, then process without extra allocations
var span = new Span(buffer, 0, 4096);
// parse span...
}
finally
{
pool.Return(buffer);
}
GC awareness
- Allocation spikes can trigger GC; keep hot paths allocation‑light.
- Prefer struct enumerators and
readonly structfor tight loops when practical.
Async/await & cancellation, IAsyncEnumerable, concurrency primitives
Diagram: Async request flow
sequenceDiagram
participant Client
participant Controller
participant Service
participant ExternalAPI
Client->>Controller: HTTP GET /items
Controller->>Service: GetItemsAsync(ct)
Service->>ExternalAPI: GET /items (ct)
ExternalAPI-->>Service: 200 OK (streamed)
Service-->>Controller: IAsyncEnumerable
Controller-->>Client: Chunked JSON response
Guidelines
async/awaitis for I/O‑bound work; for CPU‑bound tasks useTask.Runcarefully.- Always pass a
CancellationToken; prefer timeouts and propagate cancellation across layers. - For streaming, use
IAsyncEnumerable; avoid buffering large sets in memory.
Cancellation & timeout
public async Task FetchAsync(HttpClient http, string url, CancellationToken ct)
{
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromSeconds(5));
return await http.GetStringAsync(url, cts.Token).ConfigureAwait(false);
}
Streaming results
public async IAsyncEnumerable GetNumbersAsync([EnumeratorCancellation] CancellationToken ct = default)
{
for (int i = 0; i ();
}
_ = Task.Run(async () =>
{
await foreach (var item in channel.Reader.ReadAllAsync(ct))
{
// process item
}
});
await channel.Writer.WriteAsync("work-item", ct);
channel.Writer.Complete();
Library code tip
- In libraries, prefer
ConfigureAwait(false)to avoid deadlocks in legacy sync contexts.
Span, pooling, boxing, large struct caveats
Span notes
Span is a stack‑only ref struct for high‑performance slicing—no heap allocations.
- Cannot cross
awaitor be captured in lambdas; useMemory/ReadOnlyMemoryfor async boundaries.
ReadOnlySpan<char> span = "1234".AsSpan();
int total = 0;
foreach (var ch in span) total += (ch - '0');
Large struct caveats
Large structs incur copy cost when passed/returned; prefer classes or use in parameters for read‑only refs.
public readonly struct BigValue
{
public readonly int A, B, C, D, E, F, G, H;
}
int Sum(in BigValue v) => v.A + v.B + v.C + v.D + v.E + v.F + v.G + v.H;
Pooling
- Prefer
ArrayPoolor object pools in hot paths to reduce GC pressure.
CTA
Next up: Part 3 — Production‑Ready Practices (resource management, testing/diagnostics, logging/telemetry, security & reliability). Share your performance tips in the comments!