The Aggregate Report in JMeter: Turning Thousands of Samples into One Honest Table
A deep, plain-English guide to the listener most testers open first, covering what every column really means, how the numbers are produced, why the TOTAL row and the average can mislead, and how to read the table like an experienced performance engineer.
Imagine a school that has just held an exam for two thousand students. The headmaster does not want to read two thousand individual answer sheets. She wants a summary: how many students sat the paper, what the typical mark was, how the weakest tenth did, how many failed outright. A good summary compresses a mountain of detail into a few numbers she can act on, and a bad summary hides the very students she most needs to worry about.
A JMeter test produces its own mountain: every single request from every virtual user becomes one recorded sample. The Aggregate Report is the summary table that compresses that mountain into one row per kind of request. It is simple to add and quick to read, which is exactly why it is so easy to misread. This guide explains it from zero knowledge, shows how it works inside, and teaches you what each number truly says and what it quietly hides.
1What the Aggregate Report Is and Why It Exists
We begin with the problem it solves, because the table only makes sense once the problem is clear.
The raw data problem
During a load test, JMeter records a sample for every request: when it started, how long it took, whether it worked, how many bytes moved. A ten-minute test with a few hundred users can easily produce hundreds of thousands of such records. No human can read them line by line, and looking at a handful of them tells you nothing about the whole.
The solution in one sentence
The Aggregate Report is a listener, an element that collects results, which groups all samples by their label, the name of the request, and calculates statistics for each group: how many there were, the average and median time, the slow tail, the error share and the rate at which they were completed.
Why it is so widely used
It answers the questions people ask first. Is it fast enough? Is anything failing? Which requests are the slowest? How much traffic did we actually push through? Because it shows these answers in a single compact table that updates as the test runs, it became the default window into a test for generations of testers.
Think of a restaurant manager at the end of an evening. She does not read every order ticket. She looks at one sheet showing, for each dish, how many were sold, how long the kitchen usually took, how long the slowest tenth took and how many were sent back. The Aggregate Report is that sheet for your application.
2Core Concepts You Need First
A handful of terms appear throughout this guide, and learning them now makes everything easier.
Sample
The record of one request: its label, start time, elapsed time, response code, success flag and size.
Label
The name of a sampler. The report groups samples that share a label into one row.
Listener
An element that receives samples and does something with them, such as showing a table, drawing a graph or writing a file.
Elapsed Time
How long a request took from just before it was sent until its response was fully received, measured in milliseconds.
Percentile
A value below which a stated share of samples fall. The ninety-fifth percentile is the time that ninety-five out of a hundred requests did not exceed.
Throughput
How many requests were completed per unit of time. It measures how much work the system actually did.
JTL File
The results log in which samples are saved, usually as comma-separated text. A listener can read one back for later analysis.
Steady State
The part of a test when all users are active and the system has settled, which is where most conclusions should be drawn.
The Aggregate Report never changes what happens during a test. It only observes and summarises. All its numbers are derived from the samples that the samplers produce.
3Where It Fits Among the Result Listeners
JMeter offers many ways to look at results, and each answers a slightly different question.
| Listener or Tool | What It Shows | Best For |
|---|---|---|
| View Results Tree | Every request and response in detail | Debugging a single user |
| Aggregate Report | Per-label statistics with median and percentiles | Quick, readable summary of a run |
| Summary Report | Per-label statistics with standard deviation and average size | Lighter summary with spread information |
| Aggregate Graph | The aggregate figures drawn as a chart | Visual comparison across requests |
| Simple Data Writer | Nothing on screen; writes samples to a file | Low-overhead recording during heavy load |
| Backend Listener | Summaries streamed to an external dashboard | Live monitoring of long tests |
| HTML Dashboard Report | Linked pages with charts and tables built from a results file | Final, shareable report |
flowchart LR
S[Samplers produce samples] --> L{Listeners}
L --> A[Aggregate Report table]
L --> T[View Results Tree]
L --> W[Simple Data Writer to JTL file]
W --> H[HTML Dashboard Report]
W --> A2[Aggregate Report reading file later]
The Aggregate Report can work in real time or from a file written earlier. This second use, reading a saved file after a command-line run, is one of its most valuable roles.
4How It Works Inside
The table looks like magic until you see the simple bookkeeping behind it.
Samples arrive one by one
Each time a sampler finishes, JMeter creates a result object and offers it to every listener in scope. The Aggregate Report receives it and looks at its label. If the label is new, a new row is created; otherwise the sample is added to the existing row. At the same time a running total row is updated with every sample regardless of label.
What each row remembers
To compute counts and averages, a row needs only running sums. To compute the median and the percentile lines, it needs more: it must keep the individual elapsed times so it can find the value at a given position when they are sorted. This is why the Aggregate Report uses more memory than a simple counter, and why very long tests can make it heavy.
The table refreshes on a timer
Redrawing a table after every sample would swamp the machine, so the screen refreshes at intervals, while the underlying statistics are always up to date. In the graphical interface this redraw also costs processor time, one of the reasons real load tests should not rely on watching listeners.
sequenceDiagram
participant V as Virtual User
participant S as Sampler
participant L as Aggregate Report
participant R as Row for the label
participant T as TOTAL row
V->>S: Run request
S-->>L: Finished sample with label and time
L->>R: Find or create row, add sample
L->>T: Add sample to overall totals
L-->>L: Refresh screen at intervals
Picture a teacher marking papers and dropping each score into a pile for that subject. To report the average she only needs a running sum. To report the mark that separates the bottom tenth from the rest, she must keep every score so she can line them up. The Aggregate Report keeps the scores.
Believing the Aggregate Report is free to leave enabled. Because it keeps individual times, it consumes memory proportional to the number of samples, and in the graphical interface its screen updates consume processor time too.
5Adding It and Reading the Screen
Setting it up takes a minute, and knowing the controls saves confusion later.
Add the listener
Right-click the test plan or a thread group, choose to add a listener and pick Aggregate Report. Placing it at the plan level collects results from every sampler.
Name it clearly
If you use several, give each a name that says what it covers, such as the whole test or one thread group.
Choose what to record
Checkboxes let the listener show only errors or only successes, which is useful when diagnosing failures separately from healthy traffic.
Optionally write to a file
A filename box lets the listener save raw samples to a results file. The same box can be used to load a saved file into the table.
Run and watch
Start the test and watch rows appear as each label produces its first sample.
Save the table
A button saves the displayed table as text, optionally with a header line, ready to open in a spreadsheet. A clear-all command resets the table between runs.
Always clear the results before a new run. Old samples left in the table silently blend with new ones, and the averages then describe a mixture of two tests.
6The Columns Explained One by One
Every column answers one specific question, and knowing the question prevents misuse.
Label
The name of the request the row describes. The last row, named TOTAL, combines everything.
# Samples
How many times this request completed. It tells you how much evidence stands behind the other numbers in the row.
Average
The arithmetic mean of elapsed times in milliseconds. Easy to understand but pulled upward by slow outliers.
Median
The middle value: half the samples were faster and half slower. A truer picture of the typical request.
90% Line
The time that ninety percent of samples did not exceed. One in ten requests was slower than this.
95% Line
The time that ninety-five percent did not exceed. A common threshold in agreements about response time.
99% Line
The time that ninety-nine percent did not exceed. It exposes the rare slow experiences that annoy real users.
Min
The fastest sample. Often unrepresentative, since it may be a cached or trivial case.
Max
The slowest sample. A single pause anywhere in the chain can set it, so treat it as a clue, not a verdict.
Error %
The share of samples that failed, whether through a bad response code, a failed assertion or a timeout.
Throughput
How many requests were completed per second, minute or hour, with the unit chosen automatically.
Received and Sent KB/sec
The rate of data coming back from and going to the server, useful for spotting network limits and heavy pages.
All response times in the table are in milliseconds. Throughput switches between per second, per minute and per hour depending on its size, so read the unit shown before comparing runs.
7Average, Median and Percentiles: Why One Number Is Never Enough
The three families of statistics describe different parts of the same crowd of samples.
The average hides the tail
Imagine ninety-five requests taking one second and five taking thirty seconds. The average comes to a little over two and a half seconds, a figure that matches neither the happy majority nor the miserable minority. The average blends them into a number nobody actually experienced.
The median shows the typical case
The median ignores how extreme the slow ones are and only asks what the middle sample looked like. In the example above it is one second, which truly describes what most users saw. Comparing the median with the average is a quick test of skew: when the average is much larger than the median, a slow tail is dragging it up.
Percentiles describe the experience of the unlucky
The ninety-fifth percentile asks how slow the unluckiest five percent were. In the example it is thirty seconds, exposing exactly what the average hid. Service agreements are often written in percentiles, because customers remember the slow experiences, not the average ones.
Think of the waiting time at a railway ticket window. The average might be four minutes, but if one passenger in twenty waits twenty minutes, those passengers tell their friends, and the station’s reputation follows them, not the average.
| Statistic | Answers the Question | Weakness |
|---|---|---|
| Average | What is the overall mean time? | Distorted by slow outliers |
| Median | What did a typical request take? | Says nothing about the slow tail |
| 90th percentile | How slow were the unluckiest tenth? | Misses rarer problems |
| 95th percentile | How slow were the unluckiest twentieth? | Needs enough samples to be stable |
| 99th percentile | How slow were the unluckiest hundredth? | Very sensitive with small sample counts |
| Maximum | What was the single worst case? | One-off events dominate it |
8Percentile Lines and How to Configure Them
The ninety, ninety-five and ninety-nine lines are defaults, not laws of nature.
Where the values come from
The three percentile columns are controlled by settings in JMeter’s main property file. By changing those settings you can ask for different percentile lines, such as the ninety-eighth or the ninety-ninth and a half, so that the table matches the percentiles named in your own performance agreement. The change takes effect when JMeter is restarted, so decide before the test, not during it.
Match the table to the contract
If your organisation promises that ninety-five percent of requests will complete within two seconds, make sure the ninety-fifth line is present and treat it as the headline figure. Reporting a different percentile because it is the default invites arguments about whether the promise was kept.
How many samples a percentile needs
A high percentile is only meaningful when there are enough samples to populate its tail. The ninety-ninth percentile of two hundred samples is decided by the two slowest requests; a single hiccup changes it dramatically. As a rough habit, treat high percentiles from rows with only a few hundred samples as indicative rather than definitive, and repeat the test to see whether they hold.
Agree the Percentile First
Before testing, settle which percentile and which time limit define success, and configure the table to show it.
Report Several
Show the median and at least two percentiles so readers see both the typical and the unlucky experience.
Check Sample Counts
Read the sample count in every row before trusting a high percentile in that row.
Repeat the Run
Compare percentiles across repeated runs to learn how stable they are in your environment.
Changing percentile settings after a run and then comparing the table with an older report that used the defaults. The columns carry the same headings but describe different percentiles.
9Error Percentage in Depth
A fast system that returns errors is not fast, so this column deserves as much attention as any time.
What counts as an error
A sample is counted as failed when its success flag is false. For web requests, that usually means a response code outside the successful range, a connection problem, a timeout, or a failed assertion that you added. Other protocols define failure in their own ways, such as a database error or an unexpected reply on a socket.
Why errors can be hidden
Without assertions, a server that returns an error page with a normal-looking code is counted as a success. The error column then shows zero even though every user saw a broken page. The reverse also happens: a strict assertion that is slightly wrong marks healthy responses as failures. The error column is only as honest as the checks behind it.
Fast failures flatter the averages
When a server is overloaded it often answers errors very quickly, because refusing a request is cheaper than serving it. Those fast failures pull the average and the median down, so a system in serious trouble can look faster than a healthy one. Always read response times together with the error share.
flowchart TD
A[System under rising load] --> B{Overloaded?}
B -- No --> C[Responses succeed, times grow gently]
B -- Yes --> D[Server rejects requests quickly]
D --> E[Error percentage rises]
D --> F[Average time may fall]
F --> G[Misleadingly good timing numbers]
E --> H[Read time and errors together]
Use the listener’s option to show only errors, or only successes. Looking at the timing of successes separately gives a far truer view of how the system behaves for the users it is actually serving.
10Throughput in Depth and the Link to Users
Throughput is the most misunderstood column, because it depends on how you built the test as much as on the system.
What it measures
Throughput is the number of requests completed divided by the time over which they were completed, shown per second, per minute or per hour depending on size. It tells you how much work the system actually did during the test window.
It depends on the test design
In JMeter, virtual users wait for each response before sending the next request, and timers add pauses between actions. The rate at which requests are sent therefore depends on the number of users, the response times and the think times together. A slow system produces lower throughput simply because each user spends longer waiting.
A simple law connects them
For a steady test, the number of active users is roughly the throughput multiplied by the time each user spends per cycle, including response time and think time. If one hundred users each complete a cycle in five seconds, the system handles about twenty cycles per second. If response times double and think times stay fixed, throughput falls even though nothing is wrong with the load generator.
flowchart LR
U[Number of users] --> T[Throughput]
R[Response time] --> T
K[Think time] --> T
T --> S[Requests per second the system handled]
Treating throughput as a target the system must “reach”. It is a result of your design. To test a required rate, build the test to aim for that rate and check whether the system keeps up.
11Received and Sent Data Rates
Two small columns carry large clues about the network and about the weight of each page.
What they show
The received column shows how many kilobytes per second came back from the server, and the sent column shows how many went out. Both include headers as well as bodies. They are calculated over the same time window as throughput, so they rise and fall with it.
What they reveal
A sudden drop in received data with a rising error share often means the server has started returning short error messages instead of full pages. A very high received rate may mean the load generator’s own network link is becoming the limit, so response times start to include delays that belong to the generator. A high sent rate with large uploads can show where file or form submissions dominate the traffic.
Spotting heavy pages
Compare received rates across labels. A request whose row shows a rate far above the others is carrying a large response, a prime candidate for compression or caching work.
Spotting a saturated generator
If the received rate flattens at a suspiciously round number close to the generator’s network capacity while response times keep climbing, the generator, not the application, may be the bottleneck.
Confirming content is real
When errors are fast and small, the received data per request falls. A sudden fall in bytes per request is therefore a useful extra signal that responses have changed character.
Compare the received rate with the network monitor on the generator. If both agree, the numbers are trustworthy; if they disagree, investigate before drawing conclusions.
12The TOTAL Row Trap
The last row looks like the verdict on the whole test, and that is exactly why it misleads.
What the TOTAL row is
The final row combines every sample from every label into one set of statistics. Its average is the mean of all requests together, and its percentiles are calculated across the whole mixed pool. It is convenient, and it is often quoted in summaries as if it described the system as a whole.
Why mixing hides the truth
Different requests have very different natures. A light request that checks status may take twenty milliseconds, while a report generation may take eight seconds. If the light request is called a hundred times more often, it dominates the pool, and the total average looks excellent even though the heavy request is painfully slow. The reverse also happens. The total row describes your particular mix of traffic, not a property of the application.
Percentiles of a mixture are fragile
The ninety-fifth percentile of a mixed pool can fall entirely within one kind of request. If most requests are quick, the slow ones might sit above the ninety-fifth line, invisible in that figure. A change in the traffic mix alone, with no change in the application, can move the total percentiles noticeably.
Imagine averaging the cooking time of every item a restaurant serves, from toast to slow-roasted lamb. A single figure emerges, but it describes neither the toast nor the lamb. Customers waiting for lamb get no comfort from the news that the overall average is eight minutes.
Problem
A report states that the system meets its target because the TOTAL row shows an acceptable average and ninety-fifth percentile.
Why It’s Harmful
Fast, frequent requests dominate the pool and hide slow, important ones. The headline passes while a key business action fails its target.
Correct Approach
Judge each business-critical request on its own row, against its own target, and treat the total as background context only.
13Labels, Transactions and Double Counting
Rows are only as meaningful as the labels that create them, and some elements add extra samples you may not expect.
Stable, descriptive labels
Each distinct sampler name becomes a row. Use names that describe the business action, such as opening the home page or adding an item to a basket, not technical details or changing values. A label that includes an identifier unique to each request produces a row per request and makes the table useless.
Transaction controllers and their two modes
A transaction controller groups several requests into one timed business step. Depending on its setting, it either generates a single parent sample and hides the children from listeners, or it produces an additional sample on top of the children, which remain visible. In the second mode the table contains both the individual requests and the combined step, and the total row counts them all.
Why this distorts totals
When children and the parent both appear, the total sample count and throughput include the same work twice, once as separate requests and once as the combined step. Averages in the total row then mix very short and much longer samples. Be clear which mode you chose, and read the total row accordingly.
| Setting | What Appears in the Table | Effect on Totals |
|---|---|---|
| Parent sample generated, children hidden | One row for the business step | Counts the step once; request-level detail is lost |
| Extra parent sample, children visible | Rows for every request plus one for the step | Work is counted twice in the total |
| No transaction controller | One row per request label | Straightforward; no business-level timing |
Use the parent-sample mode when you care about business-step timing, and a separate listener for request detail if needed. Add a naming prefix to the step so readers recognise it.
Comparing the throughput of a run with transaction samples visible to a run without them. The same traffic produces different sample counts, so the throughput figures are not comparable.
14Ramp-Up, Warm-Up and Steady State
The table summarises the entire run, but the interesting behaviour lives in one part of it.
Three phases in every test
At the beginning, users start gradually during the ramp-up, and the system is lightly loaded, with empty caches and unprepared connections. In the middle comes the steady state, when all users are active and behaviour has settled. At the end the users finish and the load falls away. The Aggregate Report blends all three phases into one set of numbers.
Why blending misleads
Ramp-up samples were taken under light load, so they pull averages down and make the system look healthier than it is at full load. Warm-up samples may be unusually slow because nothing is cached yet, pulling the maximum and the high percentiles up. Ramp-down samples may be distorted by users stopping at different moments.
flowchart LR
A[Ramp-up: light and growing load] --> B[Warm-up effects: cold caches, new connections]
B --> C[Steady state: full load, stable behaviour]
C --> D[Ramp-down: users finishing]
C --> E[Best period for conclusions]
What you can do
Long Steady State
Design the test so the steady state is much longer than the ramp-up, which dilutes the effect of the early samples.
Separate Warm-Up Run
Run a short warm-up before the measured test and discard its results.
Filter by Time
Use a report generator that can limit analysis to a time window, so only the steady state is summarised. Check the settings your version supports.
Note the Phases
At minimum, record when each phase began and ended, so readers understand what the numbers include.
Running a five-minute test with a four-minute ramp-up and quoting the table. Almost every sample was taken while the load was still growing, so the table describes a system that was never fully loaded.
15Sample Size and Statistical Stability
A number from a handful of samples is a rumour; a number from many thousands is evidence.
Why counts matter
Every statistic in a row is an estimate drawn from that row’s samples. The more samples there are, the less a single odd value can move the result. A row with forty samples can swing wildly between runs, while a row with forty thousand barely moves.
Rare operations
Some actions happen far less often than others, such as a sign-in performed once per user or a rarely used report. Their rows have few samples, and their percentiles are correspondingly shaky. Do not draw firm conclusions from such rows without extending the test or isolating that action in its own scenario.
| Samples in the Row | How to Treat the Numbers |
|---|---|
| Fewer than one hundred | Indicative only; averages and medians are rough, percentiles unreliable |
| A few hundred | Average and median reasonable; ninety-ninth percentile still fragile |
| A few thousand | Most statistics stable; repeat runs to confirm |
| Tens of thousands or more | Stable estimates; differences between runs usually reflect real changes |
Run the same test three times and compare the rows. The spread between runs is the natural noise of your environment. A difference between two builds smaller than that noise proves nothing.
16Saving, Loading and Exporting Results
Results are only useful if you can keep them, share them and look at them again later.
Write results to a file
The listener has a filename box. Entering a path there makes the listener write every sample it receives to that file, in the format and with the fields you choose through its configuration button. This produces the results log that other tools, including the dashboard generator, can read.
Read results from a file
The same box can load a previously saved file into the table. This is how you analyse a command-line run in the graphical interface afterwards: open the listener in a fresh plan, point it at the file and browse the rows. For very large files this loads many samples into memory, so allow enough memory or use the dashboard generator instead.
Save the table itself
A separate button saves the displayed table, with an option to include a header line. The result is a small text file with one line per label, ready to open in a spreadsheet, paste into a report or compare with a previous run.
Choose the fields to save
Use the configure button to include timing, label, response code, success flag and sizes, and avoid saving response bodies except where needed.
Name files with run details
Include the date, time and build number in filenames so no run overwrites another.
Keep the raw file
The table can be rebuilt from the results file, but the file cannot be rebuilt from the table. Always archive the raw log.
Export the table for reports
Save table data with a header and open it in a spreadsheet for comparison and charts.
Archiving only the exported table and discarding the raw file. Later, when someone asks about a specific time window or request, the information to answer is gone.
17Memory, Performance and the GUI Question
A listener that helps you watch can also hurt the very test you are watching.
The cost of watching
In the graphical interface the Aggregate Report keeps individual times in memory and redraws its table regularly. Under heavy load with many users and a long duration, both costs grow. Garbage collection pauses lengthen, the processor spends time painting the screen instead of driving users, and the response times you record may include delays caused by the tool itself.
The command-line alternative
In non-GUI mode there is no screen to refresh, so the display cost disappears, but the listener’s memory cost remains if it stays in the plan. For large runs, the better design is a lightweight writer that saves samples to a file, with analysis done afterwards using the table or the dashboard generator.
| Situation | Recommended Approach | Reason |
|---|---|---|
| Debugging with one or a few users | Aggregate Report in the GUI | Overhead is negligible and feedback is instant |
| Medium run in the GUI | Aggregate Report plus a results file | Convenient, but watch memory |
| Large or long run | Command line with a simple file writer | Lowest overhead and trustworthy timing |
| Analysing a finished large run | Dashboard generator or table loaded from file | Avoids rendering during the test |
If you must read a very large results file into the table, give JMeter a larger memory allowance first, and close other heavy listeners, so the analysis itself does not fail.
18A Practical Workflow: Run Headless, Analyse Afterwards
The healthiest habit separates measuring from looking.
flowchart LR
A[Build and debug plan in GUI] --> B[Remove heavy listeners]
B --> C[Run in command-line mode with file writer]
C --> D[Raw results file]
D --> E[Aggregate Report loaded from file]
D --> F[HTML dashboard report]
E --> G[Compare rows with targets]
F --> G
G --> H[Archive raw file, table export and notes]
Prove the plan with a few users
Use the Aggregate Report and a detailed viewer to confirm each request works and each label looks right.
Strip the viewers
Disable or remove visual listeners and keep only a simple file writer for the real run.
Run from the command line
Write results to a uniquely named file and, if you like, ask for the dashboard at the end.
Load the file into the table
Open a clean plan with an Aggregate Report, point it at the results file and read the rows.
Judge per request
Compare each important row against its own target, using the steady-state period where possible.
Record and archive
Save the raw file, the exported table and a short note of settings and observations.
19Aggregate Report Versus Its Neighbours
Several tools show similar numbers, and knowing the differences avoids choosing the wrong one.
| Tool | Strengths | Limits |
|---|---|---|
| Aggregate Report | Median and percentile lines in a compact table; can read and write files | No standard deviation; keeps individual times in memory |
| Summary Report | Includes standard deviation and average response size; lighter on memory | No median or percentile columns |
| Aggregate Graph | Same statistics drawn as charts for visual comparison | Charts are static snapshots, less convenient for reading exact values |
| HTML Dashboard Report | Charts over time, percentiles, error breakdown and a shareable format | Built after the run from a suitable results file |
| Community synthesis-style reports | Combine many columns, including spread and sizes, in one table | Require a plugin and extra setup on every machine |
Choosing among them
Use the Aggregate Report when you want a quick readable table with percentiles during development or when analysing a finished run. Use the Summary Report when you want a light listener and a measure of spread. Use the dashboard when the results must be shared, compared over time or presented with charts. Many teams use the table for fast checks and the dashboard for the official record.
The standard deviation in the summary report tells you how spread out the times are. A large standard deviation next to a modest average is another way to detect a slow tail without reading percentiles.
20Reading the Patterns: Healthy Versus Troubled Tables
Experienced engineers read the shape of the table, not just individual numbers.
| What You See | Likely Meaning | What to Check Next |
|---|---|---|
| Median close to average, percentiles close too | Consistent, well-behaved responses | Confirm errors are near zero |
| Average far above median | A slow tail pulls the mean up | Look at the ninety-ninth percentile and maximum |
| Very high maximum, normal percentiles | Rare one-off pauses | Check garbage collection and network events |
| Rising error percentage with falling average | Overload producing fast failures | Look at error types and server resources |
| Throughput flat while times grow | The system has reached saturation | Find the resource that is exhausted |
| One row far slower than the rest | A specific operation is the bottleneck | Trace that request through the backend |
| Received rate unexpectedly low | Responses may be errors or empty | Add or tighten assertions |
| Results differ widely between identical runs | Unstable environment or too few samples | Lengthen the run and repeat |
Reading the table is like a doctor reading a set of vital signs together. A slightly raised pulse alone means little, but a raised pulse with a fever and low blood pressure tells a story. The numbers matter in combination.
Focusing on a single column, usually the average, and ignoring the others. Every pattern in the table above needs at least two columns to recognise.
21Turning Numbers into Pass or Fail
A table informs; a decision needs agreed thresholds.
Agree targets before the test
Settle in advance which requests matter, which statistic judges each, and what limit means success. A typical rule reads: for each critical request, the ninety-fifth percentile must stay below a named time and the error share below a named percentage. Without prior agreement, every result becomes an argument.
Judge each request against its own target
A search might need to answer in one second, while a report may be allowed ten. Setting one limit for the whole table either blocks reasonable slow operations or lets important fast ones slip. Give each critical row its own limit.
Automate the verdict
For pipelines, a script can read the saved table or results file and compare the figures against the agreed limits, failing the stage when any is exceeded. Keep the limits in a version-controlled file so reviewers can see when and why they changed.
Percentile Limit
A time that the chosen percentile must not exceed for each critical request.
Error Limit
A maximum error percentage, often very small for important actions and zero for payments.
Throughput Floor
A minimum rate the system must sustain, checked only when the test is designed to aim for that rate.
Minimum Sample Count
A required number of samples per row, so verdicts are never based on a handful of requests.
Compare each run against a baseline run of the previous release as well as against absolute limits. A slow drift that stays within limits is still worth noticing early.
22Hidden Delay: Waiting Users and Missing Samples
A subtle effect of how JMeter users behave can make a struggling system look better than it is.
Users wait for answers
A JMeter virtual user sends a request and waits for the response before doing anything else. If the system stalls for twenty seconds, that user produces no samples during the stall. A real crowd, arriving on its own schedule, would have kept arriving, and many more people would have experienced the stall.
What this does to the table
During a stall, fewer samples are recorded, and only the few that were already waiting show the long delay. The percentile lines are then calculated from a pool in which the slow period is under-represented. The table can understate how bad the stall was for the people who would have arrived during it.
Ways to reduce the effect
Model arrival rates, not only users
Where real traffic arrives at a steady rate regardless of response time, use elements that control the rate of requests, so the test keeps arriving during a stall.
Watch the time-based charts
A chart of response time over time, and of requests per second over time, shows stalls clearly even when the summary table smooths them away.
Always inspect the maximum and the error column
Very large maxima, timeouts and rising errors are the visible fingerprints of a stall that the percentiles may hide.
Believing the percentiles in the table fully describe what a real crowd would have experienced. They describe what your virtual users experienced, which is shaped by how they were told to wait.
23Behaviour in Distributed Tests
When several machines generate load, the table is built in a different place from where the samples are born.
Samples travel to the controller
In a distributed test, each worker runs its own threads and sends its samples back to the controller, where the listener summarises them. The sample counts in each row are the sum over all workers, and throughput describes the combined traffic. This is exactly what you want, but it comes with costs.
The controller can become the bottleneck
Receiving every sample from many workers takes processing and network capacity on the controller. A heavy listener on the controller, with its memory and screen updates, can slow the whole arrangement. For big distributed runs, let each worker write its own results file and merge the files afterwards, or use a lighter listener, and reserve rich analysis for after the test.
Clock and configuration differences
Samples carry time stamps from the machine that produced them. If worker clocks differ noticeably, time-based analysis such as throughput windows can be distorted. Keep worker clocks synchronised and confirm that every worker used identical settings, because a single misconfigured worker can quietly change a row’s numbers.
flowchart LR
W1[Worker 1 samples] --> C[Controller]
W2[Worker 2 samples] --> C
W3[Worker 3 samples] --> C
C --> A[Aggregate Report: combined rows]
W1 -. optional .-> F1[Worker results file]
W2 -. optional .-> F2[Worker results file]
W3 -. optional .-> F3[Worker results file]
F1 --> M[Merged file for later analysis]
F2 --> M
F3 --> M
Leaving rich listeners on the controller of a large distributed test. The controller slows, results back up, and the table reflects the controller’s struggles rather than the application’s behaviour.
24Reading the Table Next to Server Metrics
The table shows symptoms as users saw them; the servers hold the causes.
When a row slows down
Open the server dashboards for the same period. Look for rising processor use, growing queues, exhausted connection pools, long database waits or garbage collection pauses on the services behind that request.
When errors rise
Read the error types in the results and match them with server logs. Time-outs point towards saturation, refused connections towards limits, and application errors towards defects.
When throughput flattens
Find the resource that stops growing. A flat line in database connections, threads or network use usually marks the limit that the test has discovered.
When results look too good
Check that the load generator is healthy and that responses are real. A saturated generator or a cache that answers every request can both produce suspiciously fast tables.
Make the comparison easy
Record the start and end time of each test, and align the clocks of the generator and the servers, so a spike in the table can be matched to events elsewhere. Use the same time zone in all notes. A table without a time reference is hard to connect with anything else.
Agree with the owners of each backend, before the test, which metrics they will capture. Discovering afterwards that nobody recorded the database side wastes the whole run.
25Security Considerations
Results look harmless, but they can carry more than numbers.
Labels can leak information
Sampler names are copied into results and tables. A label that includes an account name, an address or a token fragment spreads that detail into every report and chat message.
Raw files hold more than the table
A results file may contain request headers, cookies, response bodies and parameters, depending on what you chose to save. Treat it as sensitive data, restrict access and delete it according to policy.
Exports travel far
Saved tables are pasted into tickets, emails and slide decks. Check them before sharing for names of internal systems or other details that should stay inside the organisation.
Do not save more than you need
Saving response bodies for every sample multiplies both the storage and the risk. Save them only for failures and only while diagnosing.
Protect shared report folders
If results are published to a shared location, review who can read them. Performance numbers can reveal capacity limits that competitors or attackers would find useful.
Posting a screenshot of the table into a public channel with real account names in the labels. Use stable, business-level labels so reports are safe to share.
26Design Patterns That Work
These patterns appear again and again in teams that read their results well.
Measure Headless, Read Afterwards
Run load from the command line with a light writer, then open the table or dashboard on the saved file.
Targets Per Request
Define success per business-critical request, with its own percentile and error limits.
Stable Business Labels
Name samplers after business actions so rows are meaningful and safe to share.
Steady-State Focus
Design long steady phases, record phase boundaries and judge results from the steady period.
Baseline Comparison
Keep a reference run for each release and compare rows against it, not only against limits.
Two Views of the Same Data
Use the table for quick checks and the dashboard for charts over time and the official record.
A good analyst is like a pilot reading several instruments before deciding on a landing. No single dial settles it, and the pilot knows which dials matter at which stage of the flight.
27More Anti-Patterns to Avoid
Besides the misleading total row, these mistakes look harmless and quietly spoil conclusions.
Problem
The team compares builds using only the average column.
Why It’s Harmful
A change that worsens the slow tail while slightly improving the average looks like progress. Users who meet the slow tail feel the opposite.
Correct Approach
Compare median and at least the ninety-fifth percentile, together with the error share and sample counts.
Problem
A large load test is run in the graphical interface with the Aggregate Report and several visual listeners enabled, so the team can watch it live.
Why It’s Harmful
Memory and processor use rise, pauses grow, and recorded timings include the tool’s own overhead. The test may even fail from memory exhaustion.
Correct Approach
Run headless with a file writer, watch server metrics live, and read the table from the saved file afterwards.
Problem
The table from a previous run is not cleared before starting the next one.
Why It’s Harmful
Old and new samples blend together. The rows describe a mixture of two tests, and nobody can say which numbers belong to which.
Correct Approach
Clear all results before each run, and write each run to its own uniquely named file.
Problem
A system is declared healthy because error percentage is zero, but no assertions check the content of responses.
Why It’s Harmful
Error pages with normal response codes count as successes. Users see broken pages while the table shows perfect health.
Correct Approach
Add assertions that verify meaningful content for every critical request, and check received data rates for sudden changes.
Problem
Two runs with different ramp-up lengths, transaction settings and traffic mixes are compared row by row as if they were identical tests.
Why It’s Harmful
Differences in the table come from the test design, not from the application, so conclusions about the build are wrong.
Correct Approach
Keep test design identical between compared runs, record every setting, and change only one thing at a time.
28Advantages, Disadvantages and Trade-offs
An honest look at benefits and costs shows when the table is the right tool.
Advantages
- Compact, readable summary with median and percentile lines
- Quick to add and understand, even for beginners
- Works live during a run or on a saved results file
- Exports easily to spreadsheets for comparison
- Configurable percentile columns to match agreements
- Shows errors, throughput and data rates in one place
Disadvantages / Trade-offs
- Keeps individual times, so memory grows with sample count
- Summarises the whole run, blending ramp-up and steady state
- TOTAL row mixes different kinds of requests
- No standard deviation or time-based charts
- Screen updates cost processor time in the graphical interface
- Can understate delay because virtual users wait for responses
The table is an excellent first look and a good companion to the dashboard, but never the only evidence. Combine it with time-based charts and server metrics before concluding.
29Best Practices Checklist
A short order of operations avoids the stumbles described in this guide.
Agree targets first
Decide which requests matter, which percentile judges them and what limits mean success.
Label carefully
Use stable business names and know how transaction controllers affect the rows.
Check the checks
Add assertions so the error column reflects real correctness, not just response codes.
Run headless for real load
Use the command line with a light writer; reserve the live table for small runs.
Focus on the steady state
Record phase boundaries and judge from the period when all users are active.
Read several columns together
Median, percentiles, errors, throughput and data rates tell the story only in combination.
Repeat, record and archive
Repeat runs to learn the noise, then save the raw file, the table export and a short note of every setting.
Declaring victory on the first run. A single run cannot show how noisy your environment is, and an apparently good result may simply be a lucky one.
30Real-World Scenarios
Stories show how the table helps, and how it can mislead, in everyday projects.
The perfect average that hid a disaster
A team reported an average of under one second and declared success. A colleague opened the ninety-ninth percentile and found one request in a hundred taking nearly half a minute, caused by a database lock. Fixing it required no change to the average at all.
The overloaded server that looked fast
During a stress test the average response time fell sharply as users were added. The error column showed a rapidly growing share of rejected requests. The server was refusing work quickly, which flattered the timing columns.
The total row that passed everything
A report passed a release because the total row met its target. The checkout row, buried among thousands of cheap status calls, was missing its target by a wide margin. Judging each critical row separately revealed it.
The ramp-up that ate the test
A short test spent most of its time ramping up, so its table described a lightly loaded system. A longer steady state exposed slow responses that the original table had diluted away.
The controller that could not keep up
A distributed run with rich listeners on the controller showed strange timings. Moving to per-worker results files and analysing afterwards gave stable, believable numbers.
The unreadable table
An analyst included each account name in the sampler labels. The table grew thousands of rows with a single sample each. Switching to stable business labels made the results readable and safe to share.
31Frequently Asked Questions
The Aggregate Report shows median and percentile columns. The Summary Report shows standard deviation and average response size but no percentiles, and it is lighter on memory.
It is the time that ninety percent of samples did not exceed. One in ten requests took longer than that value.
Yes. The three percentile values are set in JMeter’s property file, and a restart applies the change. Remember that older reports may have used different percentiles.
A slow tail is pulling the average up. Look at the high percentiles and the maximum to see how bad the slow samples are.
The number of completed requests divided by the elapsed time, shown per second, minute or hour. It is a result of your test design, not a setting.
To compute the median and percentiles it keeps individual elapsed times. Memory therefore grows with the number of samples, especially in long tests.
For large runs, no. Use a lightweight file writer and load the saved file into the table or the dashboard afterwards.
Open a plan containing an Aggregate Report, enter the path of the saved results file in its filename box and let it load. Allow enough memory for large files.
Depending on the transaction controller’s setting, it may add its own sample alongside the child samples, so both appear in the table and in the totals.
Only as background. It mixes different kinds of requests, so judge each critical request on its own row against its own target.
Without assertions, error pages with normal response codes count as successes. Add content checks so failures are detected.
There is no fixed number, but a few thousand per row gives much steadier values than a few hundred. Repeat runs to see how much the figure moves.
Yes. Use the save-table button, include the header option, and open the file in a spreadsheet for comparison and charts.
Use the table for fast checks and the dashboard for shareable reports with charts over time. Many teams use both on the same results file.
32Summary and Key Takeaways
The Aggregate Report compresses a mountain of samples into one row per request, with counts, averages, medians, percentile lines, error shares, throughput and data rates. It is quick, readable and widely used, which is why it is so often misread. The average hides the slow tail, the TOTAL row blends unlike requests, error percentages depend on the assertions behind them, throughput reflects your test design, and the whole table blends ramp-up with steady state. Used with care, it is an excellent first look at a run. Read several columns together, judge each critical request against its own target, run heavy tests headless and analyse afterwards, and confirm the story with time-based charts and server metrics.
Key Takeaways
- One row per label — stable business names make the table readable, and transaction settings change what appears and what is counted.
- Never trust the average alone — read the median and percentile lines to see the slow tail that users actually remember.
- Distrust the TOTAL row — it mixes unlike requests; judge each critical request on its own row against its own target.
- Read errors honestly — add assertions so the error column reflects real correctness, and read timings together with the error share.
- Throughput is a result — it follows from users, response time and think time, so design the test to aim for a required rate.
- Focus on steady state — ramp-up and warm-up samples flatter or distort the figures; record phase boundaries and judge from the steady period.
- Measure headless, analyse afterwards — use a light file writer for real load, load the saved file into the table or dashboard, and archive the raw results.