How Do You Measure Instrumentation Overhead in OpenTelemetry?

How Do You Measure Instrumentation Overhead in OpenTelemetry?

A complete, plain-English guide to finding out what observability really costs your application in speed, processor time, memory and network, using fair experiments, honest statistics and a clear budget, so that you can keep the insight and control the price.

Imagine you are a doctor who wants to know how a heart behaves during a run. You attach sensors to the runner’s chest, wrists and ankles. The readings are wonderful. Then someone asks an awkward question: do the sensors, the wires and the little backpack of electronics slow the runner down? If they do, then the readings describe a runner who is a little slower than the real one, and every conclusion drawn from them is slightly off. The honest doctor does not guess. The doctor has the runner run the same track twice, once with sensors and once without, and compares the two.

Instrumentation in software is exactly this. When you add OpenTelemetry to an application, you attach sensors that record what the program does. Those sensors cost something: a little time on each request, a little processor power, some memory, some network traffic. Usually the cost is small. Sometimes it is not. The only way to know which is true for your system is to measure it carefully. This tutorial explains how, step by step, using ordinary language and everyday analogies, so that you can design an experiment, read the results, and make decisions you can defend.

1Why Measure Overhead at All?

Before learning how, let us be clear about why this question deserves a careful answer.

Observability tools exist to help you understand your system. But they run inside your system, using the same processors, the same memory and the same network as the program they are watching. Physicists call this the observer effect: the act of measuring can change what is being measured. In software, the effect is usually tiny, yet it is never exactly zero, and it can grow in surprising ways when instrumentation is added carelessly to the busiest parts of a program.

There are three good reasons to measure rather than assume. The first is trust: if you cannot say how much your telemetry costs, people who care about performance will, quite reasonably, resist adopting it. The second is cost control: processor time, memory and network traffic are paid for, and overhead multiplied by thousands of servers becomes real money. The third is safety: a badly configured instrumentation setup can cause slowdowns or memory pressure during exactly the moments when the system is already under stress.

Never zero
every sensor has a cost, however small
5 areas
latency, throughput, CPU, memory, network
1 method
compare the same system with and without
Simple Analogy

Think of a smart watch that tracks your sleep. It is only useful if it is comfortable enough that you actually wear it all night. If it is so heavy that it wakes you up, it ruins the very sleep it was meant to measure. Instrumentation has to be light enough that the system it observes still behaves like itself.

i
Good to know

The goal is not zero overhead, which is impossible. The goal is a known, acceptable overhead that you have measured, written down and agreed with the people who own performance.

2The Vocabulary You Need First

Each term below comes with what it is, why it matters for this topic, and a picture to hold in your head.

Term 1

Overhead

The extra cost caused by adding instrumentation, measured in time, processor use, memory or network traffic. It is the difference between two runs.

Term 2

Baseline

The reference run without instrumentation, or with the lightest possible setup. Everything is compared against it.

Term 3

Latency

How long one request takes from start to finish. The time a customer actually waits.

Term 4

Percentile

A way to describe the slow end. The 99th percentile, often written p99, is the time that 99 out of 100 requests beat.

Term 5

Throughput

How many requests the system finishes each second. The size of the pipe, not the speed of one drop.

Term 6

CPU time

How much processor effort a program consumes, usually shown as cores used. More cores used for the same work means more cost.

Term 7

Allocation and garbage collection

Creating objects uses memory, and many runtimes later clean up unused objects in pauses or background work. More objects created means more cleanup.

Term 8

Warm-up

The early period when a program is still loading code and optimising itself. Measurements taken during warm-up are not typical.

Term 9

Load generator

A tool that sends artificial traffic to the system so the experiment is repeatable.

Term 10

Noise

Random variation between runs that has nothing to do with instrumentation. Good experiments measure it and rise above it.

Term 11

Auto-instrumentation

Instrumentation added without changing code, usually by an agent or library hook that watches common frameworks.

Term 12

Exporter

The part of the SDK that sends finished telemetry out of the application, usually to a Collector.

!
Common confusion

Overhead is not the same as the time a span reports. A span says how long an operation took. Overhead is how much longer the whole request took because the span existed. They are different questions with different measurements.

3Where Overhead Actually Comes From

To measure a cost, you must know where it hides, so let us follow a request through an instrumented program.

Instrumentation work happens in several places. Some of it happens on the thread that is serving the customer, and that part adds directly to latency. Some happens on background threads, and that part adds to processor and memory use but not directly to latency. Some happens on the network and in other programs, such as the Collector. A complete measurement looks at all of these.

SourceWhere it runsWhat it costs
Starting and ending a spanOn the request threadA little time and a few small objects per span
Setting attributes and adding eventsOn the request threadTime and memory proportional to how many and how large
Context propagationOn the request thread, at every call between servicesReading and writing a few headers
Sampling decisionOn the request threadA small calculation per span
Queueing for exportHandoff from request thread to backgroundA small, brief synchronisation
Batching and serialisingBackground threadProcessor time and temporary memory
Sending over the networkBackground thread and networkBandwidth and a little processor work
Cleaning up memoryRuntime garbage collectionExtra pressure from many short-lived objects
Agent loading and class modificationAt start-upLonger start time and some extra memory
Receiving and processing in the CollectorA separate programIts own processor, memory and network cost
Simple Analogy

Think of a restaurant that adds a feedback card to every meal. The waiter must hand out the card, which takes a few seconds at the table. Someone in the back must collect and sort the cards. A courier must deliver them to head office. The customer notices only the waiter’s few seconds, but the restaurant pays for all three jobs.

i
Design principle

OpenTelemetry’s own specification says that normal telemetry operations should not block the application, and that background work should be asynchronous. Your measurements are, in effect, a check that this promise holds in your particular system.

4The Core Idea in One Page

Every sound overhead measurement is a variation on one simple experiment.

1

Choose what to compare

Decide on a baseline and a variant, for example the program without instrumentation and the same program with it.

2

Hold everything else still

Use the same machine type, the same version of the application, the same traffic and the same settings. Change exactly one thing.

3

Apply realistic load

Send repeatable traffic that looks like real usage, and wait until the system is warm and steady before recording.

4

Measure the five areas

Record latency, throughput, processor use, memory and network traffic for both runs.

5

Repeat and compare honestly

Run each variant several times, measure the natural noise, and report the difference only if it clearly rises above that noise.

“Overhead is a difference, so you need two honest runs and one fair comparison.”

Everything else in this tutorial adds detail to those five steps: how to build the ladder of variants, how to generate realistic load, how to read the numbers, how to measure noise, and what to do with the answer. Keep the five-step shape in mind and the details will fall into place.

5The Five Dimensions of Overhead

A cost can show up as slowness, as lost capacity, as extra processor use, as memory growth or as network traffic, and each needs its own measurement.

Dimension 1

Latency

Does each request take longer? Measure typical and slow requests, using percentiles, not only averages.

Dimension 2

Throughput

Does the system complete fewer requests per second at its limit? Measure how much load it can sustain.

Dimension 3

Processor use

Does the same amount of work use more processor effort? Measure cores used at a fixed request rate.

Dimension 4

Memory

Does the program hold more memory or create more garbage? Measure heap size, allocation rate and cleanup activity.

Dimension 5

Network and downstream cost

How much data leaves the program, and how much work does it cause in the Collector and backend?

Bonus

Start-up and size

How much longer does the program take to start, and how much larger is it on disk and in memory at rest?

DimensionBest measured withWho feels it
LatencyLoad test at a fixed request rate, response-time histogramsCustomers and calling services
ThroughputLoad test that increases traffic until the system saturatesCapacity planners
Processor useProcess-level processor counters at a fixed request rate, profilersFinance and infrastructure teams
MemoryHeap and allocation metrics, garbage collection logsOperations and reliability teams
Network and downstreamCounters on exporter bytes sent and Collector resource useNetwork and observability platform teams
Start-up and sizeTimed starts from a cold container, file and image sizesDeployment and autoscaling
!
Common mistake

