[API Proposal]: `ArmBase.DataMemoryBarrier`
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Background and motivation
On xarch we expose multiple memory barrier/fence APIs already: `Sse.StoreFence()`, `Sse2.LoadFence()`, `Sse2.MemoryFence()`; however on arm we do not expose the underlying `dmb` instruction anywhere (only 2 variants via `Thread.MemoryBarrier()`/`Volatile.WriteBarrier()` and recently with `Volatile.ReadBarrier()`).
This proposal proposes exposing that underlying instruction, matching what we're doing on xarch.
The main benefit of this proposal is exposing `dmb ishst` which acts as a "weak" write barrier (where the normal `Volatile.WriteBarrier()` would be a "strong" write barrier), in that it only requires ordering between stores, as opposed to `Volatile.WriteBarrier()` which also requires that reads before the barrier are ordered before writes after the barrier.
For example:
```csharp
Read 1;
Write 2;
Volatile.WriteBarrier();
Read 3;
Write 4;
```
only allows the access 3 to pass before the barrier effectively (we require ordering of all memory accesses before the barrier with all writes after the barrier). To achieve this on arm, we need a full memory barrier `dmb ish`, which is not optimal if all that is required is ordering the writes.
If only writes need to be ordered, then using `ArmBase.DataMemoryBarrier(ArmMemoryBarrier.ISHST)` would be optimal - this allows the reads to be ordered freely still (which means it cannot be used to implement `Volatile.WriteBarrier`).
To use the terminology from https://github.com/dotnet/runtime/issues/98837, `ArmBase.DataMemoryBarrier(ArmMemoryBarrier.ISHST)` would act as a `Write-Write` barrier (that is, it orders writes before the barrier before writes after the barrier), whereas `Volatile.WriteBarrier()` acts as a `ReadWrite-Write` barrier (that is, it orders memory accesses before the barrier before writes after the barrier).
/cc @jkotas @VSadov @tannergooding
(I would be happy to implement if approved also (I am wanting for .NET 12 ideally), like I did with the volatile barriers API.)
### API Proposal
```csharp
namespace System.Runtime.Intrinsics.Arm;
public static class ArmBase
{
// If called with invalid barrier kind bits for the platform, can just treat as SY (full)
public static void DataMemoryBarrier([ConstantExpected(Min = 0, Max = 15)] ArmMemoryBarrier barrierKind);
}
public enum ArmMemoryBarrier
{
OSHLD = 0b0001, // Loads in outer sharable domain (arm64 only)
OSHST = 0b0010, // Stores in outer sharable domain
OSH = 0b0011, // Outer sharable domain
NSHLD = 0b0101, // Loads to point of unification (arm64 only)
NSHST = 0b0110, // Stores to point of unification
NSH = 0b0111, // To point of unification
ISHLD = 0b1001, // Loads in inner sharable domain (arm64 only)
ISHST = 0b1010, // Stores in inner sharable domain
ISH = 0b1011, // Inner sharable domain
LD = 0b1101, // Loads (arm64 only)
ST = 0b1110, // Stores
SY = 0b1111, // Full
}
```
### API Usage
The example given in the original weak volatile APIs proposal still works with weak write barrier: https://github.com/dotnet/runtime/issues/98837. This would provide a performance improvement to that case, just like expsosing the read barrier originally does.
Example usage would basically be the same as other volatile barrier APIs, except that it uses an enum to control the mode rather than different methods, e.g.:
```csharp
ArmBase.DataMemoryBarrier(ArmMemoryBarrier.ISHST);
```
(Please lmk if I need to provide further usage examples beyond the linked one, or if I should copy and paste the example usage from the other issue into here but using the new API, etc.)
### Alternative Designs
- We could have two different enums for arm32 and arm64.
- We could just expose a cross platform weak volatile write barrier API.
- We could name the enum values differently, either just capitalising different (e.g., `IshSt` vs `ISHST`), or we could make more descriptive names (but imo this might just make it harder to map the instruction one wants back to the name).
- We could just take in `byte` rather than the enum, which seems to be fairly common for `ConstantExpected`.
### Risks
Having constants exposed that are only supported on some platforms; however this seems better than having multiple enums, or omitting the values, to me.
Otherwise, there's not really any additional risk here compared to any of our other low-level threading APIs like `Volatile`.
Contributor guide
Research direction
Start by reviewing the existing ArmBase and volatile barrier APIs, then compare the proposed ArmMemoryBarrier values with the xarch fence APIs and linked issue 98837. Done means the API shape, platform-specific enum behavior, and usage semantics are approved and the corresponding implementation and validation locations are identified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100