Direct Lake or Import which Semantic Model has the best performance
In this blog post I am going to show you how I completed the automated testing and then the results where I am going to compare Direct Lake, Import and DirectQuery and which one appears to be the best.
As always, your testing may very or be different to my tests below.
I would highly recommend that you use the method I have used and apply this testing technique to your data to understand and validate which semantic model would be best for your capacity.
Testing
As mentioned in my previous blog post I created identical semantic models and used the automated testing with the Notebook.
I completed the tests multiple times to ensure that the results I got were consistent.
The testing criteria was the following:
- 5 concurrent users
- Loop through the DAX queries twice
- NOTE: I had put in Distinct Count measures as this is very often what I find leads to slower performance overall. This is also often valid for many customers that I work with who want distinct counts.
- It was run when the capacity was not under load.
Multiple Tests were run
I ran the test multiple times, to ensure that the results were consistent.

To be honest it took me a while to get it all right where it would run successfully, and I did get it right as shown above.
It would typically take about 5 hours to run.
NOTE: The reason for the 5-minute wait, was so that I could then easily identify when each test was taking place.

Testing Order
What I did was to run the tests in the order below
- DL_Default
- ENGINE: Direct Lake
- INGESTION: Spark with Default settings
- DL_Vorder
- ENGINE: Direct Lake
- INGESTION: Spark V-Order applied
- DL_adaptiveFileSize
- ENGINE: Direct Lake
- INGESTION: Spark Adaptive File Size
- DL_readHeavyForPBI
- ENGINE: Direct Lake
- INGESTION: Spark with Read Heavy for Power BI
- DL_readHeavyForSpark
- ENGINE: Direct Lake
- INGESTION: Spark with Read Heavy for Spark
- DL_writeHeavy
- ENGINE: Direct Lake
- INGESTION: Spark with Write Heavy for Spark
- Import11Years1Month
- ENGINE: Power BI Analysis Services Engine
- INGESTION: Import mode with Incremental Refresh
- ImportAll
- ENGINE: Power BI Analysis Services Engine
- INGESTION: Import all tables as is
- ImportDaily
- ENGINE: Power BI Analysis Services Engine
- INGESTION: Import mode with Daily Partitions
- ImportOptimized
- ENGINE: Power BI Analysis Services Engine
- INGESTION: Import mode with Daily partitions and optimised by removing unwanted columns and IsAvailableMDX set to false where applicable.
- WH_Clustering
- ENGINE: Direct Query on Warehouse
- INGESTION: Used Data Clustering on Date column
- Warehouse_DirectQuery
- ENGINE: Direct Query on Warehouse
- INGESTION: Default settings
All-important Results
Overview – Query Duration
As you can see in the chart below, first what I am showing is how long each semantic model took.
This is an overall view which consists of the Duration that the queries took to complete.
This was interesting because I could see which semantic model was the most performant.

Or another way to look at this is Direct Lake / Import / Direct Query

OBSERVATIONS
- It appears that while there are some differences, it is easy to see which has got the lowest query duration. This is the Import Models.
- Import took about 174,000
- Followed by Direct Lake
- Direct Lake took about 247,000
- Finally Direct Query.
- Took about 724,000
What I wanted to show below is where the time is spent on the different Semantic Models.
As you can see for Direct Query a significant amount of time is spent on the Total Duration, because all the heavy lifting is done in the Warehouse.
NOTE: The CPU Time for the Power BI Analysis Services engine can be run in Parallel so that is why the timing might not add up.

Overview – Interactive Capacity Consumption
Below is what was happening from a Capacity Consumption perspective.
The reason I have put in the time buckets is that represents what was happening on the Capacity.

Or another way to look at this is Direct Lake / Import / Direct Query for Capacity Consumption.

OBSERVATIONS
- All the Direct Lake semantic models were in the range of 80% or more.
- The Import semantic models were between 30 – 100%
- Direct Query is measured as a Background process, so as to be expected it was less than 1% of interactive capacity.
Just to be complete in my overview, below is what the Capacity Consumption was like when comparing both Interactive and Background Capacity usage.

