Overview

9 Exploiting concurrency

This chapter explains how exploiting concurrency is a primary lever for reducing latency, especially in systems that perform long-running or I/O-bound work. It distinguishes concurrency (many tasks making progress) from parallelism (tasks executing simultaneously), and uses Little’s Law to show that keeping throughput high at high concurrency is essential to avoid latency inflation from resource contention. Practical motivation comes from services like REST APIs, where overlapping I/O and computation prevents slow operations from stalling overall responsiveness.

The chapter surveys concurrency models and their trade-offs for low latency. Threads are widely supported but incur kernel-level context-switch overheads and cache thrashing; they shine for parallelism but can be costly for fine-grained concurrency. User-space options such as fibers (cooperative scheduling) and coroutines (async/await with futures/promises) reduce blocking and overhead, while event-driven designs revolve around a non-blocking event loop. The actor model isolates state and coordinates via asynchronous message passing, simplifying shared-state hazards. Beyond concurrency, the chapter covers parallel processing strategies: data parallelism (partitioning and locality, SIMD/GPUs) and task parallelism (independent operations, processor affinity and NUMA awareness), emphasizing that careful partitioning and placement boost throughput while acknowledging coordination and Amdahl-law limits.

The chapter then examines the consistency–concurrency trade-off in transactional systems. It contrasts serializability and strict serializability with snapshot isolation (via MVCC), highlighting anomalies like write skew and the implications of weaker isolation levels (e.g., dirty/fuzzy reads and lost updates). It outlines pessimistic control with two-phase locking versus optimistic MVCC, noting MVCC’s concurrency benefits and storage/GC costs. A micro-benchmark ties the themes together: running independent 10 ms tasks sequentially versus concurrently shows an order-of-magnitude latency reduction, underscoring the core guidance—choose an appropriate concurrency model, apply data and task parallelism thoughtfully, and select isolation/concurrency control that meets correctness needs without sacrificing latency.

Concurrency vs parallelism. Concurrent execution (on the left) interleaves thread execution on a single core, which provides an illusion of work happening at the same time because all threads make some progress quickly. In contrast, parallel execution (on the right) executes the threads simultaneously on different cores, providing true parallel execution.
The OS runs multiple threads at the same time (but not simultaneously) on a single CPU core by context switching between them. In this example, every thread is run for exactly 4 milliseconds at a time and then the OS switches to another thread. In real-world systems, a thread might block to wait for I/O, for example, causing the OS to switch to another thread more quickly.
Event loop is the heart of event-based concurrency. The event loop polls for events from the OS and invokes appropriate event handlers. For example, if a packet arrives from the network, the event loop polls for them using an I/O polling API such as io_uring, epoll, or kqueue, and then hands over the received messages to event handlers. When the event loop polling is done, the event loop calls itself to continue polling.
Summing values of an array sequentially means stepping through each element at a time. However, with data parallelism, you can reduce latency by summing subsets of the array in parallel.
An example of key-value store with two serializable transaction that update the database. The first transaction updates objects A and B, which are both atomically committed. The second transaction updates object A twice with only the second update being visible to other transactions.
Example of multi-version concurrency control (MVCC) across two transactions. The steps are the same as in Figure 9.5 on serializability. However, in the second transaction, we see how the database maintains multiple versions of the objects, which allows transactions to execute optimistically.

Summary

  • Concurrency models enable multiple tasks to execute simultaneously to reduce latency, though true parallelism specifically refers to performing tasks simultaneously across multiple processors - each approach requires different design considerations for optimal performance.
  • Threading provides a widely-used concurrency model but comes with kernel-level context switching overhead, while coroutines offer lightweight user-space managed concurrency through suspendable execution, and event-driven architectures handle concurrency through event loops and non-blocking operations.
  • Parallel execution can reduce your application latency in contrast to sequential execution because you can potentially complete more work in less time. Data parallelism improves performance by executing identical operations simultaneously across different data portions (especially effective with SIMD and GPUs), while task parallelism runs different independent tasks simultaneously - these complementary approaches can be combined for maximum performance gains.
  • Transaction isolation levels represent different tradeoffs between consistency guarantees and performance, from read uncommitted to serializable, with snapshot isolation providing a practical middle ground through multi-version concurrency control (MVCC).
  • Concurrency control mechanisms like MVCC allow databases to execute transactions concurrently while maintaining consistency, helping reduce latency compared to serial execution while preserving critical correctness guarantees for applications.

