C# Conditionals Mental Model — From `if (x > 0)` to LLM‑Ready Decisions
Source: Dev.to
Mental Model: Conditionals Are Control‑Flow Decisions
At the hardware level a conditional consists of:
- A compare instruction.
- Followed by either
- a branch (jump), or
- a conditional select / move.
There is no “if” instruction in silicon; the CPU only knows “jump to another instruction or keep going”.
Compiler vs JIT: Who Decides What Runs
Roslyn (C# compiler)
- Emits IL (
brtrue,brfalse,switch, …). - Makes no CPU‑specific decisions.
RyuJIT (runtime)
- Emits x64 / ARM64 machine code.
- Chooses between branches, jump tables, and conditional moves.
- Uses tiered compilation and profile‑guided optimization (PGO).
The same C# source can produce different machine code depending on runtime profiling.
CPU Branch Prediction (The Hidden Cost)
Modern CPUs predict the outcome of a branch:
- ✅ Correct prediction → pipeline stays full.
- ❌ Misprediction → pipeline flush (10–20+ cycles).
Data predictability matters more than clever syntax.
| Data pattern | Predictor accuracy |
|---|---|
| Mostly true | Very high |
| Mostly false | Very high |
| Random | Very poor |
if / else Lowering Patterns
Branched version (assembly)
cmp x, 0
jle ELSE
add sum, x
jmp END
Branchless version (assembly)
cmp x, 0
cmovle x, -x
add sum, x
The JIT picks the form based on hotness, instruction count, and predictability. Simplify expressions rather than trying to force a specific pattern.
switch vs switch expressions
switch statement
- Dense values → jump table.
- Sparse values → chain of compares.
switch expression
Often lowered to a decision DAG, which works well with pattern matching:
var label = value switch
{
"Negative" => "Negative",
0 => "Zero",
> 0 => "Positive"
};
Readable and optimizable.
Pattern Matching = Declarative Decision Trees
Pattern matching lets the compiler build structured decision graphs:
- Relational patterns (
> 0, …) - Type patterns (
is MyType t) - Property patterns (
{ Length: > 5 })
Benefits
- Better inlining.
- Fewer redundant checks.
- Clear intent – useful for both humans and LLMs.
Branchless Logic (Use Carefully)
Prefer branchless code when:
- The branch outcome is unpredictable.
- Both paths are tiny.
- Inside a hot loop.
Example (branchless absolute value):
int abs = Math.Abs(x);
Let the JIT decide; manual bit‑twiddling should be a last resort.
Expert Heuristics
- ✅ Prefer guard clauses for early exits.
- ✅ Optimize data layout before adding branches.
- ✅ Use
switchfor closed sets of values. - ✅ Use dictionaries for extensible mappings.
- ✅ Measure with BenchmarkDotNet.
- ❌ Don’t micro‑optimize cold code.
Why This Matters for LLMs
LLMs operate as probabilistic decision engines. Code with clear decision boundaries:
- Minimizes ambiguous branching.
- Encodes intent declaratively.
Understanding conditionals helps you:
- Write cleaner APIs.
- Design better agent routing.
- Chunk logic into predictable flows.
- Reduce hallucination risk by providing explicit structure.
Clear control flow → clearer mental models → better LLM output.
Production Checklist
- Guard clauses for early exits.
- Switch expressions for domain decisions.
- Avoid unpredictable branches in hot paths.
- Benchmark before optimizing.
- Prefer readability unless proven hot.
- Understand branch cost vs memory cost.
Final Thought
Conditionals aren’t about if; they’re about predictability, intent, and control flow—for CPUs and for LLMs. Master these concepts and level up as a systems‑thinking developer.
Happy branching. 🌿