C# Loops — From `for` and `foreach` to CPU Pipelines and LLM‑Ready Code

C# Loops — From `for` and `foreach` to CPU Pipelines and LLM‑Ready Code

Source: Dev.to

C# Loops — From for and foreach to CPU Pipelines and LLM‑Ready Code ## Table of Contents ## 1. Mental Model: A Loop Is a Control‑Flow Machine ## 2. CPU Pipelines & Branch Prediction ## Why loops are special ## What actually hurts ## 3. Roslyn vs JIT — Who Does the Work? ## Roslyn (C# compiler) ## RyuJIT (runtime) ## 4. for, while, do/while — Real Differences ## 5. foreach Under the Hood ## IEnumerable ## 6. Bounds‑Check Elimination (BCE) ## 7. Branch Prediction: Data Beats Code ## 8. Span: Zero‑Allocation Iteration ## 9. Vectorized Loops (SIMD Taste) ## 10. yield return: The Hidden State Machine ## 11. World‑Class Loop Heuristics ## 12. Why This Matters for LLM‑Assisted Code ## Final Thought Most developers use loops every day. Very few truly understand what happens below the syntax. Why does one for loop fly while another crawls? Why can foreach be free… or secretly expensive? Why does the same loop get faster after it runs for a while? And how can understanding loops help you write LLM‑friendly, performance‑predictable code? This article is a mental model upgrade — from beginner syntax to processor‑level reality, modern .NET JIT behavior, and how to reason about loops like a scientist. If you can write for (int i = 0; i < n; i++), you’re ready. • Check • Execute • Jump back At the CPU level, a loop becomes: That back‑edge branch is one of the most optimized patterns in modern CPUs. Loops are not slow. Bad memory access and unpredictable branches are slow. • Execute instructions speculatively • Predict branches before knowing the result • Flush the pipeline on misprediction (~10–20 cycles) Loop back‑edges are extremely predictable: • Taken many times • Not taken once (exit) So the loop branch itself is usually cheap. • Cache misses (100+ cycles) • Pointer chasing • Allocations inside loops • Interface dispatch • Bounds checks that weren’t eliminated 👉 Memory beats syntax every time. • Emits IL • Lowers foreach • Inserts branches • Generates machine code • Removes bounds checks • Hoists invariants • Specializes hot loops • Uses Tiered Compilation + PGO 💡 The same loop may get re‑compiled after warming up. This is why microbenchmarks need warmup. Performance difference is usually noise. Choose based on correctness and readability. ➡ lowered to a for loop ➡ bounds checks often eliminated ➡ very fast • Uses struct enumerator • No allocation • Still very fast ⚠️ Potential performance cliff: • Interface dispatch • Possible allocation • No bounds‑check elimination 👉 Avoid IEnumerable<T> in hot loops. Because the JIT can prove safety. But weird indexing patterns can break BCE. Rule of thumb: • Linear access • Single index • Cached length Two loops. Same code. Different data. • 99% predictable → fast • 50/50 random → slower The branch predictor learns data patterns, not syntax. Sorting data beats rewriting code. • No allocations • Stack‑only • Cache‑friendly • Safe Span is one of the most important performance tools in modern .NET. • Uses SIMD when available • Processes multiple elements per instruction • Great for numeric workloads Data layout matters more than loop shape. • A compiler‑generated state machine • Often a heap allocation • Extra indirections Great for clarity. Avoid in ultra‑hot paths. ✔ Prefer contiguous memory ✔ Avoid allocations inside loops ✔ Avoid interface dispatch in hot paths ✔ Let the JIT eliminate bounds checks ✔ Measure with BenchmarkDotNet ✔ Optimize memory before branches Most performance bugs are memory bugs. • Generate correct syntax • Do not understand cache lines • Do not feel branch misprediction • Do not see GC pressure Your mental model is the safety net. If you understand loops at this level, you can: • Guide LLMs • Review generated code intelligently • Predict performance before profiling • Write code that scales under real load A loop is not a construct. It is a contract between your data, the JIT, and the processor. Once you understand that, you stop guessing — and start engineering. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: L0: compare condition branch if true → L0 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: L0: compare condition branch if true → L0 CODE_BLOCK: L0: compare condition branch if true → L0 CODE_BLOCK: foreach (var x in array) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: foreach (var x in array) CODE_BLOCK: foreach (var x in array) CODE_BLOCK: for (int i = 0; i < arr.Length; i++) sum += arr[i]; Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: for (int i = 0; i < arr.Length; i++) sum += arr[i]; CODE_BLOCK: for (int i = 0; i < arr.Length; i++) sum += arr[i]; CODE_BLOCK: no bounds checks inside the loop Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: no bounds checks inside the loop CODE_BLOCK: no bounds checks inside the loop COMMAND_BLOCK: Span<int> slice = array.AsSpan(1, 3); foreach (ref var x in slice) x++; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: Span<int> slice = array.AsSpan(1, 3); foreach (ref var x in slice) x++; COMMAND_BLOCK: Span<int> slice = array.AsSpan(1, 3); foreach (ref var x in slice) x++; COMMAND_BLOCK: Vector<float> v1, v2; acc += v1 * v2; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: Vector<float> v1, v2; acc += v1 * v2; COMMAND_BLOCK: Vector<float> v1, v2; acc += v1 * v2; COMMAND_BLOCK: IEnumerable<int> Evens() { yield return 2; } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: IEnumerable<int> Evens() { yield return 2; } COMMAND_BLOCK: IEnumerable<int> Evens() { yield return 2; } - The Mental Model: What a Loop Really Is - CPU Pipelines & Branch Prediction - Roslyn vs JIT: Who Optimizes Your Loop - for, while, do/while: What Actually Changes - foreach Under the Hood (Arrays vs List vs IEnumerable) - Bounds‑Check Elimination (The Hidden Superpower) - Branch Prediction: Predictable vs Random Data - Span and Zero‑Allocation Iteration - Vectorized Loops (SIMD Taste) - yield return: The Hidden State Machine - World‑Class Loop Heuristics - Why This Matters for LLM‑Assisted Code