FAQ

What is the difference between concurrency and parallelism, and why does it matter for latency?Concurrency is a system’s ability to make progress on multiple tasks at the same time (often by interleaving work), while parallelism runs tasks simultaneously on multiple cores. Concurrency keeps applications responsive during I/O or long operations; parallelism reduces wall-clock time by using more compute units. Both are key to lowering latency.
How does Little’s Law relate concurrency, throughput, and latency?Little’s Law states C = T × L and L = C / T, where C is concurrency, T is throughput, and L is average latency. If throughput stays constant, increasing concurrency tends to increase latency due to contention. Your concurrency model must sustain high throughput at high concurrency to keep latency low.
When should I use a thread-based model, and what are its latency pitfalls?Threads map directly to OS primitives and are simple conceptually, good for parallel execution across cores. For low latency, pitfalls include synchronization overhead, complex fine-grained locking, and costly kernel-managed context switches (including cache thrashing). Many high-performance systems use kernel threads for parallelism and handle concurrency in user space.
What are fibers and when do they help?Fibers are user-space threads with cooperative scheduling. Context switches are cheaper (no kernel crossing), and critical sections can run without preemption. Caveats: a non-yielding (buggy) fiber can starve others, and mapping fibers to multiple CPUs is harder, which can lead to imbalance. They work well when you control tasks and cooperative scheduling fits.
How do coroutines help reduce latency?Coroutines are resumable functions that suspend and resume without blocking a thread. They’re lightweight, language-managed, and ideal for I/O-bound or long-running work: while one coroutine waits, others can run. This keeps throughput high with minimal resources compared to many threads.
How does event-driven concurrency achieve high concurrency with minimal threads?An event loop waits for events (for example via epoll, io_uring, or kqueue) and dispatches non-blocking handlers. Handlers must not block; they either finish quickly or defer back to the loop. This model excels for I/O-bound workloads, enabling thousands of concurrent operations with low overhead.
What are futures and promises, and what are their downsides for low-latency systems?Functions return a future immediately instead of blocking; a producer fulfills the paired promise when the result is ready. Callers await or chain work. Downsides: many small heap allocations, auxiliary state per promise, and synchronization overhead across many small tasks. Mitigations include custom allocators and data partitioning.
What is the actor model and what trade-offs does it bring?The actor model encapsulates state and behavior per actor, interacting via asynchronous message passing. It simplifies concurrency by avoiding shared state and locks, scales well, and supports distribution. Trade-offs include non-guaranteed message ordering and more complex debugging of asynchronous flows.
How do data parallelism and task parallelism reduce latency, and what are their limits?Data parallelism runs the same operation on independent data chunks (for example summing array segments in parallel or using SIMD/GPU). Task parallelism runs independent tasks simultaneously (for example parallel API calls), reducing latency to roughly the slowest task. Limits include overhead of splitting/combining work, communication costs, and Amdahl’s Law.
How do isolation levels and concurrency control impact latency?Stronger isolation (for example serializable) prevents more anomalies but often increases latency via locking or validation. Snapshot isolation (via MVCC) boosts concurrency by giving each transaction a consistent snapshot, preventing dirty and non-repeatable reads but allowing write skew. 2PL (pessimistic) blocks on locks; MVCC (optimistic) avoids many waits but needs extra storage and garbage collection. Choose the weakest isolation that meets correctness needs.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Latency ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Latency ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Latency ebook for free