Why doesn't this CompiledQuery give a performance improvement?
-
26-09-2019 - |
Question
I am trying to speed up an often used query. Using a CompiledQuery
seemed to be the answer. But when I tried the compiled version, there was no difference in performance between the compiled and non-compiled versions.
Can someone please tell me why using Queries.FindTradeByTradeTagCompiled
is not faster than using Queries.FindTradeByTradeTag
?
static class Queries
{
// Pre-compiled query, as per http://msdn.microsoft.com/en-us/library/bb896297
private static readonly Func<MyEntities, int, IQueryable<Trade>> mCompiledFindTradeQuery =
CompiledQuery.Compile<MyEntities, int, IQueryable<Trade>>(
(entities, tag) => from trade in entities.TradeSet
where trade.trade_tag == tag
select trade);
public static Trade FindTradeByTradeTagCompiled(MyEntities entities, int tag)
{
IQueryable<Trade> tradeQuery = mCompiledFindTradeQuery(entities, tag);
return tradeQuery.FirstOrDefault();
}
public static Trade FindTradeByTradeTag(MyEntities entities, int tag)
{
IQueryable<Trade> tradeQuery = from trade in entities.TradeSet
where trade.trade_tag == tag
select trade;
return tradeQuery.FirstOrDefault();
}
}
Solution
Thanks to orandov, I found the answer here (at the end). If you make any changes to the query, the precompiled statement is discarded. In my case, FirstOrDefault()
was changing the underlying query.
Solution was to call AsEnumerable()
on the query first. By calling AsEnumerable()
the precompiled query was protected, and FirstOrDefault()
was executed locally on the results (it was called against Linq.Enumerable.FirstOrDefault
rather than Linq.Queryable.FirstOrDefault
).
Net result: execution time was reduced from 45ms to 4ms. 11x faster.
public static Trade FindTradeByTradeTagCompiled(MyEntities entities, int tag)
{
IQueryable<Trade> tradeQuery = mCompiledFindTradeQuery(entities, tag);
return tradeQuery.AsEnumerable().FirstOrDefault();
}
OTHER TIPS
Rather than AsEnumerable
(which won't limit the results at the database), have you tried:
// Pre-compiled query, as per http://msdn.microsoft.com/en-us/library/bb896297
private static readonly Func<MyEntities, int, IQueryable<Trade>> mCompiledFindTradeQuery =
CompiledQuery.Compile<MyEntities, int, IQueryable<Trade>>(
(entities, tag) => (from trade in entities.TradeSet
where trade.trade_tag == tag
select trade).Take(1));
Queries are always "compiled" it's just that if you don't use the CompiledQuery
then it'll be compiled on-demand. Also, the CompiledQuery
is only compiled the first time it's executed anyway (the difference being that a CompiledQuery
is only compiled once, whereas the 'regular' way will be compiled each time). For such a simple query like the one you've got, the overhead of compiling is probably quite small.
Do you have an index on the trade_tag
field? That'll provide you with the biggest performance increase.
Instead of returning IQueryable, just set up the compiled query to return the single Trade object directly. This is a lot cleaner code than the previous solutions.
// Pre-compiled query, as per http://msdn.microsoft.com/en-us/library/bb896297
private static readonly Func<MyEntities, int, Trade> mCompiledFindTradeQuery =
CompiledQuery.Compile<MyEntities, int, Trade>(
(entities, tag) => (from trade in entities.TradeSet
where trade.trade_tag == tag
select trade).FirstOrDefault() );
public static Trade FindTradeByTradeTagCompiled(MyEntities entities, int tag)
{
return mCompiledFindTradeQuery(entities, tag);
}
Another example is here: Linq to SQL to Linq compiled performance