Start with the symptom, not a rewrite

A slow Power BI report can originate in visual rendering, DAX evaluation, semantic model design, source queries or shared capacity. Rebuilding the entire report before isolating the bottleneck wastes time and can introduce new errors.

Begin with a reproducible page, a documented filter state and a baseline for the affected visual. Record the duration, dataset size, refresh mode and whether the slowdown occurs in Desktop, the service or both.

1. Capture a reliable baseline

Use Performance Analyzer to separate visual display, DAX query and other elapsed time. Repeat the test with a cold and warm cache, and avoid comparing a heavily filtered page with an unfiltered one. Record p50 and p95 timings where repeated samples are available.

For DirectQuery or composite models, check the source system's query history as well. A visual can look like a DAX problem while actually waiting on a SQL source or gateway.

2. Check model shape before optimizing measures

Inspect relationships, cardinality, bidirectional filtering and unnecessary high-cardinality columns. Prefer a clear star schema with fact tables connected to reusable dimensions. Remove columns that do not serve reporting, calculations or governance.

Large text identifiers, overly granular timestamps and redundant calculated columns can increase memory pressure. Measure before and after changes rather than assuming every reduction improves the user experience.

3. Diagnose DAX with query evidence

Use DAX Studio and server timings where available to inspect expensive measures. Look for repeated iterators over large tables, excessive context transitions and filters that force unnecessary materialization.

For example, a measure that iterates every transaction row may be replaced with a more selective aggregation, but only after verifying identical totals across slices and edge cases. Do not trade correctness for a fast benchmark.

4. Separate refresh problems from interactive latency

Import-model refresh duration and report interaction latency are different operational concerns. Investigate incremental refresh partitions, source folding and dataflow dependencies for refresh failures. For interactive issues, focus on measure evaluation, relationships, visual count and capacity contention.

Use service capacity metrics where available to identify concurrent workload pressure, throttling or memory evictions. A report that performs well in Desktop may still suffer under shared service load.

5. Make improvements testable

Maintain a simple comparison sheet: page and visual, filter state, baseline, change, new timing, correctness result and rollback plan. Test representative users with row-level security, not just an administrator account.

After optimization, validate totals, subtotals, blank behavior, drill-through and export results. Performance gains that alter business KPIs are regressions.

6. Establish an operating routine

Assign ownership for semantic model changes. Track slow visuals, refresh duration, model size, failed refreshes and capacity pressure. Review regressions after deployments and significant source changes.

A useful acceptance criterion is not simply “the report feels faster.” It is a documented reduction in latency under comparable conditions, with unchanged reconciled metrics and an explicit monitoring owner.

Practical checklist

  • Reproduce and measure the slow visual.
  • Separate DAX, rendering and source latency.
  • Inspect relationships and high-cardinality fields.
  • Profile expensive measures with query tools.
  • Compare Desktop and service capacity behavior.
  • Reconcile business metrics after each change.
  • Record evidence and a rollback path.

Further reading

Consult Microsoft's official Power BI performance guidance, Performance Analyzer documentation and DAX optimization resources for current feature-specific instructions. The workflow above is an editorial engineering checklist, not a claim about measured search volume or a particular tenant.

Worked example: diagnose a costly sales measure

Suppose a report measures sales across a transaction table with millions of rows. Start with a measure that preserves the business definition:

Total Sales = SUM ( FactSales[NetAmount] )

If a more complex measure iterates the entire fact table, use DAX Studio server timings to compare storage-engine versus formula-engine work. Confirm whether filter context, inactive relationships or row-level security change the result before replacing an iterator. Compare identical filter selections and validate subtotals, not only the grand total.

A model-level experiment should record the measure definition, baseline duration, model version, test user, filter state and result checksum. Never optimize solely against an unfiltered development report.

Relationship and capacity decision matrix

| Observation | Investigate first | Evidence to capture | | --- | --- | --- | | Slow only with certain slicers | Relationship direction, cardinality, filter propagation | Query plan and relationship diagram | | Slow for every user in service | Capacity pressure or storage mode | Capacity metrics and percentile latency | | Slow only for RLS users | Security filters and relationship paths | Same query under representative identities | | Refresh slow, interaction fast | Query folding and partitions | Refresh logs and partition boundaries |

Related AIDataCenter reading

For the broader visual-to-source troubleshooting process, see Power BI Performance Troubleshooting Framework. This article instead focuses on semantic-model-level evidence and reproducible optimization experiments.

Prerequisites and a repeatable test environment

