<div dir="ltr">Hi Jean,<div><br></div><div>We have observed some performance issues with qlc-s and select-s that we were expecting to use a secondary index, but they turned out not to. To quote my colleague, Andreas (not on this list):</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">It seems like mnesia select is able to use it index only when doing exact match and when the match spec that it receives have the value to match in the match head (not in the match body): </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Fast:<br><font face="monospace">mnesia:dirty_select(pacc, ets:fun2ms(fun(#pacc{ano = 4611713374746180065} = P) -> P end)). </font></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Slow:<br><font face="monospace">mnesia:dirty_select(pacc, ets:fun2ms(fun(#pacc{ano = A} = P) when A =:= 4611713374746180065 -> P end)).</font> </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">One problem with qlc is that it is easy to shoot yourself in the foot. It is important to put the guards that translate to match specs first.</blockquote><div><br></div><div>However, I tried your hand written qlc query from the benchmark PR (the version you inlined in the email is not correct, the <font face="monospace">'_'</font>-s within the qlc will be translated to matches against the literal underscore atom, not treated as wildcards), and it still executes a dirty_index_read (not even a dirty_select), which is fast. Qlc still has a significant overhead, in my case ~1300 us vs 45 us for a plain dirty_index_read. But that should be independent of the table's size, and shouldn't be measured in seconds.</div><div><br></div><div>Can you verify with tracing what are the actual mnesia calls your qlc queries perform? They should do (dirty_)index_read-s, but maybe they do a (dirty_)select or even full table scan instead?</div><div><br></div><div>Cheers,</div><div>Daniel</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, 6 Oct 2021 at 15:04, Jean Parpaillon <<a href="mailto:jean.parpaillon@free.fr">jean.parpaillon@free.fr</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Sorry, the missing links:<br>
Merge request with benchmarks:<br>
<a href="https://gitlab.com/patatoid/ecto3_mnesia/-/merge_requests/56" rel="noreferrer" target="_blank">https://gitlab.com/patatoid/ecto3_mnesia/-/merge_requests/56</a><br>
<br>
Ecto Query to Qlc compiler:<br>
<a href="https://gitlab.com/patatoid/ecto3_mnesia/-/blob/master/lib/ecto/adapters/mnesia/query/qlc.ex" rel="noreferrer" target="_blank">https://gitlab.com/patatoid/ecto3_mnesia/-/blob/master/lib/ecto/adapters/mnesia/query/qlc.ex</a><br>
<br>
Cheers,<br>
Jean<br>
<br>
<br>
Le mercredi 06 octobre 2021 à 14:37 +0200, Jean Parpaillon a écrit :<br>
> Dear all,<br>
> With Pascal in CC, I'm working on an elixir Ecto to mnesia adapter.<br>
> <br>
> Ecto queries are represented as an AST, which is compiled into database<br>
> specific calls:<br>
> * `mnesia:read/2` for basic queries<br>
> * Qlc for others<br>
> <br>
> We are facing huge performance issues and have tried to optimize the<br>
> library in many ways, but it looks like the performance bottleneck is<br>
> now in Qlc itself.<br>
> <br>
> The library now includes a benchmark. In this merge request, you will<br>
> find a benchmark that fetches 1000 records randomly from a table, by a<br>
> non-key column.<br>
> <br>
> There a 3 implementations:<br>
> * mnesia.get.int.idx -> `mnesia:index_read/3`<br>
> * qlc.get.int.idx -> use hand written qlc query ([Rec || {'_', '_',<br>
> IndexedIntField, '_', '_', '_', '_', '_'} = Rec <-<br>
> mnesia:table('test_table'), IndexedIntField == B0].')<br>
> * ecto.get.int.idx -> use Ecto adapter<br>
> <br>
> Here is the result of the benchmark:<br>
> <br>
> Name                         ips        average  deviation        <br>
> median         99th %<br>
> mnesia.get.int.idx         30.38       0.0329 s    ±21.16%       0.0308<br>
> s       0.0655 s<br>
> qlc.get.int.idx             0.62         1.61 s     ±0.81%         1.60<br>
> s         1.63 s<br>
> ecto.get.int.idx            0.34         2.94 s     ±1.90%         2.93<br>
> s         3.02 s<br>
> <br>
> Comparison: <br>
> mnesia.get.int.idx         30.38<br>
> qlc.get.int.idx             0.62 - 48.82x slower +1.57 s<br>
> ecto.get.int.idx            0.34 - 89.44x slower +2.91 s<br>
> <br>
> Memory usage statistics:<br>
> <br>
> Name                       average  deviation         median        <br>
> 99th %<br>
> mnesia.get.int.idx      0.00496 GB     ±0.10%     0.00496 GB    <br>
> 0.00497 GB<br>
> qlc.get.int.idx            1.71 GB     ±0.00%        1.71 GB       <br>
> 1.71 GB<br>
> ecto.get.int.idx           1.55 GB     ±0.00%        1.55 GB       <br>
> 1.55 GB<br>
> <br>
> Comparison: <br>
> mnesia.get.int.idx      0.00496 GB<br>
> qlc.get.int.idx            1.71 GB - 344.04x memory usage +1.70 GB<br>
> ecto.get.int.idx           1.55 GB - 313.04x memory usage +1.55 GB<br>
> <br>
> We can observe a small overhead between plain qlc and Ecto, but a MAJOR<br>
> performance drop between plain mnesia and qlc.<br>
> <br>
> Has this performance drop been observed in other applications ? May I<br>
> expect better performance with a different usage of qlc ?<br>
> <br>
> I may try to fix qlc, would some bottlenecks have been identified or<br>
> someone give me some hints on where those bottlenecks can be.<br>
> <br>
> I have also had a look at erl_syntax module from syntax_tools. Does it<br>
> seem a better approach to use erl_syntax for generating query handles,<br>
> rather than existing qlc parse transform ?<br>
> <br>
> I need and willing to help on this topic, and every hint is welcome.<br>
> <br>
> Best regards,<br>
<br>
-- <br>
Jean Parpaillon<br>
--<br>
Software Engineering Consultant<br>
Chairman @ OW2 Consortium - <a href="https://ow2.org/" rel="noreferrer" target="_blank">https://ow2.org/</a><br>
Président L'Insolite Compagnie - <a href="https://insolitecompagnie.com" rel="noreferrer" target="_blank">https://insolitecompagnie.com</a><br>
--<br>
Phone: +33 6 30 10 92 86<br>
linkedin: <a href="http://www.linkedin.com/in/jeanparpaillon/en" rel="noreferrer" target="_blank">http://www.linkedin.com/in/jeanparpaillon/en</a><br>
<br>
</blockquote></div>