Manage and improve performance#
Distribute the load is about the hardware and where the work runs. This page is about the work itself: why an existing report, job, or form performs the way it does, and what to change.
When to use it. When a pack that used to finish in three minutes now takes twenty, when several people report slowness in the same week, when everything feels slower after an upgrade, or when you have been asked to make something faster and want to know where the time is actually going before you start changing things.
Before you start#
- Open a report's Run History from the workspace browser: the report row's ... menu, then Run history.
- The diagnostics tabs sit behind View diagnostics, which a view-only User account does not have. Authors and administrators reach them.
- Settings ▸ Performance is administrator-only and is not offered on a load-balancer node.
- Comparison needs history. A baseline needs at least 3 comparable earlier runs; a step-change search needs 6. A recently upgraded instance is thin until new runs accumulate.
Ask the questions in this order#
Four surfaces answer four different questions, and working from the outside in stops you optimizing the wrong thing. A report that regressed at the same moment as every other report on its node is not a report problem.
| Ask | Where | You learn |
|---|---|---|
| 1. Is it the instance? | Settings ▸ Performance ▸ Distribution | Whether many reports moved together, and whether a release or a node explains it |
| 2. Is it this report, over time? | The Compare tab on a run's diagnostics | Whether there is a single point where this report's typical run time stepped, and whether volume, release, or node moved with it |
| 3. Where did this run's time go? | The Performance tab on a run's diagnostics | The cost broken down by output, workbook, processing phase, and calculation engine |
| 4. What did it coincide with? | The Timeline tab on a run's diagnostics | Every span, event, and the memory and CPU trace, drawn on one clock |
If contributors rather than reports are the problem, the equivalent funnel is per-server memory on Settings ▸ Performance ▸ Contribution, then a form's footprint in Campaign profiling. See Scale and size a contribution campaign.
1. Rule the instance in or out#
Settings ▸ Performance ▸ Distribution ranks every report against its own baseline and pivots the whole estate by product release and by execution node. It is the only surface that can see across reports, and it is where "did the upgrade do this" is answered.
Choose a window of 30 days, 90 days, or 1 year. Everything on the page, including the baselines, is recomputed inside that window, so a delta on screen is always explained by runs you can see.
Read the five figures first. Runs with telemetry tells you how much of the window is actually measurable, and a low percentage means the page below is thinner than it looks. Releases seen and Nodes seen tell you whether the pivots have anything to compare at all: one release or one node is nothing to compare.
Then read the two pivots at the foot of the page. Three rules govern them, and they are the reason a cell sometimes refuses to give you a verdict:
- A group is compared against the other groups, never against the overall median. A node that runs most of the work would drag an overall median toward itself and look normal however slow it was.
- A group needs at least 3 runs, and there has to be something to compare it with.
- Volume has to match. If a group processed more than 15 percent more or less data than the others, the cell reads "different volume" and gives no verdict. A release that looks 40 percent slower because its runs carried three times the data is not a regression.
A refusal to compare is information. It is the page declining to tell you something it cannot honestly know, and it is far more useful than a confident number built on three unlike runs.
The page distinguishes its two empty outcomes, which matters when you are diagnosing rather than browsing. "No runs in this window." means it read the history and found nothing, so widen the window or check that job-history logging is on. "Performance data is unavailable." means the read itself failed, and it shows what went wrong. Those are different problems and the page will not pass one off as the other.
Full field-by-field detail is in Compare performance across reports.
2. Find the moment a report changed#
The Elapsed vs baseline column answers "was the last run normal". That is the wrong question for a report that has been drifting for a fortnight, because every individual run looked fine against a baseline that was itself creeping upward.
The Compare tab asks the other question: across this report's whole history, is there a point where its typical run time changed? It searches for a single step and either finds one or says plainly that there is not one.
The verdict sentence is the whole answer, and it names a cause only when the data shows one. It will tell you whether volume moved across the same split, whether the release differs on either side, and whether the runs moved to a different node. "It got slower" and "it got bigger" are different findings, and this is the surface that separates them.
On a bursting job, use the scope picker to narrow to one variation. A whole-run total moves for reasons that have nothing to do with any one output: a burst list grew, a region was retired, a run was restricted to one recipient to test a fix. Each surviving output stays perfectly comparable with its own past even when the total does not.
See Find when a report got slower.
3. Break one run down#
The Performance tab leads with one sentence naming the dominant cost, then six figures, then a walk you can drill.
Read Elapsed with its volume denominator. Underneath the elapsed time sits the volume the run covered: data points where the run recorded them, otherwise the formula count. This is the first thing to check, because a report doing twice the work in twice the time has not regressed.
Walk from the run down to a phase. One ranked list, largest first, at three levels:
- Whole run, listing the outputs it generated. This is where a burst tells you which output is expensive.
- One output, listing the workbooks inside it.
- One workbook, listing its processing phases. Phases are the last level, and this is where "the PDF took nine minutes" or "the refresh took nine minutes" gets settled.
At workbook level, two tails are listed apart from the ranked phases: phases that ran but cost nothing measurable, and phases that never ran at all. Keeping them separate matters, because "this workbook has no charts" and "charts were free" are different facts about the same run.
What each phase is#
The phase names are the vocabulary the whole surface is written in, so knowing what each one covers is what turns the ranked list into an action. These are the phases Reportworq expects a workbook to have, in roughly the order they run.
| Phase | What it covers |
|---|---|
| Open Workbook | Loading the source workbook from its report provider, file open and parse |
| Workbook Initialization | Preparation before calculation: hidden-sheet extraction, very-hidden sheet removal, worksheet generation |
| Generate Worksheets | Cloning template sheets per parameter set |
| Data Refresh | The full calculation pass: every datasource calculation service plus the recalculation loop |
| Recalculate Indirect Formulas | INDIRECT and dynamic-formula recalculation after structural edits |
| Suppress Worksheets | Sheet-level suppression driven by RWSHEETSUPPRESS |
| Rename Worksheets | Sheet renaming driven by RWSHEETNAME |
| Variables | Report-scoped variable resolution |
| Pivot Tables | Pivot-table filter application |
| Charts | Refreshing add-in-authored chart and map pictures against the calculated workbook |
| Images | Shape and image refresh across the workbook |
| Formatting | Tab colors, output order, and exception-cell reads |
| Remove Formulas | Formula stripping before save |
| Workbook Cleanup | Deleting worksheets that are not part of the output |
| Save Workbook | Serializing the destination workbook to its output stream |
| Save PowerPoint Datasource | Saving the extra workbook copy kept as a PowerPoint datasource |
| Script Hooks | Custom scripts that run inside report processing |
Data Refresh is almost always the largest, and it is the one that contains the recalculation loop, which is why the engine table below it matters more than the phase list. When something other than Data Refresh tops the list, that is the interesting result: a workbook dominated by Charts, Images, or Formatting is paying for its presentation rather than its data, and that is a different conversation with the author.
A few other phases can appear when the work applies to that report, such as parameter application, closing the workbook, or loading a non-Excel source document. They are ranked like any other; only the phases in the table above take part in the "never ran" tail.
The engine table is where the fixable problems are#
Below the walk, the Engines table lists one row per formula family and per calculation service, with Calls, Total, Avg, and Recalc passes.
Recalc passes is the column to read. It says once for an engine that ran a single time, or N of M for one that re-ran during recalculation. An engine reading 5 of 5 on a five-pass workbook ran on every pass, and Reportworq highlights that row when the pass count is high enough for it to be expensive.
That pattern is the single most actionable finding on the whole surface. A data retrieval repeated on every recalculation pass is usually a formula that can be restructured, not an unavoidable cost, and it is invisible from the durations alone: an engine can look cheap per call and still dominate a run because it is trapped in the loop.
Above workbook level the pass counts are folded together from the workbooks in scope, so 5 of 5 there reads as "in the recalculation loop the whole way" rather than as a run-wide total.
See Review a run's performance.
4. See what the time coincided with#
The Timeline tab re-projects the same run onto one clock: execution phases with their varieties nested beneath, per-workbook phase spans, event ticks, and the memory and CPU trace, all sharing one x-axis and one cursor.
It answers a different question from the other tabs. They say where the time went; this says what the time coincided with, which is the only way to see that the third data refresh landed on a memory spike.
Two readings are easy to get wrong:
- A hatched break in the CPU line is "not measured", never 0 percent. The opening stretch of every run is hatched, because the first sample has no interval to measure against. That is correct, not missing data.
- The expected-completion band is the expectation that stood when the run started, built from the runs before it, which is what makes an estimate on a finished run honest. It can legitimately extend past the end of the run, since an estimate built from a median is beaten by about half of all runs.
The tab also says when a cap has hidden detail, such as a very long run whose resource samples were thinned or a run with more spans than the chart draws. Read those notes before concluding that something did not happen.
The levers, and the order to try them#
Work top down. Everything above a line is cheaper and more reversible than everything below it.
Content: change what the work does#
| Lever | When it applies | What to do |
|---|---|---|
| A formula family in the recalculation loop | The engine table shows N of N on a multi-pass workbook, or a highlighted row |
Restructure the formula so the retrieval is not re-evaluated on every pass. This is usually the largest single win available. |
| Data volume grew | Elapsed rose and the volume denominator rose with it | Not a regression. Decide whether the report needs that volume, and narrow the query or the burst if it does not. |
| One output dominates a burst | The whole-run list is lopsided | Treat that output as its own problem. The other outputs are fine and changing them costs effort for nothing. |
| A phase you did not expect | The workbook-level list ranks something surprisingly high | The phase names what kind of work it was. Confirm the report actually needs it. |
What the breakdown will not tell you. It attributes cost to phases and engines, not to individual design choices, so it cannot confirm that a particular chart, image, or block of formatting costs a given amount. If you suspect one, remove it, run again, and let the before-and-after decide.
Placement and clock: change where and when it runs#
| Lever | When it applies | Where |
|---|---|---|
| Split a monolithic job | Throughput is not scaling with node count | Distribute the load |
| Raise Tasks per Job | A single job's outputs are running more serially than the node can afford | Settings ▸ Configuration ▸ Performance ▸ Execution |
| Pin or exclude with Filter jobs | A long job is occupying nodes that serve short runs | Settings ▸ Load Balancer, per node |
| Stagger the schedule | The spike is a clock artifact, or a schedule is overrunning its own interval | Scheduled Content |
Instance: change what the server does#
| Lever | When it applies | Where |
|---|---|---|
| Parallel Job Execution and Tasks per Job | The server has headroom, or is over-committed against its RAM | Settings ▸ Configuration ▸ Performance ▸ Execution |
| Enable Out of Process Job Execution | Should be on. Turn it off only on support advice | Settings ▸ Configuration ▸ Performance |
| Enable Out of Process Reporting Queries | Interactive add-in refresh is pressuring the web tier | Settings ▸ Configuration ▸ Performance ▸ Reporting |
| Retention and Compress History Files | History or stored output has grown large | Settings ▸ Configuration ▸ Performance, and Output retention |
| Debug Logging left on | Always worth checking. It produces very large logs and archives | Settings ▸ Configuration ▸ Logging |
| Recycle Service | A long-lived instance benefits from a daily restart | Settings ▸ Configuration ▸ Web Server |
Contribution: change what a form costs#
| Lever | When it applies | What to do |
|---|---|---|
| Make the form smaller | Peak cost is high, and bytes per cell is normal | Cost is roughly bytes per cell multiplied by cell count, so removing an unused sheet or trimming to the range contributors actually fill removes real cost. Profile before and after. |
| Manual calculation mode | Large forms filled slowly over a day | The form stops recalculating on every edit and the server releases an idle session's workbook shortly after the last action that needed it, rebuilding on demand. The trade is that the contributor waits on their next action. |
| Max Sessions per Worker | Workers read At capacity while server memory is comfortable | The per-worker session cap is binding, not memory. Raising it lets sessions share a worker baseline, which lowers the per-contributor cost. Default 1, range 1 to 50. |
| Recycle Worker After Sessions | High Served (lifetime) against a short Uptime | Workers are recycling often and contributors repeatedly pay a cold start. Default 25; 0 never recycles. |
| Warm Worker Pool Size | Contributors wait on a cold start when they open a form | Keeps workers started and waiting. Default 0, meaning no warm pool, so every session pays a start. Range 0 to 50. |
| Warm Worker Idle Timeout | Warm workers are being held longer than a round needs | How long an idle warm worker is kept before it exits. Default 30 minutes. |
All four are on Settings ▸ Configuration ▸ Performance, under Contribution.
Full arithmetic and the live-round playbook are in Scale and size a contribution campaign.
Prove the change worked#
An improvement you cannot demonstrate is an opinion. The measurement rules make a fair before-and-after straightforward, provided you know them.
- The baseline is the median of up to 12 comparable earlier runs, and the median is deliberate: one pathological run would drag an average far enough to hide every later regression.
- A difference within 15 percent counts as noise and is not flagged. If your change is worth less than that, this instrumentation will not show it, and neither will your users notice it.
- A run is never part of its own baseline, and failed, canceled, and unmeasured runs are held out entirely.
- Compare like with like. Check the volume denominator on both sides. A faster run over less data has proved nothing.
- Two different elapsed figures exist. Where telemetry is present, elapsed is measured from execution start; where it is not, the column falls back to the wall-clock duration on the run record, and the tooltip says which you are looking at. They are never mixed inside one trend, and you should not mix them either.
- Pin the runs that matter. Performance data is removed when retention removes the run, so pin the before run if the comparison has to survive the retention window.
- On contribution, deltas travel and absolutes do not. Profiler numbers are working set on the machine that ran the profile. Compare before and after on the same machine.
Once several runs have accumulated after the change, the Compare tab should find the step you created, in the direction you intended. That is the cleanest evidence available.
Red flags and what they usually mean#
| Symptom | Usual cause | Where to look |
|---|---|---|
| Many reports regressed in the same week | A release or a node, not the reports | Settings ▸ Performance ▸ Distribution, the release and node pivots |
| One report drifted with no run ever flagged | A gradual step the dead band absorbed run by run | The Compare tab |
| Elapsed doubled and so did the volume | Not a regression | The volume denominator on the Performance tab |
| One engine dominates with a modest average | It is being re-run on every recalculation pass | The Engines table, Recalc passes |
| Occurrences are missing rather than late | A schedule overran its own interval past the 30-minute overdue window | Scheduled Content, and Distribute the load |
| Throughput did not improve when a node was added | A job runs atomically on one node | Distribute the load, job granularity |
| Analyst refresh is slow while batch throughput is fine | Interactive refresh is not distributed and lands on the node holding the session | Distribute the load |
| Contributors are refused a session | The 90% memory admission valve | Settings ▸ Performance ▸ Contribution |
| A column of dashes where measurements should be | Runs that predate telemetry, or runs held out of the baseline | The note under the run list, which states both counts |
When to escalate#
Reportworq's own surfaces attribute cost down to a phase and an engine. Below that, the support desk needs the raw material.
Collect it with Settings ▸ Log Diagnostics, which packages application logs from every node, including load-balancer nodes, into a support packet. Say which report and which run you are asking about, since a run's measurements are what support will read first. If you were asked to reproduce the problem with Debug Logging on, turn it off again after the capture: it produces very large logs and archives and is itself a drag on the instance. See Log diagnostics and support packets.
Notes and limits#
- This is not alerting. There are no thresholds to set and no notifications. The baseline rules described here are fixed.
- A failed run still records its performance, deliberately, since a failure is often exactly the run whose breakdown you want. For diagnosing the failure itself rather than its cost, see Diagnose job failures.
- Releasing a held report writes no new measurement. The release re-opens the original run's record, and overwriting it would destroy the history this instrumentation exists to build.
- Only one step change is reported by the Compare tab, the single most significant one. It does not enumerate every wobble.
- The report ranking on Settings ▸ Performance ▸ Distribution has no paging and no row cap. On a large instance it is a long table, and narrowing the window is the only way to shorten it. A deleted report can still appear, because the page explains runs that already happened.
- There is no comparison level below one variation on the Compare tab. For per-workbook detail, use the Performance tab on a single run.
Going deeper. For sizing and placement, see Distribute the load. For the full field-by-field reference on each screen, see Compare performance across reports, Review a run's performance, and Find when a report got slower. For the live contribution console, see Contribution processes.
Feedback on this page
Comments, questions, requests, or something missing or unclear? Email us - the page you are on is filled in for you.
Email feedback on this pageOr write to support@reportworq.com directly.