docs: move folding predicates topic

(cherry picked from commit fe18c18619)
This commit is contained in:
james
2020-01-02 15:09:51 +00:00
parent 2900dced18
commit 8e6e6d356d
3 changed files with 42 additions and 37 deletions

View File

@@ -13,6 +13,5 @@ Topics on advanced uses of QL. These topics assume that you are familiar with QL
- :doc:`Semantics of abstract classes <abstract-classes>`
- :doc:`Choosing appropriate ways to constrain types <constraining-types>`
- :doc:`Determining the most specific types of a variable <determining-specific-types-variables>`
- :doc:`Folding predicates <folding-predicates>`
- :doc:`Understanding the difference between != and not(=) <equivalence>`
- :doc:`Monotonic aggregates in QL <monotonic-aggregates>`

View File

@@ -1,36 +0,0 @@
Folding predicates
==================
Sometimes you can assist the query optimizer by "folding" parts of predicates out into their own predicates.
The general principle is to split off chunks of work that are "linear" - that is, there is not too much branching - and tightly bound, such that the chunks then join with each other on as many variables as possible.
Example
-------
.. code-block:: ql
predicate similar(Element e1, Element e2) {
e1.getName() = e2.getName() and
e1.getFile() = e2.getFile() and
e1.getLocation().getStartLine() = e2.getLocation().getStartLine()
}
Here we explore some lookups on ``Element``\ s. Going from ``Element -> File`` and ``Element -> Location -> StartLine`` are linear: there is only one ``File`` for each ``Element``, and one ``Location`` for each ``Element``, etc. However, as written it is difficult for the optimizer to pick out the best ordering here. We want to do the quick, linear parts first, and then join on the resultant larger tables, rather than joining first and then doing the linear lookups. We can precipitate this kind of ordering by rewriting the above predicate as follows:
.. code-block:: ql
predicate locInfo(Element e, string name, File f, int startLine) {
name = e.getName() and
f = e.getFile() and
startLine = e.getLocation().getStartLine()
}
predicate sameLoc(Element e1, Element e2) {
exists(string name, File f, int startLine |
locInfo(e1, name, f, startLine) and
locInfo(e2, name, f, startLine)
)
}
Now the structure we want is clearer: we've separated out the easy part into its own predicate ``locInfo``, and the main predicate is just a larger join.

View File

@@ -81,6 +81,48 @@ That is, you should define a *base case* that allows the predicate to *bottom ou
The query optimizer has special data structures for dealing with `transitive closures <https://help.semmle.com/QL/ql-handbook/recursion.html#transitive-closures>`__.
If possible, use a transitive closure over a simple recursive predicate, as it is likely to be computed faster.
Folding predicates
~~~~~~~~~~~~~~~~~~
Sometimes you can assist the query optimizer by "folding" parts of large predicates out into smaller predicates.
The general principle is to split off chunks of work that are:
- **linear**, so that there is not too much branching.
- **tightly bound**, so that the chunks join with each other on as many variables as possible.
In the following example, we explore some lookups on two ``Element``\ s:
.. code-block:: ql
predicate similar(Element e1, Element e2) {
e1.getName() = e2.getName() and
e1.getFile() = e2.getFile() and
e1.getLocation().getStartLine() = e2.getLocation().getStartLine()
}
Going from ``Element -> File`` and ``Element -> Location -> StartLine`` is linear--that is, there is only one ``File``, ``Location``, etc. for each ``Element``.
However, as written it is difficult for the optimizer to pick out the best ordering. Generally, we want to do the quick, linear parts first, and then join on the resultant larger tables. Joining first and then doing the linear lookups later would likely result in poor performance. We can initiate this kind of ordering by splitting the above predicate as follows:
.. code-block:: ql
predicate locInfo(Element e, string name, File f, int startLine) {
name = e.getName() and
f = e.getFile() and
startLine = e.getLocation().getStartLine()
}
predicate sameLoc(Element e1, Element e2) {
exists(string name, File f, int startLine |
locInfo(e1, name, f, startLine) and
locInfo(e2, name, f, startLine)
)
}
Now the structure we want is clearer. We've separated out the easy part into its own predicate ``locInfo``, and the main predicate ``sameLoc`` is just a larger join.
Further information
-------------------