Measuring only latency and declaring success. An application can show no change in latency while using noticeably more processor time and memory. If you pay per machine, those hidden costs are the ones you will see on the invoice.

6Overhead Is a Budget: Decide What Is Acceptable First

A number means nothing until you know what number would be too much.

Many overhead investigations go in circles because nobody agreed in advance what counts as acceptable. The measurement shows that processor use is up six percent, and one person calls that negligible while another calls it a scandal. The cure is to write the budget down before you run the test. A budget turns an argument about opinions into a check against a target.

A good budget has a few lines, each tied to something the business cares about. For a customer-facing service, the most important line is usually the slow tail of latency, because slow requests are what customers complain about. For a service that is cost-sensitive, processor and memory lines matter more. For a service with a strict capacity plan, the throughput line matters.

Budget line (illustrative targets)Example limitWhy this limit
Added latency at p50No more than about 1 millisecondTypical requests should feel no difference
Added latency at p99No more than about 3 percentSlow requests drive complaints and timeouts
Added processor useNo more than about 5 percentKeeps cost growth predictable
Added memoryNo more than a fixed amount of megabytes per instanceFits within container limits with safe headroom
Maximum sustainable throughputDrop of no more than about 3 percentProtects the capacity plan
Start-up timeIncrease of no more than a few secondsKeeps scaling and deployments responsive
i
The numbers above are examples

They show the shape of a budget, not recommended values. Your limits should come from your own service-level goals and cost targets, agreed with the people responsible for them.

Simple Analogy

Think of a family budget. You decide in advance that streaming services may take at most a certain share of your income. Then, when a new subscription appears, the question is not whether it feels expensive, but whether it fits the budget. A written limit makes the answer easy and keeps the conversation friendly.

7Designing a Fair Experiment

Most bad overhead numbers come from unfair comparisons, not from bad tools.

A fair experiment changes only the thing you are studying. In practice, many other things can change by accident, and each one can hide or exaggerate the effect of instrumentation. A short checklist of controlled factors prevents most of these accidents.

1

Same machine type

Run both variants on identical hardware, ideally dedicated, so that neighbours on a shared machine do not add random noise.

2

Same build of the application

Use the same code version. The only difference should be whether and how instrumentation is enabled.

3

Same traffic, same pattern

Send identical request mixes at identical rates. A different mix changes the work done, not just the instrumentation.

4

Same settings and dependencies

Databases, caches and downstream services should behave the same way in both runs, or be replaced by consistent stand-ins.

5

Warm up before measuring

Let the program run under load until its behaviour stabilises, then record. The specification’s own benchmark guidance calls for a warm-up phase for languages that optimise code while running.

6

Alternate and repeat the runs

Run baseline, variant, baseline, variant, rather than all baselines first. This stops slow drifts, such as a machine heating up, from being mistaken for an effect.

Simple Analogy

Imagine testing whether a new pair of running shoes makes you faster. You would run the same route, at the same time of day, in the same weather, after the same breakfast. If you ran the old shoes on a cold morning and the new ones on a hot afternoon, the result would say more about the weather than the shoes.

!
Hidden changers

Background jobs, automatic updates, other tenants on the same host, and power-saving modes can all change results between runs. If a number looks surprising, check whether something else changed before blaming the instrumentation.

8The Overhead Ladder: Isolating Each Layer

Comparing only the two extremes tells you that there is overhead, but not where it comes from; a ladder of variants tells you both.

Instead of two runs, design a ladder of several. Each step adds exactly one more piece of the instrumentation stack, so that the difference between neighbouring steps is the cost of that one piece. This is the single most useful technique for turning a vague “it seems slower” into a precise “this particular layer costs this much”.

flowchart TD
    A["Step 0: no instrumentation at all"] --> B["Step 1: API only, no SDK installed"]
    B --> C["Step 2: SDK installed, sampler drops everything"]
    C --> D["Step 3: SDK samples, spans created, no export"]
    D --> E["Step 4: spans exported to a local fast sink"]
    E --> F["Step 5: spans exported to the real Collector"]
    F --> G["Step 6: full production configuration"]
        
FIG 1 — The overhead ladder: each step adds one layer, and each gap is that layer’s cost
StepWhat is switched onThe gap to the previous step shows the cost of
0 to 1The instrumentation interfaces, with no real implementation behind themThe calls themselves, which should be nearly free
1 to 2The SDK, with a sampler that records nothingMaking sampling decisions and carrying context
2 to 3Spans are really created and recordedCreating spans, attributes and events
3 to 4Spans are batched, serialised and sent to a trivial local receiverExport work inside the application
4 to 5The real Collector replaces the trivial receiverNetwork realities and real back-pressure
5 to 6Production settings such as sampling rate and limitsThe effect of your real configuration choices
Simple Analogy

Think of finding out why a car uses more fuel. You do not just compare the empty car to the fully loaded one. You add the roof rack, then the luggage, then the passengers, one at a time, and note the change after each addition. The step that costs the most is the one to fix first.

i
Start with the cheap steps

Steps one to three need no network and no Collector, so they are easy to run on a laptop or in a quick pipeline job. Run them first to find out whether overhead sits in your application code or further downstream.

9Designing a Realistic Workload

The traffic you send decides what you learn, so it must resemble the traffic your system really sees.

Instrumentation overhead depends heavily on what the application is doing. A request that makes a single quick database call creates a handful of spans. A request that fans out to twenty services creates dozens. If your test sends only simple requests, you will measure a very light overhead and be surprised in production. A good workload reflects the real mix: the common requests, the heavy ones, and the rare expensive ones.

Open loop versus closed loop

Load generators work in one of two ways. A closed loop generator has a fixed number of virtual users, and each user waits for its response before sending the next request. If the system slows down, the generator automatically sends fewer requests, which hides the slowdown. An open loop generator sends requests on a fixed schedule, regardless of whether earlier ones have finished, just as real customers do. Open loop generators give a much more honest picture of latency and of how the system behaves near its limits.

flowchart LR
    subgraph CL["Closed loop"]
        U1["Virtual user sends request"] --> W1["Waits for response"] --> U1
    end
    subgraph OL["Open loop"]
        T["Timer fires on schedule"] --> S["Sends request"] --> T
        S --> R["Response arrives whenever it arrives"]
    end
        
FIG 2 — Closed loop waits for each answer; open loop keeps the schedule like real customers do
!
Coordinated omission

When a closed loop generator waits during a slow period, it simply stops sending requests that would have been slow, and so never records them. The results look better than reality. This well-known trap makes overhead look smaller than it is, especially in the slow tail. Prefer open loop generators for latency work.

Feature 1

Realistic request mix

Use the same proportions of request types seen in production, taken from logs or existing metrics.

Feature 2

Realistic data

Use varied inputs. Identical requests can be unrealistically fast because everything is cached.

Feature 3

Steady phase

Include a long, constant-rate period after warm-up, because that is where the measurements are taken.

Feature 4

Several rates

Test at low, typical and high load. Overhead often becomes visible only when the system is busy.

10Measuring Latency the Right Way

Averages hide the very requests that matter most, so latency must be measured with percentiles.

Suppose a service answers ninety-nine requests in ten milliseconds and one request in a full second. The average is about twenty milliseconds, which sounds fine and describes nobody’s real experience. Customers see either ten milliseconds or one second. Percentiles describe this honestly: the 50th percentile, or median, is ten milliseconds, and the 99th percentile is a full second. Instrumentation overhead often shows up first in the slow tail, for example when a garbage collection pause coincides with a burst of span creation.

StatisticWhat it tells youHow overhead shows up in it
Median (p50)The typical requestA small, steady shift if each request does extra work
p90 and p95The upper-normal rangeGrowth when queues or locks become busy
p99The slow tail that customers complain aboutJumps from pauses, contention and bursts
p99.9 and maximumThe worst casesRare pauses; useful but noisy, so interpret with care
AverageA single summary numberEasy to compute but easy to misread; never use it alone

Record histograms, not just summary numbers

