Is this library method CPU-bound or I/O-bound?

1 min read

A signature does not say whether a method is CPU-bound or I/O-bound (see I/O-bound or CPU-boundI/O-bound or CPU-bound?Async helps most when the work is waiting, and CPU-heavy work needs Task.Run.Opens in a new tab).

  1. What it touches. A Stream, HttpClient, SqlConnection, File.* or socket means I/O. Transforming bytes already in memory (parsing, decoding, resizing, compressing) means CPU. This settles most cases.
  2. The Async suffix or a Task return proves nothing. Fake async methods call Task.Run(() => Sync()) or wrap a result in Task.FromResult, and still use a pool thread.
  3. The source, if open: look for Stream.ReadAsync, sockets or HttpClient (I/O), or loops and math with no I/O (CPU).
  4. Run it and watch. A core near 100% for the duration means CPU-bound. Near-idle CPU while the call runs means I/O-bound. dotnet-counters works for closed-source libraries.
  5. A profiler. A dotnet-trace shows a thread either executing code (CPU) or blocked in a kernel wait or I/O completion (I/O).
  6. A sync-only method. RenderPage(data) with no RenderPageAsync hints at CPU-bound work, since authors usually offer async for I/O. It is not proof.

Example

In a PDF library, a page-loading method with a real LoadPageAsync that awaits streamStreamsReading and writing data as a flow of bytes, a chunk at a time.Opens in a new tab reads is I/O, so it suits Task.WhenAll or channels. Page rendering is CPU-bound (rasterization, font shaping), even if wrapped in Task.Run, so it needs explicit parallelism (see the load-then-render pipelineLoad everything then render, or stream through a channelTwo shapes for a job that loads data (I/O) and renders it (CPU).Opens in a new tab).