A Systems-Level Guide to Writing Fast .NET Applications
Introduction
Chapter 1: Why Performance Matters and How to Think About It
- The Hidden Costs of Slow Software
- Throughput Versus Latency: What Are You Optimizing?
- The Performance Stack: From C# to Silicon
- Evidence-Based Optimization: The Scientific Method Applied
- Common Mistakes and False Intuitions
Chapter 2: The .NET Runtime Architecture
- Inside the Execution Engine: CorRuntime and CoreCLR
- The Type System: Types, Metadata, and Runtime Representation
- Assembly Loading and Isolation: AssemblyLoadContext and Performance
- The Execution Pipeline: From Source to Machine Code
- Understanding Runtime Overhead: What the CLR Actually Does
Chapter 3: JIT Compilation and Code Generation
- The RyuJIT Compiler: Architecture and Optimization Pipeline
- On-Demand Compilation: When Methods Get Compiled
- Optimization Passes: From IL to Optimized Machine Code
- Reading Disassembly: Understanding What the JIT Produced
- How Your C# Constructs Translate to Machine Code
Chapter 4: Tiered Compilation and Dynamic Optimization
- ReadyToRun and Precompilation: Reducing JIT Overhead
- Tiered Compilation: JIT Tiering for Startup and Steady-State
- Crossgen2 and Full AOT Compilation
- Profile-Guided Optimization: Using Runtime Data to Improve Code
- Choosing Compilation Modes for Different Scenarios
Chapter 5: Native AOT
- How Native AOT Works: The Compilation Story
- Startup Performance and Memory Footprint Gains
- Feature Limitations: What Native AOT Cannot Do
- Trim Mode and Dependency Analysis
- When to Use Native AOT: Real-World Tradeoffs
Chapter 6: Method Inlining and Devirtualization
- Method Inlining: How It Works and Why It Matters
- Inlining Decisions: Size Limits, Heuristics, and Exceptions
- Devirtualization: Turning Virtual Calls into Direct Calls
- When Inlining and Devirtualization Fail
- Designing Code the JIT Can Optimize Aggressively
Chapter 7: Value Types, Reference Types, and Struct Design
- Stack Versus Heap: Understanding Where Types Live
- Value Type Semantics: Copies, Moves, and Escape Analysis
- Struct Design: When Structs Help and When They Hurt
- Boxing and Unboxing: The Hidden Performance Cost
- Escaping and In-Struct Storage of Value Types
Chapter 8: Object Layout, Memory, and Allocation
- Object Headers and Layout: Understanding Managed Object Structure
- Allocation: The Allocator and the Thread-Local Allocation Buffer
- Padding, Alignment, and Memory Wastage
- Designing Memory-Efficient Types
- Field Ordering and Cache-Friendly Layout
Chapter 9: Boxing, Generics, and Reflection
- Boxing and Unboxing: Detailed Mechanics and Alternatives
- Generic Code Generation: Code Sharing Across Types
- Generic Virtual Calls and Interface Constraints
- Reflection Performance: Costs and Patterns
- Source Generation and Compiled Reflection as Alternatives
Chapter 10: Spans, Memory, and Zero-Allocation Patterns
- Span and Memory: The Abstraction Story
- How Spans Work: Pointers, Handles, and Safety
- Stackalloc and Native Arrays
- Zero-Allocation Parsing and Processing Patterns
- When Zero-Allocation Does Not Help
Chapter 11: Object Pooling and ArrayPool
- Why Object Pooling Works: Avoiding GC Pressure
- ArrayPool: Built-In Array Reuse
- Building Custom Object Pools
- Concurrency and Pooling: Thread Safety Considerations
- When Pooling Makes Things Worse
Chapter 12: Collections and Data Structures Performance
- List and Array: Allocation Patterns and Growth
- Dictionary Performance: Hashing, Collisions, and Load Factors
- Concurrent Collections: Overhead and Alternatives
- LINQ and Allocation-Free Query Patterns
- Choosing the Right Structure for Your Access Pattern
Chapter 13: The Garbage Collector: Generations and Algorithms
- How Generational GC Works: The Theory and Practice
- The Nursery: Short-Lived Object Collection
- Promotion: How Objects Survive and Age
- Old Generation Collection: Compacting Long-Lived Objects
- Tracing Algorithms and Concurrent Marking
Chapter 14: GC Modes: Workstation, Server, Background, and Latency
- Workstation GC Versus Server GC: Fundamental Differences
- Background GC: Reducing Stop-the-World Pauses
- Low-Latency and High-Throughput Modes
- Large Object Heap: Behavior and Pitfalls
- Pinned Object Heap and Pinning Costs
Chapter 15: Finalization, IDisposable, and Cleanup
- The Finalization Queue: How Finalizers Work
- Why Finalizers Are Expensive
- The Dispose Pattern: Managed and Unmanaged Resources
- SuppressFinalize and When to Use It
- Alternatives to Finalization
Chapter 16: GC Tuning, Diagnostics, and Troubleshooting
- GC Configuration Options and Environment Variables
- Reading GC Counters and Metrics
- Diagnosing Excessive Allocations
- LOH Fragmentation: Detection and Remediation
- Tuning GC for Different Workloads
Chapter 17: Async State Machines, Tasks, and ValueTask
- How async and await Work: The Generated State Machine
- Task Allocation and Lifecycle
- ValueTask: Benefits and Pitfalls
- Async Overhead: Measuring and Reducing It
- Async Patterns for High-Performance Scenarios
Chapter 18: Delegates, Closures, and Event Performance
- Delegates: How They Work Under the Hood
- Closures and Captured Variables: The Hidden Allocation
- Multicast Delegates and Event Invocation Performance
- Avoiding Closure Allocations in Hot Paths
- Delegate Performance Patterns
Chapter 19: Thread Pools, Synchronization, and Concurrency
- The Thread Pool: Architecture and Behavior
- Lock Types: Monitor, Mutex, Semaphore, and SpinLock
- Lock Contention: Detection and Mitigation
- Reader-Writer Locks and Partitioning Strategies
- High-Level Concurrency Patterns in .NET
Chapter 20: Lock-Free Programming and Memory Ordering
- Interlocked Operations: Atomic Updates
- Memory Ordering and the Memory Barrier
- Volatile Semantics in C# and .NET
- Building Lock-Free Data Structures
- When Lock-Free Is Not Actually Faster
Chapter 21: I/O, Networking, and Serialization Performance
- Synchronous Versus Asynchronous I/O: Mechanisms and Performance
- Socket and Networking Performance
- Span-Based I/O and Zero-Copy Patterns
- Serialization Formats: Performance Comparison
- Binary Serialization and Protocol Buffers
Chapter 22: SIMD, Vectorization, and Hardware Intrinsics
- SIMD Architecture: Single Instruction Multiple Data
- System.Numerics.Vectors and Portable SIMD
- Hardware Intrinsics: Direct CPU Instruction Access
- AutoSIMD and Compiler Vectorization
- Practical SIMD in .NET: Real Performance Gains
Chapter 23: CPU Architecture, Caches, and Branch Prediction
- CPU Execution Pipelines: How Instructions Really Run
- Branch Prediction: Cost of Wrong Predictions
- CPU Caches: L1, L2, L3, and Cache Lines
- Cache Locality and Data Layout
- False Sharing and Cache Line Contention
Chapter 24: Memory Subsystem, NUMA, and Hardware Considerations
- The Memory Hierarchy: From Registers to Main Memory
- Memory Bandwidth: When It Becomes the Bottleneck
- NUMA: Non-Uniform Memory Access and Awareness
- x64 Versus ARM64: Architectural Differences
- Modern Hardware Effects on .NET Performance
Chapter 25: Benchmarking: Methodology and Tools
- The Scientific Method for Performance Work
- BenchmarkDotNet: Architecture and Features
- Controlling Variables and Isolation
- Statistical Significance and Noise
- Common Benchmarking Mistakes
Chapter 26: Profiling, Tracing, and Diagnostics
- Profiling Approaches: Sampling Versus Instrumentation
- EventPipe and ETW: The Tracing Infrastructure
- PerfView: Deep Runtime Analysis
- dotnet-trace, dotnet-counters, dotnet-dump
- Production Profiling and Diagnostics
Chapter 27: Disassembly Analysis and Flame Graphs
- Reading JIT Disassembly Output
- Understanding Assembly: Registers, Instructions, and Calls
- Generating Flame Graphs from Profiling Data
- Interpreting Flame Graphs: Finding the Real Bottleneck
- Combining Disassembly and Flame Graphs for Analysis
Chapter 28: Production Observability and Performance Monitoring
- Runtime Counters for Production Monitoring
- Tracing in Production: Overhead and Safety
- Metrics and Alerting for Performance Degradation
- Correlating Application and Infrastructure Metrics
- Continuous Performance Regressions and Guardrails
Chapter 29: Case Studies and Real-World Performance Engineering
- Case Study: Optimizing a CPU-Bound Computational Workload
- Case Study: Reducing Allocations in a High-Throughput Service
- Case Study: Tuning for Low Latency in a Trading System
- Case Study: Optimizing Startup and Deployment with Native AOT
- Case Study: Concurrency Optimization in a Database-Backed API