The best practice is to record the full distribution of response times as a histogram, then compare the two histograms. A histogram lets you see whether the whole curve shifted slightly, which suggests a steady per-request cost, or whether only the slow tail grew, which suggests pauses or contention. It also lets you compute any percentile later without rerunning the experiment.

Simple Analogy

Think of a bus route. The average journey time may be thirty minutes, but if one bus in a hundred breaks down and takes two hours, the people on that bus remember it for weeks. The average hides them. Percentiles tell you how often the bad days happen and how bad they are.

i
Measure per endpoint

Overhead differs between request types. A cheap endpoint with a handful of spans may show a larger relative slowdown than an expensive one, because instrumentation is a fixed cost added to a small base. Compare each important endpoint separately, not just the overall mix.

11Throughput and the Saturation Point

Latency at a comfortable load can hide a cost that appears only when the system is pushed to its limit.

Every service has a point beyond which adding more traffic stops increasing the number of completed requests and only makes the waiting longer. This is the saturation point. Instrumentation uses a little processor time on every request, so it moves the saturation point slightly lower. To see that, you must push the system to saturation with and without instrumentation and compare where the knee of the curve appears.

1

Start at a low rate

Send a modest, steady rate and confirm that response times are low and stable.

2

Raise the rate in steps

Increase traffic by a fixed amount, hold each step long enough to settle, and record latency and errors at each level.

3

Find the knee

The knee is the level where latency starts rising sharply or errors begin. Record the highest rate that stays within your latency goal.

4

Repeat with the variant

Run exactly the same steps with instrumentation enabled and compare the two knee positions.

Knee
the load level where latency begins to climb steeply
Lower knee
means less spare capacity because of overhead
Same steps
for both runs, or the comparison is not fair
Simple Analogy

Picture a motorway. At low traffic, every car moves at full speed. As more cars enter, speeds hold up until a certain density, then suddenly everyone slows down. If a new toll booth takes a second from each car, the point where traffic jams begin moves a little earlier. Throughput testing finds where that point is, with and without the booth.

!
Do not test only near idle

At low load, there is plenty of spare processor capacity to absorb background export work, and overhead is nearly invisible. The same instrumentation near saturation can show a clear effect. Always test at several loads, including one close to your real peak.

12Measuring Processor Use

Processor cost is the overhead that most directly becomes money, so measure it at a fixed amount of work.

The cleanest way to compare processor use is to hold the request rate constant, so both variants do the same work, and then compare how many processor cores each one uses. If the instrumented version uses a little more at the same rate, the difference is the cost of the instrumentation. Dividing the difference by the request rate gives a very useful number: the extra processor time spent on each request.

Method 1

Process-level counters

Read the total processor time consumed by the application process at the start and end of a steady period, and divide by the length of the period.

Method 2

Container or host metrics

Use the platform’s own processor metrics for the container or machine, which also capture helper processes such as an agent.

Method 3

Sampling profilers

Profilers that periodically sample what the program is doing show which functions use the processor, so you can see how much is instrumentation code.

Method 4

Continuous profiling

In production, low-overhead profiling over long periods can reveal instrumentation costs that short tests miss.

MetricHow to computeWhy it is useful
Cores usedProcessor seconds consumed divided by elapsed secondsDirectly comparable between variants
Processor time per requestCores used divided by requests per secondRemains meaningful if the request rate differs slightly
Relative overheadVariant minus baseline, divided by baselineA single percentage to compare with the budget
Share of profile in instrumentation codeProfiler samples inside instrumentation divided by all samplesShows exactly where the cost sits
i
Include the helpers

If instrumentation adds a separate process, such as a local Collector, include its processor use in the variant’s total. Otherwise the cost has merely been moved out of sight.

13Measuring Memory and Garbage Collection

Memory overhead has two faces: how much is held, and how much is created and thrown away.

Instrumentation holds memory in a few places. Spans that are still in progress live in memory until they end. Finished spans wait in a queue until they are exported. The SDK itself and any agent add a fixed amount. Because the specification asks implementations not to use unbounded memory, the queue has a limit, but that limit can still be a noticeable amount of memory when spans are large or numerous.

Instrumentation also creates many small, short-lived objects: spans, attribute lists, context objects, serialised buffers. In languages with automatic memory cleanup, these objects must later be collected. A high rate of object creation means more frequent cleanup, which can cause short pauses or extra background processor use. Often the allocation rate is a more telling number than the total memory held.

What to recordWhat it showsWarning sign
Steady-state heap sizeMemory held after warm-upA steady increase beyond what the budget allows
Peak heap sizeHighest memory during the testPeaks near the container limit
Allocation rateHow fast new objects are createdA large jump compared with the baseline
Cleanup frequency and pause timeHow often and how long the runtime pauses to clean upLonger or more frequent pauses at the same load
Resident memory of the processWhat the operating system sees the program usingGrowth that is not explained by the heap alone
Export queue depthHow many spans wait to be sentA queue that sits near its limit
Simple Analogy

Think of a kitchen. The pantry holds ingredients, which is the memory held. But the cooking also produces a constant flow of peelings and wrappers that must be cleared away, which is the allocation and cleanup. A kitchen can have a small pantry and still be overwhelmed by rubbish if it produces too much too quickly.

!
Look for slow growth

A short test can miss a memory problem that builds over hours. For anything that must run for days, include at least one long run and watch whether memory stabilises or keeps climbing.

14Start-Up Time and Footprint

Instrumentation can slow down the first seconds of a program’s life, which matters more than people expect.

Automatic instrumentation often works by inspecting and modifying program code as it loads. That takes time at start-up, and it keeps extra information in memory afterwards. In a world of containers that start and stop frequently, because of scaling, deployments and restarts, a few extra seconds at start-up can slow rollouts and delay new capacity during a traffic surge.

1

Start from cold

Measure start-up from a freshly created container or process, not a warm machine with cached files.

2

Define the finish line

Decide what ready means, for example the health check passing or the first request being answered, and time exactly that.

3

Repeat many times

Start-up times vary a lot. Run at least ten starts for each variant and compare the typical and the slowest.

4

Record footprint at rest

Compare memory use just after start-up, before traffic, and the size of the application package and image.

i
Who cares most

Start-up time matters most for short-lived jobs, serverless-style functions and fast-scaling services. For a long-running server that restarts once a month, a few seconds is usually irrelevant, so weigh this line of the budget accordingly.

Simple Analogy

Imagine a sprinter who must fit a heavy tracking vest before every race. Over a marathon the extra seconds at the start line are nothing. For a one-hundred-metre dash, they are the whole race. The shorter the lifetime of your program, the more the start-up cost counts.

15Network and Downstream Cost

The cost of telemetry does not stop at the edge of your application; it continues across the network and into the systems that receive it.

Every span that leaves the application becomes bytes on a network and work for a Collector and a backend. At small scale this is invisible. At large scale it is often the largest part of the bill. A full picture of overhead therefore includes how much data is sent and what it costs the receiving systems to handle it.

Measure 1

Bytes sent per request

Divide the bytes the exporter sends by the number of requests served. This shows how heavy each request’s telemetry is.

Measure 2

Spans per request

Count how many spans each kind of request creates. This number multiplies almost every other cost.

Measure 3

Export success and failure

Count exports that succeed, fail or are dropped. Rising failures mean data is being lost and queues are growing.

Measure 4

Collector resource use

Record the processor and memory the Collector uses at your traffic level, and divide by traffic to get a cost per span.

i
Compression and batching

Larger batches and compressed transfers usually reduce network cost and per-item processing cost, at the price of slightly higher delay and a little processor time. Include these settings in your ladder so that you can see their effect.

Simple Analogy

Think of posting letters. Mailing each letter separately costs a stamp and a trip to the box every time. Gathering a week of letters into one envelope costs one stamp and one trip. The batch is a little late, but the total cost is far lower. Export batching trades a little delay for much lower cost.

16What OpenTelemetry Itself Suggests

The project publishes benchmark guidance that you can borrow as a starting recipe.

OpenTelemetry’s specification includes a short benchmark description for its tracing SDKs. It defines one standard test scenario: spans with no parent, default settings and always-on sampling, each carrying a numeric attribute and one event, and each ended immediately after creation. The guidance asks implementers to report three things for this scenario, and gives a few rules for how to run it.

