When are x86 LFENCE, SFENCE and MFENCE instructions required?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
On x86, explicit fence instructions are needed less often than many developers think because the architecture already provides a relatively strong memory-ordering model for normal cached memory. The right answer depends on what kind of ordering you need: loads, stores, or both. In portable application code, you usually should not write raw fence instructions yourself and should rely on language atomics or compiler intrinsics instead.
The Short Mental Model
For normal write-back memory on x86 TSO, the architecture already preserves:
- load-load order
- load-store order
- store-store order
The main reordering visible to other cores is store-load, due to store buffering.
That is why x86 often needs fewer explicit barriers than weaker architectures such as ARM.
What the Three Fence Instructions Mean
At a high level:
- '
LFENCEorders prior loads before later loads' - '
SFENCEorders prior stores before later stores' - '
MFENCEis a full memory fence for both loads and stores'
In intrinsics form on MSVC or similar toolchains:
The question is not what they do in isolation, but when they are actually required.
When LFENCE Is Required
LFENCE is mostly relevant when you need to ensure that earlier loads are complete before later operations proceed, especially in low-level timing or speculation-sensitive code.
Typical uses:
- serializing load-dependent timing sequences
- ordering around instructions such as
RDTSCin some measurement patterns - mitigation patterns related to speculation control in low-level code
Example timing pattern:
For ordinary lock-protected application code, LFENCE is rarely something you insert manually.
When SFENCE Is Required
SFENCE matters most when stores are not already strongly ordered by the normal memory type behavior you rely on.
Typical cases:
- non-temporal or streaming stores
- write-combining memory regions
- device or low-level systems code where store visibility order matters explicitly
Example with streaming stores:
Without the fence, later code may observe the streaming-store sequence as incomplete at a point where you expected it to be globally visible.
When MFENCE Is Required
MFENCE is a full barrier and is the broadest tool of the three. It is used when you need both load and store ordering and cannot rely on another synchronizing instruction.
Typical cases:
- very low-level lock-free code written directly in assembly or intrinsics
- ordering around non-temporal memory operations
- special memory-type interactions outside normal application code
Example:
In practice, if you are asking whether to insert raw MFENCE in ordinary C or C++ multi-threaded code, the answer is usually no: use atomics instead.
Why Atomics Usually Matter More Than Raw Fences
In C++, C#, Java, and Rust, you normally express synchronization through language-level atomic or lock primitives. The compiler and runtime then generate the right machine instructions for the target architecture.
For example, in C++:
On x86, acquire and release semantics are often satisfied without explicit fence instructions because the architecture is already strong enough for those operations on normal memory.
That is a feature, not a weakness. It means you should write portable synchronization semantics and let the toolchain map them correctly.
Locked Instructions Already Imply Ordering
Many atomic read-modify-write instructions on x86 are generated as locked operations, and those already provide strong ordering. That is another reason manual fences are often redundant in high-level code.
If your code already uses:
- mutexes
- atomics
- compare-and-swap
- lock-prefixed instructions
then adding raw fences manually is often unnecessary or wrong.
Cases Where Manual Fences Are Usually Not Needed
For ordinary application code on x86, you usually do not need LFENCE, SFENCE, or MFENCE just to:
- protect shared data with a mutex
- use normal acquire-release atomics
- publish simple state transitions in standard lock-free patterns
If you think you need them there, re-check whether the real problem is missing atomic semantics or a data race.
Common Pitfalls
One common mistake is assuming that every memory-ordering problem should be solved with a raw fence instruction. In high-level code, atomics are usually the correct tool.
Another mistake is ignoring memory type. Fences become much more relevant with streaming stores or non-standard memory regions.
Developers also mix compiler barriers and CPU fences as if they were the same thing. They are not.
Finally, many explanations oversimplify x86 as "strong enough so fences never matter." They do matter in low-level and non-temporal cases, just less often than on weaker architectures.
Summary
- x86 already provides strong ordering for normal cached memory under TSO.
- '
LFENCEis mainly for load ordering and timing or speculation-sensitive cases.' - '
SFENCEis mainly for ordering stores, especially streaming or weakly ordered stores.' - '
MFENCEis a full barrier but is rarely needed directly in ordinary application code.' - In portable code, prefer language atomics and let the compiler generate the right instructions.
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.