dotnet / dotnet/runtime

[API Proposal]: MemoryMarshal.GetAlignmentOffset for safe SIMD loop peeling

Open
#131,325 12 comments 2 reactions 0 assignees View on GitHub
api-suggestion area-System.Runtime.InteropServices
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Background and motivation

Vectorizing a loop over a GC-allocated array is often ~15% slower (up to 30% in some cases) than it should be: the GC only guarantees 8-byte alignment, so `Vector512` loads cross a cache line every iteration and pay a misalignment penalty. Occasionally the GC hands back a 64-byte-aligned buffer, which also makes benchmarks very unstable.

```csharp
[MethodImpl(MethodImplOptions.NoInlining)]
public static int SumSafe(Span span)
{
int sum = 0;
if (Vector128.IsHardwareAccelerated)
{
var acc512 = Vector512.Zero;
while (span.Length > Vector512.Count)
{
acc512 += Vector512.Create(span);
span = span.Slice(Vector512.Count);
}
sum = Vector512.Sum(acc512);
}
foreach (int value in span)
sum += value;
return sum;
}
```

### API Proposal

```diff
namespace System.Runtime.InteropServices
{
public static partial class MemoryMarshal
{
+ // Number of leading elements to skip so the rest is `alignment`-byte aligned.
+ // Returns [0, span.Length]. Best-effort for non-pinned memory. `alignment` must be pow2.
+ public static int GetOpportunisticAlignmentOffset(ReadOnlySpan span, nuint alignment);
+ public static int GetOpportunisticAlignmentOffset(Span span, nuint alignment);
}
}
```

### API Usage

```csharp
int offset = MemoryMarshal.GetAlignmentOffset(span, 64);
for (int i = 0; i < offset; i++) // scalar prologue
sum += span[i];
span = span.Slice(offset); // aligned body follows
```

### Alternative Designs

* Move to `MemoryExtensions` or `Unsafe`?
* Add `Opportunistic` into the API name? to imply the GC issue.

### Risks

- Opportunistic only: GC may relocate the buffer, so it's a hint. Same caveat as the existing internal helper. Presumably, it shouldn't be an issue for 100% safe code.
- What if input is Span of misaligned elements - should we return 0?

Contributor guide

Open the contributing guide

Research direction

No implementation file or test is named. Start by reviewing the proposed MemoryMarshal overloads, the alternative API locations, and the existing internal alignment helper mentioned in the risks; done means resolving the API name, placement, alignment semantics, and relocation caveat.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend-api-design, performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.