Before tuning, identify the semantic model's storage mode (Import, DirectQuery, or composite), capacity assignment, refresh schedule, relationship diagram, and the report page with the reproducible problem. Obtain permission to use Performance Analyzer in Power BI Desktop, and use DAX Studio only in an authorized environment. Record the Power BI Desktop version, model version, active filters, RLS identity, and representative data volume.

A practical test matrix includes an unfiltered view, a common business filter, a high-cardinality selection, and a representative RLS user. Run each several times to distinguish a cold-cache outlier from persistent latency. Report the median and a high-percentile timing only when you have enough observations to make that statistic meaningful.

Example: distinguish measure logic from model behavior

Consider a star schema with FactSales joined to DimDate, DimProduct, and DimCustomer. A base measure should be simple and independently reconcilable:

Net Sales = SUM ( FactSales[NetAmount] )

Sales YTD =
TOTALYTD ( [Net Sales], DimDate[Date] )

Sales per Order =
DIVIDE (
    [Net Sales],
    DISTINCTCOUNT ( FactSales[OrderID] )
)

These are illustrative measures, not benchmarked optimizations. TOTALYTD assumes a suitable date table and appropriate calendar semantics. Sales per Order measures sales divided by distinct order IDs, which may differ from the business's definition of average order value if refunds, cancellations, or split orders exist.

Compare these measures across the same date and product filters. If totals differ from the source system, stop: correctness takes priority over speed.

Reading DAX Studio evidence

For a slow measure, capture server timings and the query plan. Look at the balance of formula-engine and storage-engine work, the number of storage-engine queries, and any large intermediate materializations. One expensive iterator is not automatically a defect; its cost depends on cardinality, context, and what the engine can push down.

Use this sequence:

  1. Run the original visual query under the documented filter state.
  2. Record timings and output values.
  3. Change one thing at a time—measure expression, relationship, or column design.
  4. Re-run with comparable cache conditions and the same security identity.
  5. Confirm totals, subtotals, blanks, and edge cases before accepting the change.

Avoid claiming that a rewrite is universally faster. The VertiPaq engine, storage mode, and query shape determine the outcome.

Cardinality, relationships, and VertiPaq

Columnar compression benefits from appropriate types and repeated values. High-cardinality transaction IDs and high-precision timestamps can consume disproportionate space, but removing them is only safe when they are not required for drill-through, relationships, audit, or business operations.

Prefer single-direction filtering in a conventional star schema unless a documented requirement justifies another pattern. Many-to-many relationships and bidirectional filtering can introduce ambiguous filter paths and surprising totals. Validate relationship keys for blanks, duplicates on the one-side, and unexpected granularity.

For a model that mixes imported and DirectQuery tables, identify whether a slow visual crosses storage groups. Source-side indexing, query folding, and gateway health can matter more than DAX expression tuning.

A practical performance experiment

Imagine a monthly sales report whose p95 visual latency appears high. The team suspects a complex measure, but the problem occurs only when a customer-name slicer is used.

Hypothesis: The slicer propagates filters through an expensive relationship path.

Experiment: Capture the query and timings with and without that slicer; inspect relationship direction and cardinality; compare against a controlled alternative model in a development workspace.

Validation: Confirm identical totals across customers, dates, and RLS identities, and inspect service performance under realistic concurrency.

Decision: Promote only if the improvement is repeatable and there is no semantic regression. This scenario illustrates an investigation method, not an observed production result.

Deployment, monitoring, and rollback

Keep model changes in version control where the team's tooling supports it. Record the baseline, proposed change, expected benefit, test cases, reviewer, and rollback artifact. Validate refresh completion, report visuals, bookmarks, export behavior, and downstream dependencies after deployment.

Monitor interactive latency and refresh failures separately. Capacity metrics help distinguish model problems from concurrent workload pressure. If service latency worsens after release, restore the prior validated semantic model rather than making unreviewed changes in production.

Common mistakes and how to avoid them

| Mistake | Why it fails | Better approach | | --- | --- | --- | | Optimize only the grand total | Filtered totals and RLS can regress | Test representative slices and identities | | Compare cold and warm cache results | Timing comparison is misleading | Record cache conditions and repeat trials | | Replace every iterator | Some iterators express necessary business logic | Profile first, then rewrite selectively | | Treat refresh time as visual latency | Different subsystems and bottlenecks | Measure separately | | Remove high-cardinality keys blindly | Breaks traceability and drill-through | Document downstream dependencies |

Official technical references

The code and scenarios here are educational examples. Verify them against your semantic model, current Microsoft documentation, security model, and business definitions before production use.