MeasurementWhat the guidance describes
ThroughputHow many spans with ten attributes, each with short text values, can be created and exported per second per processor core
Processor useAverage and peak processor use of the SDK with the default simple and batching span processors and the standard exporter, at a stated rate, with ten thousand spans per second as the default
MemoryHeap use under the same conditions as the processor test
Rule 1

Run long enough

Tests should last at least fifteen seconds so that short bursts do not dominate the result.

Rule 2

Repeat the runs

Repeat measurements at least ten times and report the combined result, which gives a feel for natural variation.

Rule 3

Warm up

For languages that optimise code while running, include a warm-up phase before measuring.

Rule 4

Use a cheap receiver

The receiving end should answer immediately without extra work, so the test measures the SDK and not the receiver.

i
How to use this

Treat the specification’s scenario as the micro-level building block of your own ladder, steps three and four. Then add your own application-level tests on top, because a synthetic span test cannot tell you what your real request mix will cost.

!
Limits of a standard test

A standard scenario is useful for comparing SDK versions or languages under identical conditions. It is not a prediction of your overhead, because your application has its own span counts, attribute sizes and traffic patterns.

17The SDK Settings That Move the Numbers

A few configuration choices have an outsized effect on overhead, so they belong on your ladder.

The tracing SDK’s batching span processor has four main settings, each with a documented default. The queue size, defaulting to two thousand and forty-eight spans, limits how many finished spans can wait for export. The schedule delay, defaulting to five seconds, controls how often a batch is sent. The export timeout, defaulting to thirty seconds, limits how long one export may take. The maximum batch size, defaulting to five hundred and twelve, limits how many spans go out in one request. When the queue is full, new spans are dropped rather than slowing the application.

SettingDocumented defaultEffect on overhead if you raise itEffect if you lower it
Queue size2048 spansMore memory held, fewer dropped spans in burstsLess memory, more drops under bursts
Schedule delay5 secondsLarger, less frequent batches and fresher memory use, but later dataMore frequent small exports, more network and processor work
Export timeout30 secondsSlow exports tie up the background worker longerFailed exports are abandoned sooner, risking loss
Maximum batch size512 spansFewer requests, larger payloadsMore requests, smaller payloads

Simple versus batching processors

A simple processor sends each span as soon as it finishes, which is easy to understand but costly because every span triggers its own export call. A batching processor groups spans and sends them together. For nearly all production use, batching is far lighter, which is why your ladder should compare both.

!
Defaults may differ

The values above come from the specification. Individual language SDKs can differ in details, and defaults can change between versions, so confirm the settings of the exact SDK you test and record them with your results.

Simple Analogy

A simple processor is like a courier who drives to the depot with every single parcel. A batching processor is like a courier who waits for a van-load. The van takes slightly longer to fill, but the fuel bill is far smaller.

18Sampling: The Biggest Lever You Can Test

Choosing how many traces to record changes almost every cost at once, so test it explicitly.

The SDK offers several standard samplers. The always-on sampler records every span. The always-off sampler records none. The ratio-based sampler records a chosen fraction of traces, deciding in a way that stays consistent across all services touched by one trace. The parent-based sampler follows the decision already made by the parent span, which keeps whole traces together. Each choice changes how many spans are fully recorded and exported, and therefore how much overhead the system carries.

SamplerWhat it doesEffect on cost
Always onRecords every spanHighest cost; useful as a worst-case baseline
Always offRecords no spansLowest SDK cost; shows the floor of the instrumentation layer
Ratio basedRecords a fixed fraction of tracesCost falls roughly in proportion to the fraction kept
Parent basedFollows the parent’s decision, with a chosen rule for root spansKeeps traces complete; cost follows the root decision

Not every cost shrinks with sampling

When a span is not sampled, most of the heavy work is skipped: recording attributes and events, queueing, serialising and sending. But some work remains for every request, such as creating the context, making the sampling decision and passing headers to the next service. This residual cost is exactly what steps one and two of your ladder measure. A good result is one where the always-off configuration is very close to the uninstrumented baseline.

flowchart LR
    R["Request arrives"] --> S{"Sampling decision"}
    S -->|"Not sampled"| N["Light path: context only, no recording, no export"]
    S -->|"Sampled"| H["Heavy path: record, queue, batch, serialise, send"]
        
FIG 3 — Sampling splits requests into a light path and a heavy path
i
Test several ratios

Run your ladder at a few sampling levels, for example everything, a tenth and a hundredth. Plot overhead against the sampling ratio. The curve usually shows a steady floor plus a part that grows with the ratio, and tells you which sampling level meets your budget.

!
Sampling changes what you learn, too

Lower sampling reduces cost but also reduces visibility. Overhead measurement tells you the price; deciding how much visibility to buy is a separate, equally important decision.

19Attributes, Events and Limits

How much you attach to each span matters as much as how many spans you create.

A span can carry attributes, which are labelled facts such as the route name or a customer tier, and events, which are timestamped notes. Each attribute costs a little time to set and some memory to hold, and it adds bytes to every export. A span with three short attributes and a span with fifty long ones are very different loads, even though both count as one span.

The SDK specification therefore defines limits that cap how many attributes, events and links one span can carry, and how long values may be. When a limit is exceeded, extra data is discarded and a warning is logged. These limits are a safety net, but they are not a substitute for thoughtful instrumentation, because the discarded data has already cost some effort to produce.

Factor 1

Attribute count

More attributes per span mean more time and more bytes per span. Measure with your real attribute counts, not only minimal ones.

Factor 2

Attribute value size

Long text values, such as full query strings or message bodies, inflate memory and network cost quickly.

Factor 3

Event count

Adding an event inside a loop can create thousands of events on one span. Check the maximum in realistic worst cases.

Factor 4

Link count

Links join spans across traces. Batch jobs that link to many inputs can generate large numbers of links on a single span.

Test variationQuestion it answers
Minimal attributes versus typical attributesHow much of the cost is driven by what we attach?
Short values versus real valuesDo large values dominate the export size?
Normal request versus worst-case requestWhat happens to the heaviest requests, not only the average ones?
Default limits versus tightened limitsCan limits cap the worst cases cheaply?
i
Cheap wins

Trimming oversized values and avoiding low-value attributes often cuts memory and network cost more than any tuning of queues or timers, and it makes traces easier to read as well.

20Instrumentation Style and Span Granularity

Where you place spans, and how many, usually matters more than any setting inside the SDK.

There are three broad ways to add instrumentation. Automatic instrumentation watches common frameworks and libraries for you, giving wide coverage with little effort. Library instrumentation is built into libraries by their authors. Manual instrumentation is code you add yourself, at places that matter to your business. Each style produces a different number of spans per request, and span count is the single biggest driver of cost.

StyleTypical span volumeOverhead characteristicWhat to test
AutomaticMany spans, wide coverageStart-up cost plus per-call cost across many librariesSpans per request and start-up time
Library built-inModerate, chosen by authorsUsually carefully tunedCost per common library call
ManualAs many as you writeEntirely under your controlPlacement in hot code paths
ANTI-PATTERN-01 Avoid
Problem

A span is created inside a tight loop or around a very small function that runs millions of times, such as every item in a large list.

Why It’s Harmful

The fixed cost of a span is small, but millions of them swamp the real work, create huge garbage, fill export queues and produce traces nobody can read.

Correct Approach

Create one span around the whole loop and record the item count as an attribute. Use spans for meaningful units of work, such as a call to another system, not for every function.

Simple Analogy

A tour guide who announces every single step the group takes would exhaust everybody. A good guide announces each landmark. Spans work best as landmarks, not as footsteps.

i
Measure span count first

Before any timing test, simply count spans per request for each request type. If one type creates hundreds of spans, you have found your likely overhead problem without running a single benchmark.

21Metrics and Logs Have Their Own Costs

Tracing is only one signal, and the other two can be just as expensive in different ways.

