Systems Architecture End-to-End Decision Matrices
Master comprehensive architectural decision frameworks for data storage, polymorphism dispatch strategies, and multi-threaded synchronization.
Architectural excellence in systems engineering comes from making deliberate trade-offs rather than defaulting to generic patterns.
This lesson consolidates the entire course into three definitive Systems Architecture Decision Matrices covering memory allocation, polymorphism, and concurrency.
Definitive criteria for memory allocation, method dispatch, and concurrency synchronization.
1. Matrix 1: Data Storage & Allocation Strategies#
Choosing where and how data resides in hardware determines memory latency, cache efficiency, and thread scalability:
| Strategy | Ownership Model | Heap Alloc? | Allocation Latency | Pointer Footprint | Hardware Overhead | Recommended Use Case |
|---|---|---|---|---|---|---|
| Stack Allocation | Unique | No | (Stack pointer register shift) | bytes (Stored inline) | Zero overhead | Fixed-size local data ( KB) with lexical frame lifespan. |
Box<T> | Unique | Yes | Allocator search latency | bytes on stack | Zero heap metadata | Dynamically sized types, recursive structures, or transfers outliving the stack. |
Rc<T> | Shared (Single-Thread) | Yes | Allocator search latency | bytes on stack | 16-byte heap header (Strong + Weak counters) | Complex acyclic graphs, tree structures, or single-threaded shared caches. |
Arc<T> | Shared (Multi-Thread) | Yes | Allocator search latency | bytes on stack | 16-byte atomic heap header | Thread-safe data sharing across concurrent OS worker threads. |
2. Matrix 2: Polymorphism & Method Resolution Strategies#
Rust separates compile-time static dispatch from runtime dynamic dispatch:
| Strategy | Syntax | Method Resolution | Call Overhead | Binary Footprint | Optimizer Inlining | Collection Homogeneity |
|---|---|---|---|---|---|---|
| Static Dispatch | <T: Trait>, impl Trait | Compile-time Monomorphization | indirection (Direct CPU call) | Larger (Code bloat per concrete type) | Full inlining and SIMD auto-vectorization | Homogeneous only (all elements must be identical type) |
| Dynamic Dispatch | dyn Trait, Box<dyn Trait> | Runtime Vtable Jump Table | 1 indirection (Indirect hardware jump) | Minimal (Single machine code implementation) | Cannot inline through dynamic vtable boundary | Heterogeneous (can store diverse types in one collection) |
3. Matrix 3: Concurrency & Synchronization Primitives#
Selecting the proper synchronization mechanism prevents deadlocks while maximizing multi-core CPU scaling:
| Primitive | Access Permissions | Contention Behavior | Synchronization Invariant | Primary Use Case |
|---|---|---|---|---|
Atomics (AtomicUsize, AtomicBool) | Shared lock-free access | Non-blocking hardware execution | Hardware memory fences (Relaxed to SeqCst) | Flags, metrics, progress counters, and lock-free rings. |
std::sync::Mutex<T> | Exclusive access (Single reader OR writer) | Blocks OS thread via kernel futex | RAII MutexGuard, lock poisoning on panic | Short critical sections with balanced or write-heavy workloads. |
std::sync::RwLock<T> | Multiple concurrent readers OR single exclusive writer | Blocks writers during active reads; blocks readers during active write | Shared read locks, exclusive write locks | Read-heavy, write-seldom workloads (e.g. routing tables, configuration caches). |
Channels (mpsc, crossbeam) | Ownership transfer across threads | Decoupled thread queues | Message passing (Actor model) | Pipeline processing, task distribution, and event-driven architectures. |
tokio::sync::Mutex<T> | Asynchronous exclusive access | Yields worker thread cooperatively | Safe to hold across .await points | Coordinating shared state that requires network or disk I/O while locked. |
4. Concept Verification#
Test your mastery of systems architecture decision matrices:
Under what conditions is Box<T> superior to Arc<T>?
Why is static dispatch preferred over dynamic dispatch in computational inner loops?