📊 Power BI

How to Diagnose
a Slow Power BI Model

Most Power BI performance work starts with a guess. Someone read that calculated columns are bad, removes a few, and the report is still slow — because the actual cost was somewhere else entirely.

The workflow below takes about an hour on a model you have never seen and tells you where the time genuinely goes. All three tools are free.


Measure Before You Change Anything

All three are free, and none of them require access to your production tenant. A copy of the .pbix is enough to run the entire workflow, which means you can start today without waiting on an access request.

Record a baseline first, for a practical reason: query results cache. Run a visual twice and the second run is faster regardless of what you did in between, which makes it very easy to convince yourself a change worked.

Clear the cache between every measurement. DAX Studio has a Clear Cache command; use it before each run, and take three readings rather than one.

Step 1: Performance Analyzer, in Desktop

Built into Power BI Desktop under the View ribbon. Start recording, refresh the page, stop. You get a breakdown per visual, split three ways.

ComponentWhat it meansWhat to do
DAX queryTime the engine spent answeringThe number that matters. Over 1,000ms needs attention.
Visual displayTime rendering the resultHigh usually means too many data points, not a model problem.
OtherWaiting, cross-filtering, overheadHigh values often point at bidirectional relationships.

Sort by DAX query descending. The top two or three visuals almost always account for most of the page load, and they are frequently not the ones users complain about.

💡 A visual under 100ms is not worth optimising no matter how inefficient it looks. Spend the effort on whatever sits at the top of the sorted list.

Step 2: DAX Studio, for the Expensive Query

Copy the query from Performance Analyzer into DAX Studio, enable Server Timings, clear the cache and run it. The split you get is the important part.

What Server Timings tells you

Total        2,840 ms
  SE          320 ms   (11%)   Storage Engine
  FE        2,520 ms   (89%)   Formula Engine
  SE Queries      14

Read it like this:

  FE-heavy  -> the DAX is the problem.
               Row-by-row evaluation, iterators over
               large tables, repeated sub-expressions.

  SE-heavy  -> the model is the problem.
               Too much data scanned, poor compression,
               high cardinality, missing relationships.

  Many SE queries -> the engine is making repeated
               round trips. Often a sign of a measure
               that could be restructured.

This single split decides where you spend your effort. Rewriting DAX on an SE-heavy query is wasted work, and restructuring a model to fix an FE-heavy one is a great deal of effort for very little.

Step 3: VertiPaq Analyzer, for the Model Itself

Also in DAX Studio, under Advanced. It reads the model's internal statistics and shows you what is actually consuming memory, column by column.

What to look for, in order

1. Sort columns by Size, descending.
   The top five usually account for most of the model.

2. Check Cardinality on those columns.
   High cardinality compresses badly. Timestamps to
   the second, GUIDs and free text are the usual
   offenders.

3. Look at the Type column.
   A calculated column in the top five is a finding.
   It is stored, permanent, and not filter-aware.

4. Compare Dictionary vs Data size.
   A large dictionary means many distinct values.
   Splitting a datetime into date + time columns
   frequently cuts this dramatically.

5. Check for columns you do not use at all.
   Every loaded column costs memory whether or not
   a report references it.

Point four is the one people miss. A single datetime column with second-level precision on forty million rows can be the largest object in the model. Splitting it into a date key and a separate time column often reduces it by an order of magnitude, because the date has a few thousand distinct values and the time has 86,400 — rather than the two together having tens of millions.

Reading the Three Together

PatternMost likely causeWhere to start
FE-heavy, model is smallInefficient DAXRewrite the top two measures
SE-heavy, model is largeStructure or cardinalitySplit datetimes, remove unused columns
SE-heavy, model is smallMissing or wrong relationshipsCheck the model diagram
High 'Other' time across visualsBidirectional relationshipsReplace with CROSSFILTER in the measure
One visual dominates everythingA single expensive measureIsolate and rewrite it
Everything uniformly slowCapacity or gatewayCheck refresh history and capacity metrics

That last row is worth stating plainly: sometimes the answer genuinely is capacity. But it is the last conclusion to reach, not the first, and you should only reach it after the first three tools have ruled out the alternatives.

One more reading worth knowing: a high SE query count with low total SE time. That means the engine is making many small round trips rather than one large scan, which usually points at a measure the engine cannot fold into a single storage query. Restructuring that measure often collapses fourteen queries into one.

And if Performance Analyzer shows large Other time spread evenly across every visual on a page, stop looking at individual measures. That pattern is almost always model-wide: bidirectional relationships, or a many-to-many relationship the engine has to resolve before it can answer anything.

Write Down What You Found

The step almost everyone skips, and the one that determines whether the work has any value beyond this week.

Record four things for every model you diagnose: the baseline timings, the FE/SE split for the worst query, the top five columns by size, and what you changed with the measured effect of each change. That takes twenty minutes and it turns a fix into knowledge.

Without it, the next person who inherits the model repeats the entire archaeology from scratch. Frequently that next person is you, eighteen months later, having forgotten which of the six things you tried was the one that worked.

It also settles arguments. When somebody asks in three months why a measure is written the unusual way it is, the answer is a recorded before-and-after rather than a recollection.

Four Mistakes People Make While Measuring

Measuring a warm cache. The most common one. Run a visual twice and the second is faster regardless of what you changed. Clear the cache before every single reading, and take three.

Testing against a subset. A model that performs acceptably against a development extract can behave completely differently against three years of production history. Cardinality, not row count, is what changes — and a subset usually has much lower cardinality than the real thing.

Optimising what users complain about rather than what costs the most. These are frequently different. Users complain about the page they open every morning; the expensive query is often on a page nobody mentioned, quietly consuming capacity that everything else needs.

Changing several things at once. If you remove three calculated columns, rewrite two measures and change a relationship, and the report gets faster, you have learned nothing transferable. Change one thing, measure, record. It is slower and it is the only way to build knowledge you can reuse on the next model.

What We Would Do

This workflow is what a performance audit consists of, done systematically and written up. We record baseline timings, run all three tools, and hand back findings ranked by impact against effort — with the before-and-after measurement for each change we make.

The deliverable most clients value is not the speed improvement. It is the documentation, because it means the next person who inherits the model does not repeat the archaeology.

💬 Working with us

If you would rather not spend the hour, that hour is the first day of a performance audit — a fixed-price two-week engagement ending in a documented, measured result. Or run the free 32-point checklist to see where the obvious gaps are first.

Keep Reading

Would rather not spend the hour?

That hour is day one of a fixed-price performance audit. Send us the model size and slowest page timing to start.

Start the Conversation →