Final Result
Thanks for following along so far, and here is the final result, which I am assuming you would have gathered already as to what was the fastest and used the least amount of capacity units.

I had to combine the Interactive Capacity and query duration together with the 5-minute interval to be able to overlay the two measures.
OBSERVATIONS
- Overall, the lowest combination was the Import Mode

- The Direct Lake V-Order was the best overall

- And even though Direct Query did not consume much capacity it was by far the longest in terms of query duration.
- It does get a little confusing on this chart as the query duration peaks at a similar point to where the Interactive Capacity is.
Summary
As shown above with all my testing Import mode was the fastest closely followed by Direct Lake.
It did take me a significant amount of time to get it all in place, then get the testing completed and repeatable
I am thinking about also looking into including time to ingest the data for DL and refresh time for Import and the CUs which also factor into overall how this consumes CUs
Any other suggestions for what to test or other semantic model combinations please let me know. One other option I am thinking of is a combination of DL for fact table and Import for Dimension tables?
Any comments or suggestions on what I should create, and test next please let me know in the comments.
Finally, if you would like all the pieces that I put together for this testing also let me know as I think it would be useful for others to be able to complete load testing on your own data.
Interesting comparison. Import for Dimension and DL for Fact tables is a comparison I would like to see, but the priority should be to include the cost of refresh and maintenance (e.g., reindex) for a fair comparison. I expect Import for Dimension to primarily reduce latency for models that are not used often; I don’t expect a meaningful difference with DL.
Thanks Marco that is something I am planning on doing is to include the inserting of the data to the semantic models. For Import this would be refreshing the semantic model. And for DL it would be inserting the data into the new Lakehouse table, followed by reframing.
Would you also want to see the capacity usage and performance after a refresh?
Capacity usage is more important IMHO: it’s where the money is. Performance can change for many reasons after a refresh, so I wouldn’t spend too much time looking into it.
Thanks Marco. The cost for the refresh or indexing are all background processes which are smoothed over 24 hours so have a small effect on capacity and costs. While viewing reports is an interactive process that impacts the capacity directly.
[…] Gilbert Quevauvilliers performs some testing: […]
The interactive spikes are killing us. We have multiple models with 1+ billion rows in them (medical data), and we can have single users pushing us into throttled territory just by looking at a dashboard. Our interactive spikes sometimes go to 200% or 300%+.
We’ve had to switch all large models to direct query mode, so the spikes get averaged over the 24hour background vs. the 5 minute interactive. Just curious to hear your thoughts on this.
Hi Scott,
I have a customer who has got a similar situation with a billion row table. What we found was killing it was distinct counts on the data, so potentially you have something similar in your data. What I did to alleviate this was actually to put all the users in a premium per user licensing mode which then did not use the fabric capacity. By doing so allowed everything to go a lot better and not be throttled.
This sounds like a very interesting use case and if it is something you would like to discuss further with me, I would be happy to have a chat if you would like to contact me via the contact form.
I agree that Import mode in Premium per User is the best ROI if you have user consuming similar models. Incremental refresh mandatory in these cases! 🙂
Totally agreed!
Nice and in-depth analysis. Thank you. Looking forward once you try DL for fact and dimension as import as well. Is there any limitations around functionality of model (Dax calculations etc) in DL mode?
Hi Hari,
As far as I am aware there are no DAX calculation issues when working in DirectLake mode.
Very nice article and tests Gilbert. I’d be curious to know if on the DL models you’ve used the data already paged into memory or not? If not, maybe this is also a good test to be done, once first query takes all the heavy lift for transcoding dictionaries into semantic model’s AS.
A quick comment, on your first two charts, would be nice to have the legends and maybe an average/meadian for each of the models. Would facilitite the understanding on what the chart is showing.
Thanks a lot!
Cheers,
João Martins
Hi João, The data was not paged into memory at the beginning of the test. Would have been paged into memory for the subsequent tests that were occurring. And thanks for the suggestions on the charts, something I will look to do in the future.