Metrics are numbers aggregated over time. Recording a measurement is usually very cheap, because it only updates a running total. The danger lies elsewhere: every distinct combination of label values creates a separate series that must be stored and exported. A label with an unbounded number of possible values, such as a user identifier or a full web address, can create an enormous number of series. This is called a cardinality explosion, and it costs memory, network and storage far more than the act of recording ever did.

Logs are records of individual events. Their cost scales with volume, which can be enormous if applications log on every request. The specification itself notes that logging can use a great deal of memory by default and recommends giving applications ways to filter what is sent. Tests should include realistic log volumes, including bursts caused by error storms.

Metrics

Cost of recording

Usually tiny per measurement. Test at your real measurement rate, especially for measurements recorded on every request.

Metrics

Cost of series count

Measure how many distinct series the application produces and how that number grows with traffic and labels.

Logs

Cost of volume

Measure records per second, bytes per record and behaviour during error bursts.

Logs

Cost of correlation data

Attaching trace identifiers to logs is cheap, but check that extra fields are not inflating each record.

!
Common mistake

Putting identifiers with unbounded values, like a customer number, into metric labels. The recording looks cheap in a benchmark with ten customers and becomes a disaster in production with ten million.

Simple Analogy

Writing a number on a tally sheet is quick. But if you start a new sheet for every possible combination of customer, product and day, you soon have a warehouse full of paper. The act is cheap; the filing system is not.

22Context Propagation and Baggage

The small headers that travel with every call are cheap individually and noticeable in aggregate.

For a trace to span several services, each call carries a few headers with the trace identifier and the sampling decision. Reading and writing these headers costs a tiny amount of time on every outgoing and incoming call, whether or not the trace is sampled. For almost all systems this cost is very small, but it is worth confirming, particularly for services that make a very large number of internal calls per request.

Baggage is a separate feature that carries custom key-value pairs along with the trace context to every downstream service. It is useful, but because it is copied into the headers of every call, it can grow the size of each message and the work of reading and writing it. Many teams are surprised by how much baggage accumulates over time.

ItemWhere the cost landsHow to measure it
Trace context headersEvery call, sampled or notLadder step two: compare no instrumentation with sampler-off
Baggage entriesEvery call, scaled by entry count and sizeCompare header sizes and per-call time with and without baggage
Header size growthNetwork and parsing in every serviceRecord average and maximum header size per call
i
Keep baggage small and deliberate

Put only a few short, genuinely needed values in baggage, and review it regularly. Large or numerous entries tax every service on the path, including ones that never use them.

Simple Analogy

Think of a school trip where every child wears a name badge. One small badge is nothing. If every teacher along the route adds another sticker, by the end each child is wearing a heavy coat of labels. Baggage behaves the same way.

23Micro-Benchmarks and Macro-Benchmarks

Small, focused tests and whole-system tests answer different questions, and you need both.

A micro-benchmark measures one tiny operation in isolation, such as the time to create and end a span. It is quick, repeatable and good at spotting changes between SDK versions. A macro-benchmark measures the whole application under realistic load, which captures interactions that no isolated test can, such as processor contention, memory pressure and garbage collection behaviour.

AspectMicro-benchmarkMacro-benchmark
What it measuresOne small operationThe whole application under load
Speed and cost to runFast and cheapSlower and more expensive
RealismLow, syntheticHigh, close to production
Best forComparing SDK versions, spotting regressions in the instrumentation itselfAnswering whether your service meets its budget
Main riskResults that do not carry over to the real applicationNoise from many moving parts
ANTI-PATTERN-02 Avoid
Problem

A team runs only a micro-benchmark, finds that creating a span takes a few microseconds, and concludes that overhead is negligible for the whole service.

Why It’s Harmful

The micro-benchmark ignores how many spans real requests create, garbage collection effects, queue contention and network costs. The real overhead can be far larger.

Correct Approach

Use micro-benchmarks to understand and compare building blocks, and use macro-benchmarks with a realistic workload to judge the budget.

!
Beware the clever compiler

In micro-benchmarks, runtimes can optimise away work whose result is never used, producing absurdly fast numbers. Make sure the benchmarked code genuinely runs and that its result is consumed. Warm-up matters here too, as discussed earlier.

24Measuring in Production

Test environments never perfectly match reality, so the final proof of overhead comes from real systems.

Production measurement answers the question that test environments cannot: what does instrumentation cost with real traffic, real data and real neighbours? The safest way to measure it is a controlled comparison inside production, using a small group of instances with the variant and a matching group without it, both receiving the same live traffic.

1

Create two matching groups

Pick instances of the same type, same version and similar load. One group runs the instrumentation under test, the other runs the baseline.

2

Split traffic fairly

Send both groups random, comparable slices of live traffic, so neither receives an easier mix.

3

Compare the same metrics

Look at latency percentiles, error rates, processor use and memory for both groups over several hours or days.

4

Keep a quick off switch

Make sure the variant can be disabled or its sampling reduced instantly, using a setting rather than a new release.

Tool 1

Canary groups

A small share of instances run the new instrumentation first, and their numbers are compared with the rest.

Tool 2

Sampling-rate toggles

Raising or lowering the sampling rate on a group shows how overhead scales, without changing code.

Tool 3

Self-telemetry

SDKs and Collectors can report on their own queues, drops and exports, giving a direct view of instrumentation activity.

Tool 4

Continuous profiling

Low-overhead profiles taken over long periods show how much of the application’s time is spent in instrumentation code.

!
Protect customers first

A production experiment must never put customers at risk. Start with a tiny group, watch closely, and stop at the first sign of harm. Overhead experiments are valuable, but not worth an outage.

25Statistics Without Tears: Noise and Confidence

A measured difference is only real if it is bigger than the random variation between identical runs.

No two runs of the same test give exactly the same numbers. Machines vary, networks vary, and the program itself varies. This natural variation is called noise. Suppose that two identical runs differ by two percent in processor use. If your instrumented variant uses three percent more than the baseline, then three percent is not clearly bigger than the noise, and you cannot honestly claim an effect. You need a way to find out how big the noise is.

The A/A test: measuring the noise floor

The simplest tool is an A/A test. Run the baseline against itself several times and look at how much the results vary. This shows the size of natural noise for your setup. Any difference between baseline and variant smaller than that noise should be treated as no detectable effect, not as zero effect.

1

Run the baseline several times

At least ten runs, as the specification’s benchmark guidance suggests for its own tests.

2

Note the spread

Record the lowest and highest values, and the typical value. The spread is your noise.

3

Run the variant the same number of times

Use identical conditions and alternate the runs with the baseline.

4

Compare the typical values and the ranges

If the ranges barely overlap and the typical values differ clearly, the effect is real. If the ranges overlap heavily, you need more runs or a quieter environment.

Simple Analogy

Weigh yourself five mornings in a row and the number will wobble by a few hundred grams even if nothing changed. If you then eat a large dinner and weigh two hundred grams more, you cannot say that dinner made a difference. The wobble is the noise floor, and only changes larger than the wobble mean anything.

i
Report ranges, not single numbers

Instead of saying that overhead is four percent, say that overhead was between three and five percent across ten runs. The range tells the reader how much to trust the result.

26The Experiment Flow from Start to Result

Let us follow one complete experiment through every component involved, to see how the pieces fit.

sequenceDiagram
    participant L as Load generator
    participant A as Application under test
    participant S as SDK and exporter
    participant C as Collector or local sink
    participant M as Metrics store
    L->>A: Send steady open-loop traffic
    A->>S: Create spans and record data
    S->>C: Export batches in the background
    A-->>L: Responses with timings
    A->>M: Report processor and memory use
    C->>M: Report received and dropped counts
    L->>M: Store latency histograms
    M->>M: Compare baseline and variant runs
        
FIG 4 — Components in an overhead experiment and the data each one produces
Output 1

Latency histograms

Recorded by the load generator, showing the full distribution of response times for each run.

Output 2

Resource metrics

Processor, memory and garbage collection numbers from the application’s host or container.

Output 3

Telemetry counters

Spans created, exported, dropped and received, which prove that the variant really did the work it was supposed to.

