views:

218

answers:

3

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();
    }
}
+3  A: 

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.

Dean Harding
Thank you for your general comments, but you have not answered the question at all. Yes, there is an index on trade_tag. If the query is called 100,000 times (as it will be), the overhead is not "quite small".
Grammarian
+4  A: 

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();
}
Grammarian
+2  A: 

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));
Marc Gravell