An abstraction can make code easier to read while concealing how much work an operation performs. Its cost may be measurable in a benchmark, yet impossible to infer from the line that invokes it. That is not a flaw in abstraction by itself: hiding implementation details is the point. The engineering challenge is recognizing when a hidden difference matters to a real workload.
What “invisible at the call site” means
A call site shows the operation the code asks for, not necessarily the work required to fulfill it. A short function call might be inlined into a few instructions, or it might cross a process boundary, allocate memory, issue I/O, or trigger many operations elsewhere. The syntax alone often does not reveal which path is taken.
As an Amazon Associate I earn from qualifying purchases.
Chris’s September 8, 2026 article distinguishes costs that are apparent in source, costs that are difficult to measure, and costs that can be measured but are not visible in the source syntax. The last kind is easy to overlook: there is no obvious visual cue at the call site telling a developer to budget for it or investigate further. As the article puts it, “The abstraction earns its place.”
Recommended Free Tools
Why the same abstraction can cost different amounts
A useful way to reason about hidden cost is to ask how often an operation runs, how much useful work each invocation accomplishes, and what implementation path the system takes. A fixed overhead can be negligible when attached to a large operation, yet dominate when repeated for tiny pieces of work.
#1 Best Overall
- Frequency: A small cost per call can accumulate in a hot loop or a large number of requests.
- Work per call: Moving little data or doing little computation gives fixed overhead less useful work to be amortized against.
- Implementation path: A compiler may inline a function; a library may use a kernel-provided fast path; another configuration may require a more expensive transition.
- Workload and resources: Throughput, latency, CPU use, and concurrency can move in different directions, so one timing number may not describe the trade-off.
Chris illustrates repeated overhead with the arithmetic that a hundred-nanosecond cost repeated across a million tiny reads would total a tenth of a second of transition time. That is an explanatory calculation, not an independent benchmark result. The article also cites ORM N+1 queries, remote method calls, and string concatenation in a hot loop as examples of familiar-looking syntax that can conceal work; those examples do not establish a universal cost for each technique.
Examples: hidden work and ways to amortize it
Clock reads and the Linux vDSO
On Linux, the virtual dynamic shared object (vDSO) is a kernel-mapped shared object available to user-space processes. A C library can use it for supported operations, allowing some calls to be handled without a system call. The kernel documentation describes this as useful for frequently called operations whose system-call overhead could otherwise matter. Whether a particular operation uses the vDSO depends on the architecture, kernel, and supported function; the call’s spelling does not settle that question. See vdso(7).
Rank #2
Chris reports that, on the author’s laptop, clock_gettime(CLOCK_MONOTONIC, ...) took about 17 nanoseconds, while syscall(SYS_getpid) took about 107 nanoseconds—roughly six times as long in that comparison. These are individual observations reported in the article, not general constants or a benchmark study. They should not be extrapolated to other CPUs, kernels, C libraries, or clock sources.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
File transfer with sendfile()
Linux sendfile() transfers data between file descriptors within the kernel. Compared with a read()/write() sequence, its documented design advantage is avoiding the transfer into and back out of user space. This describes a mechanism, not a guaranteed speedup: workload, filesystem, destination, and surrounding application behavior still matter. See sendfile(2).
Batching asynchronous I/O with io_uring
Linux io_uring provides shared submission and completion queues for asynchronous operations, and applications can submit multiple requests in batches. Batching can spread per-submission overhead across more work. Its benefits depend on how an application uses the interface and the workload it serves; asynchronous does not mean cost-free. The SQPOLL option can have a polling thread check for submissions and avoid some submission calls, but that thread consumes CPU while active. It is therefore a configuration trade-off to test, not an automatic optimization. See io_uring(7) and io_uring_sqpoll(7).
How to investigate a hidden cost
Start from the work the application must do, not from a blanket rule that abstractions are slow. Compare alternatives under conditions that represent the actual workload, and measure the resources and outcomes that matter.
Rank #4
- Identify the operation and its frequency. Find the relevant call path and estimate how often it runs in the real scenario. A cost that is immaterial once may matter when repeated many times.
- Inspect what the call can trigger. Determine whether it stays in user space, crosses a kernel boundary, performs I/O, allocates, fans out into additional calls, or is optimized by the compiler or runtime. Do not infer the implementation solely from concise syntax.
- Measure useful work alongside overhead. Include realistic data sizes, concurrency, and call frequency. Look at latency and CPU use as well as throughput where they matter to the application.
- Compare batching or buffering where appropriate. Test whether doing more useful work per invocation reduces fixed overhead, while checking that added buffering, latency, memory use, or CPU consumption remains acceptable.
- Record the environment and configuration. A result belongs to the machine, software stack, and settings that produced it. For system interfaces, note relevant kernel and architecture details; for asynchronous I/O, include options such as polling.
A benchmark can establish that one path performed better under its measured conditions. It cannot by itself establish a universal ranking of abstractions. Preserve the conditions with the result, then verify that the application’s own workload behaves similarly.
The practical trade-off
Abstraction makes programs easier to understand and change by separating intent from implementation. That same separation means performance-relevant work may be hidden behind a stable-looking call. The right response is neither to avoid abstraction nor to assume it is free: use it for the clarity it provides, and investigate its implementation when measurement shows that a hidden cost matters.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