Output 4

Run records

Configuration, versions, machine type, start and end times, stored with each result so that it can be reproduced later.

i
Prove the variant was real

Always check the span counters. A variant that quietly dropped every span, because of a misconfiguration, will look wonderfully cheap and teach you nothing. The count of spans actually exported is your proof that the instrumentation was active.

Many tools can do each job, so it is more useful to know the categories than to memorise names.

CategoryJob in the experimentWhat to look for
Load generatorSends repeatable trafficOpen-loop mode, realistic request mixes, histogram output
Benchmark harnessRuns micro-benchmarks reliablyBuilt-in warm-up, repeated runs, protection against over-optimisation
ProfilerShows where processor time and memory goLow overhead itself, flame graph views, allocation profiling
Host and container metricsReport processor, memory and network useFine time resolution and per-process detail
Runtime diagnosticsReport heap, allocation and cleanup behaviourGarbage collection pauses and allocation rates
Local telemetry sinkReceives exports instantlyCounts what it receives, adds almost no delay
Metrics store and dashboardsKeeps and compares results across runsTagging runs by configuration and version

Keep the measuring tools light

A profiler or monitoring agent that itself uses a lot of processor time distorts the experiment. Use tools designed for low overhead, and apply the same tools to both variants so any distortion is shared equally.

!
Tools are not neutral

Attaching a heavy profiler changes timings. Use profilers in separate runs to explain a result, and use light-touch measurement for the main comparison.

28A Worked Example With Illustrative Numbers

Numbers make ideas stick. The figures below are invented for teaching, so please do not treat them as a promise about any real system.

Imagine an online shop with a service that handles checkout. During a busy hour it receives about two thousand requests every second. The team wants to switch on tracing, metrics and logs through OpenTelemetry. Before they do, they set a budget: the instrumented service may add at most five percent to the slowest requests, at most ten percent more processor time, and at most fifteen percent more memory.

They run the experiment ladder from earlier chapters. The first run is the baseline with no instrumentation. The second run has the library installed but switched off. The third run has full tracing at one hundred percent sampling. The fourth run keeps tracing but samples only ten percent of requests. All four runs use the same machine type, the same replayed traffic and the same duration, and each is repeated several times.

RunSlowest one percent latencyProcessor cores usedMemory in useData sent per second
Baseline, nothing installed40 ms6.0800 MB0
Installed but disabled40 ms6.0810 MB0
Full tracing, every request46 ms7.1980 MBHigh
Tracing, ten percent sampled41 ms6.3870 MBLow

Now do the arithmetic. Full tracing raised the slow-request latency from forty to forty-six milliseconds, which is fifteen percent. It raised processor use from six to seven point one cores, about eighteen percent. Memory grew from eight hundred to nine hundred eighty megabytes, about twenty-two percent. All three exceed the budget. With ten percent sampling, latency rose by two and a half percent, processor use by five percent and memory by about nine percent. All three fit.

Simple Analogy

Think of a school bus carrying a few extra backpacks. One bag barely changes the trip. Every child carrying a heavy science kit slows the bus on every hill. The team found that carrying kits for only one child in ten kept the bus on schedule while still showing what the trip was like.

i
Read the table, not just the headline

The disabled run matters. It shows that merely installing the library cost almost nothing, so the overhead came from creating and exporting data, not from the presence of the library.

29Interpreting Results Without Fooling Yourself

A result is only useful when you understand what it does and does not say.

The first lesson is to look at both absolute and relative change. A rise of six milliseconds sounds small, yet on a request that normally takes forty it is fifteen percent. A rise of two milliseconds on a request that takes two seconds is invisible. Always write down both numbers so that readers can judge for themselves.

The second lesson is to think in cost per request. If instrumentation adds a fixed amount of processor time to each request, then a service handling more requests pays more in total. A second way to say this is that overhead usually grows with the number of spans, metric updates and log lines per request, not with the number of requests alone. A request that touches twenty internal steps and records a span for each pays much more than one that records a single span.

The third lesson is about shape. Averages hide trouble. If most requests are unchanged but a few become much slower because an export buffer fills up, the average barely moves while the slow tail grows. Look at percentiles, and look at the whole distribution, not at one summary number.

Question 1

Is the change bigger than the noise?

Compare the difference with the spread between repeated runs of the same setup. A difference smaller than the spread proves nothing.

Question 2

Does it grow with load?

Overhead that is tiny at low traffic may explode near saturation. Check several load levels.

Question 3

Does it grow with detail?

Double the spans or attributes and see whether cost doubles. This reveals what you are really paying for.

Question 4

Does it hurt the tail?

Look at the slowest requests, because users feel those first.

“Averages comfort you; percentiles inform you.”

30Ways to Reduce Overhead, Ordered by Impact

When the budget is exceeded, change the biggest levers first.

Many teams start by tuning small settings and are disappointed. The better habit is to work from the largest source of cost to the smallest. The timeline below follows that order.

1

Record less, by sampling

Keeping only a share of traces cuts creation, storage and export work for everything downstream. This is usually the largest single saving.

2

Remove low-value instrumentation

Spans around tiny internal functions rarely help anyone. Keep spans for meaningful boundaries such as incoming requests, outgoing calls and database work.

3

Control attributes and events

Fewer, smaller attributes and events mean less memory and less to send. Use the SDK limits to put a ceiling on size.

4

Control metric dimensions

Each distinct combination of attribute values creates a separate series to track. Avoid attributes with endless possible values, such as user identifiers.

5

Batch and compress exports

Sending data in larger groups, away from the request path, reduces network calls. Compression trades processor time for bandwidth, so measure it.

6

Move work off the application

Let a separate Collector perform heavy tasks such as filtering and enrichment, so the application only hands over raw data.

7

Tune the small settings last

Queue sizes, timers and buffer limits matter, but only after the bigger levers are set.

Simple Analogy

If a suitcase is too heavy, you do not start by trimming the zipper. You first take out the heavy shoes you will not wear. Sampling and removing needless spans are the heavy shoes; buffer tuning is the zipper.

!
Do not reduce visibility blindly

Cutting data lowers cost but also lowers insight. Keep errors and unusually slow requests whenever your sampling approach allows it, so the data you keep is the data you most need.

31Measurement Mistakes That Produce False Answers

Bad measurements are worse than none, because they create confidence in the wrong answer.

ANTI-PATTERN-03 Avoid
Problem

The team compares one baseline run with one instrumented run, done on different days, on shared machines, with different traffic.

Why It’s Harmful

Neighbouring workloads, warm-up state and traffic differences all change results. The team then blames or excuses instrumentation based on noise.

Correct Approach

Run both setups on identical, quiet machines, interleave them, repeat several times, warm up before recording, and compare the spread between runs against the difference you found.

ANTI-PATTERN-04 Avoid
Problem

The team measures only the average response time and declares the overhead negligible.

Why It’s Harmful

The average hides the slow tail, where full queues and garbage collection pauses show up first. Users and service targets are usually tied to the tail.

Correct Approach

Record percentiles such as the median, the ninety-ninth and higher, alongside throughput, processor use, memory and garbage collection behaviour.

ANTI-PATTERN-05 Avoid
Problem

The test sends data to a fast local sink that discards everything immediately.

Why It’s Harmful

Real destinations have delays, limits and occasional failures. A perfect sink hides back-pressure, retries and dropped data, so the result is too optimistic.

Correct Approach

Use a destination that resembles production, and also test a slow or failing destination to see how the application behaves under stress.

i
A simple sanity check

Before trusting any result, ask whether you could repeat it tomorrow and get a similar answer. If not, the experiment, not the instrumentation, is the thing to fix.

A short list of habits separates calm measurement from chaos.

Best practices

  • Write the overhead budget before measuring.
  • Change one thing at a time on the ladder.
  • Warm up the service before recording.
  • Repeat each run and report the spread.
  • Use a realistic workload with realistic load levels.
  • Record percentiles, processor use, memory and garbage collection.
  • Test with a production-like destination.
  • Save the configuration and versions with each result.

Common mistakes

  • Measuring once and declaring victory.
  • Using only averages.
  • Testing at low load and assuming it scales.
  • Leaving a heavy profiler attached for the main comparison.
  • Ignoring the cost of the pipeline after the application.
  • Forgetting that settings differ between languages.
  • Never re-measuring after upgrades.
  • Treating a number from another company as your own.
Simple Analogy

A good cook tastes the soup several times while it simmers, using a clean spoon each time. A careless cook tastes once, from the top, and serves it. The habits above are the clean spoon and the repeated tasting.

33Reporting Results So People Can Trust Them

A result that cannot be repeated by someone else is only an opinion.

A good report is short, honest and complete enough that a colleague could rerun the experiment. Start with the question and the budget. Then describe the setup: machine type, number of processor cores, language and runtime versions, OpenTelemetry library versions, configuration, sampling setting and the workload shape. After that show the results as a table with the spread between runs, followed by a plain sentence stating whether the budget was met.

Next, state the limits. Say what you did not test, such as other languages, much higher traffic or unusual failures. This is not weakness; it protects readers from using your result somewhere it does not apply. Finally, store the raw data and the exact configuration in a place the team can find later, because the question will come back after the next upgrade.

Report sectionWhat it containsWhy it matters
Question and budgetWhat you wanted to know and the limits you acceptedMakes the conclusion testable
SetupHardware, versions, settings, workloadLets others repeat the test
MethodLadder steps, warm-up, repeats, durationShows the test was fair
ResultsTables with percentiles, processor, memory and spreadShows the evidence
ConclusionBudget met or not, and whyGives a decision
LimitsWhat was not testedPrevents misuse of the result
i
Keep a results diary

Over time, a folder of dated reports becomes a history. You can see whether each release made instrumentation heavier or lighter, and catch drift early.

34Catching Regressions Automatically

Overhead is not a one-time question. It can creep up every release unless something watches for it.

Because instrumentation changes with every new span, attribute and library upgrade, overhead tends to rise slowly, like weight gained a little at a time. The cure is to run a small, repeatable test inside your delivery pipeline. It does not need to be perfect; it needs to be consistent. If the result moves outside an agreed band compared with the last accepted result, the pipeline raises a warning or blocks the change.

flowchart LR
    A["Code or library change"] --> B["Build service"]
    B --> C["Run baseline and instrumented tests"]
    C --> D["Compare with last accepted result"]
    D --> E{"Within the budget band?"}
    E -->|"Yes"| F["Accept and store the result"]
    E -->|"No"| G["Warn or block and investigate"]
    G --> H["Fix or update the budget with approval"]
    H --> C
        
FIG 5 — A pipeline that checks overhead on every change

The diagram shows the loop. A change triggers a build. The pipeline runs the baseline and the instrumented versions under the same conditions, then compares the outcome with the last accepted result. If it fits, the new result becomes the reference. If not, a person investigates, and either fixes the cause or deliberately updates the budget with approval so that the decision is visible.

!
Beware of noisy shared runners

Machines shared with other jobs produce noisy numbers. Use dedicated or well-isolated machines for these tests, and set the allowed band wider than the usual noise so that the check does not cry wolf.

Simple Analogy

A bathroom scale that you step on every morning catches a slow gain long before your clothes feel tight. The pipeline check is that scale for your service.

35Design Patterns for Keeping Overhead Under Control

A few repeatable patterns make the whole subject easier to live with.

Pattern

The overhead budget

Agree on written limits for latency, processor and memory before work begins, and treat them like any other service target.

Pattern

The overhead ladder

Measure in steps, from nothing installed to full detail, so each cost has a name and a number.

Pattern

The canary comparison

Run the instrumented version on a small share of production machines next to the old version and compare them live.

Pattern

The tiered detail level

Give each service a default light level and a switch for deeper detail that can be turned on temporarily when investigating a problem.

Pattern

The offload pipeline

Keep the application thin and let a Collector do filtering, enrichment and routing.

Pattern

The regression gate

Automate a small overhead test in the delivery pipeline so every change is checked.

Simple Analogy

A household budget, a monthly weigh-in and a trial run of a new route to work are three ordinary habits for staying within limits. These patterns are the same habits applied to telemetry.

36Real-World Scenarios

Different kinds of systems feel overhead in different places.

A high-traffic web service

A service handling thousands of requests every second watches the slowest requests closely. The team uses head sampling at a modest percentage, keeps spans to important boundaries, and moves filtering to a Collector. The main measurements are tail latency and processor cores at peak traffic.

A short-lived serverless function

Functions start often and run briefly, so start-up time and the time to flush data before the function freezes matter most. The team measures cold start with and without the library, and keeps the setup light.

A background batch processor

A job that processes millions of records has no user waiting, but total run time and memory matter. The team avoids a span per record and records one span per batch, with counters for totals, which keeps both memory and run time steady.

A small device or edge node

Devices with limited memory and weak processors cannot afford heavy libraries. The team measures memory footprint and battery or power use, and sends data in larger, less frequent batches.

A latency-critical trading or gaming path

When every millisecond counts, teams instrument the edges of the path only, and use sampling or separate paths for detailed investigation, because even small additions are visible.

37Comparing Measurement Methods and Their Trade-offs

No single method answers every question, so choose by the question you are asking.

MethodStrengthWeaknessBest used for
Micro-benchmarkPrecise cost of one operationIgnores real traffic and interactionComparing two ways to record a span or attribute
Load test in a labControlled, repeatable, fair comparisonMay not match real trafficAnswering the budget question before release
Canary in productionReal traffic and real destinationsLimited control, risk to usersConfirming lab results at small scale
Continuous monitoringCatches drift over timeHard to separate causesWatching for regressions after release
ProfilingShows where time and memory goItself changes timingExplaining a result you already found

Why combine methods

  • Lab tests give fairness.
  • Canaries give realism.
  • Profilers give explanations.
  • Monitoring gives memory over time.

Risk of using only one

  • Only micro-benchmarks miss real interaction.
  • Only production tests risk users.
  • Only profilers distort what they observe.
  • Only monitoring cannot isolate a cause.

38Troubleshooting Unexpected Results

When numbers surprise you, the cause is usually one of a small set of familiar things.

SymptomLikely causeWhat to check
Instrumented run is faster than baselineNoise, warm-up differences or machine differencesRepeat runs, interleave them, extend warm-up
Slow tail grows but average is steadyFull queues or garbage collection pausesQueue metrics and memory behaviour
Memory keeps risingToo many attributes, unbounded metric dimensions or stuck buffersAttribute limits and series counts
Processor use jumps at high loadPer-request work grows with span countSpans per request and sampling rate
Data is missing at the destinationDropped data when queues overflowDrop counters in the SDK and Collector
Results differ between languagesDifferent SDK designs and defaultsMeasure each language separately
Start-up much slowerHeavy auto-instrumentation or loading many librariesDisable unused instrumentation, measure start-up alone
i
Change one thing, then look again

When debugging a surprise, resist fixing three things at once. One change and one measurement at a time is slower per step but faster overall, because you always know what caused what.

39Maturity, Ecosystem and What to Verify

OpenTelemetry gives guidance, but you remain responsible for your own numbers.

The project publishes specification material about performance and benchmarking, and the individual language projects often keep their own benchmark results and suggestions. These are helpful starting points, yet they come from the maintainers’ own machines, workloads and versions. They cannot tell you how your service behaves.

Remember too that implementations differ. A setting that exists in one language may be named differently, or may not exist, in another. Defaults also change from release to release. Whenever you upgrade a library, treat the previous measurements as out of date until you repeat them. Check the current documentation for your language before relying on any specific setting described in a general guide.

!
Verify before you rely

Published benchmark numbers are context, not conclusions. Use them to know what order of magnitude to expect, then confirm with your own measurements.

“Trust the guidance; verify the numbers.”

40Frequently Asked Questions

Q1What is instrumentation overhead?

It is the extra time, processor work, memory, network traffic and start-up cost that your service pays because it creates and sends telemetry data.

Q2Is there a standard acceptable percentage?

No. The right limit depends on your service targets and business. Set your own budget in advance, then test against it.

Q3Does simply installing the library slow my service?

Usually very little, but check. The disabled-library step on the ladder tells you exactly how much.

Q4Which costs the most?

Typically creating many spans, attaching many attributes and exporting everything. Reducing the volume through sampling and careful choice of what to record usually helps most.

Q5Does sampling remove overhead completely?

No. Unsampled requests may still pay a small decision cost. Sampling mostly removes the larger cost of recording and exporting the data.

Q6Why measure percentiles and not averages?

Averages hide the slowest requests, which are the ones users notice and where queue and pause problems appear first.

Q7Can I measure overhead in production?

Yes, carefully, using a canary that compares an instrumented share of machines with an uninstrumented share, ideally after lab tests have passed.

Q8Why do results differ between languages?

Each language library is built differently, with its own runtime, memory model and defaults, so each needs its own measurement.

Q9Does the Collector add overhead too?

Yes, but in a separate process. It moves work off the application and adds its own resource cost and network hop, which you should measure and plan for.

Q10How often should I measure?

Before the first rollout, after each major upgrade or instrumentation change, and ideally automatically on every change using a small regression check.

Q11Are published benchmark results enough?

They are a useful guide to what is typical, but your workload, hardware and settings are different, so measure your own service.

Q12What if the budget is exceeded?

Work down the list of levers: sample less, remove low-value spans, limit attributes and dimensions, batch exports, and offload work to a Collector. Then measure again.

41Why Each Programming Language Behaves Differently

The same idea of a span costs different amounts in different languages, because the languages themselves work differently.

Some languages run inside a managed runtime that cleans up unused memory automatically through a process called garbage collection. Every span, attribute and event is a small object. Many small objects make the cleaner work harder, and the cleaner can briefly pause the program. In such languages, overhead often appears as extra pauses and a fatter slow tail.

Other languages manage memory directly, or through ownership rules. There the cost shows up more as processor work and copying, and less as pauses. Interpreted languages add their own cost, since each instruction takes longer to run, and instrumentation code is instructions too. Languages that run many tasks at once may lose time when instrumentation uses locks that tasks must queue for.

Simple Analogy

Carrying a parcel by bicycle, by car and by train costs different things. The bicycle rider tires, the car burns fuel and the train needs a timetable. The parcel is the same, but the price is paid in a different currency each time. Overhead has different currencies in different languages.

Runtime styleWhere overhead often showsWhat to watch
Managed with garbage collectionPauses and slow tailCollection frequency and pause length
Direct memory controlProcessor work and copyingProcessor cores and allocation counts
InterpretedSlower per-instruction costThroughput and processor use
Highly concurrentWaiting on shared resourcesLock contention and queue depth
i
Practical consequence

If your organisation uses several languages, run the experiment for each one separately. Never copy a result from one language to another.

42Turning Overhead Into Capacity and Cost Plans

Overhead is not only a technical number; it becomes machines, bills and headroom.

Suppose full tracing adds eighteen percent processor use to a service. If the service runs on one hundred machines, the fleet needs roughly eighteen more machines to carry the same traffic at the same comfort level. That cost is real, and it belongs in the plan next to the cost of storing and viewing the data.

Capacity planning therefore asks three questions. How much extra resource does instrumentation need per unit of traffic? How much headroom do we keep so that a sudden spike does not push the service past its limit? And how will the overhead change as traffic and detail grow? Answering them early avoids unpleasant surprises, such as a service that was comfortable on Monday and struggling on Friday because a new release added many spans.

1

Measure cost per request

Use the lab experiment to find the extra processor time and memory for each request at your chosen detail level.

2

Multiply by expected traffic

Combine the per-request cost with your peak traffic forecast to see the total extra load.

3

Add headroom

Keep a margin for spikes, failures and growth, rather than planning to run exactly at the limit.

4

Include downstream cost

Remember the Collector machines, network traffic and storage for the telemetry itself.

“Every span has a price; the bill arrives as machines.”

43A Simple Roadmap for Your Own Team

You do not need to do everything at once. Grow the habit in small, sensible steps.

Stage 1

First look

Run a basic baseline versus instrumented test on one important service and write down the result and the budget.

Stage 2

Repeatable method

Adopt the ladder, warm-up, repeats and percentiles as the standard way your team measures.

Stage 3

Automation

Add a small regression check to the delivery pipeline so that every change is tested.

Stage 4

Production confirmation

Use canaries and live dashboards to confirm that lab results hold on real traffic.

Stage 5

Shared knowledge

Keep a results diary and shared guidance, so new engineers inherit the lessons instead of repeating the mistakes.

Simple Analogy

People learn fitness the same way. First they check their weight once, then they weigh themselves regularly, then they follow a plan, and finally they teach others. Each stage builds on the one before it.

i
Start with your most important service

A single careful measurement on the service that matters most teaches more than a shallow survey of fifty services.

44Measuring the Collector and the Path After the Application

Overhead does not stop at the edge of your application; the data still has to travel and be handled.

Many teams place a Collector between applications and the final destination. This moves heavy work out of the application, but it does not make the work disappear. The Collector uses processor time to receive, change and forward data, memory to hold queues, and network bandwidth in both directions. If you measure only the application, you will believe the cost is smaller than it truly is.

To measure the pipeline fairly, give the Collector the same treatment as the application. Run it on machines like those you will use in production. Send it realistic volumes of traces, metrics and logs. Record processor use, memory, queue depth, the number of items dropped, and the delay between arrival and delivery. Then raise the volume step by step until you find the point where it starts to fall behind. That point, with a safety margin, tells you how many Collector instances you need.

Watch

Queue depth

A queue that keeps growing means data arrives faster than it leaves.

Watch

Dropped items

Any drops mean visibility is being lost, so treat them as a serious signal.

Watch

Delivery delay

Long delays make data stale, which hurts live troubleshooting.

Watch

Resource use

Processor and memory trends show how close each instance is to its limit.

Simple Analogy

A post office sorting centre sits between the houses that send letters and the places that receive them. Houses may post their letters quickly, but if the centre is too small, letters pile up and arrive late. You must size the centre as carefully as you pack the letters.

!
Do not forget the network

Moving data between machines, and especially between zones or regions, can carry a cost and a delay. Include these in your plan, not only the processor and memory figures.

45Summary and Key Takeaways

Measuring overhead is a calm, repeatable habit, not a one-time scare.

You began with a doctor who must not disturb the patient while checking their health. You learned that instrumentation costs time, processor work, memory, network traffic and start-up speed, and that these costs come mainly from creating, enriching and exporting data. You learned to set a budget, build a fair experiment, climb an overhead ladder, use a realistic workload, and read percentiles rather than averages. You saw how sampling, careful instrumentation, bounded attributes and batching reduce cost, and how pipeline checks catch slow creeping increases.

Remember that overhead is a trade, not a sin. Telemetry buys you the ability to see failures, slow requests and strange behaviour that would otherwise stay hidden. The goal is never zero overhead; the goal is a known, agreed and affordable overhead that gives your team clear insight. When the cost is measured honestly and kept within a written budget, the debate stops being a matter of opinion and becomes a matter of evidence that anyone can inspect, repeat and improve over time.

Finally, share what you learn. A short note describing your budget, your method and your latest numbers helps neighbouring teams avoid repeating your mistakes, and invites them to correct yours.

5
Cost dimensions to watch
1
Change per step on the ladder
3+
Repeats of every run
p99
Tail latency matters most

Key Takeaways

  • Decide your overhead budget before you measure, and write it down.
  • Measure on a ladder, changing one thing at a time, from nothing installed to full detail.
  • Use a realistic workload and a production-like destination, and warm up before recording.
  • Look at percentiles, processor use, memory, garbage collection and network, not only the average.
  • Reduce overhead by the biggest levers first: sampling, fewer spans, limited attributes and dimensions, then batching.
  • Automate a small regression check so overhead cannot creep up unnoticed.
  • Treat published numbers as hints, and always verify with your own service, language and versions.