Skip to content

Commit d56c02b

Browse files
author
james
committed
docs: start work on debugging queries topic
1 parent 5ecfaed commit d56c02b

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
Query writing: common performance issues
2+
========================================
3+
4+
This topic offers some simple tips on how to avoid commons problems that can affect the performance of your queries.
5+
Before reading the tips below, it is worth reiterating a few important points about CodeQL and the QL language:
6+
7+
- In CodeQL `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ and `classes <https://help.semmle.com/QL/ql-handbook/types.html#classes>`__ are all just database `relations <https://en.wikipedia.org/wiki/Relation_(database)>`__---that is, sets of tuples in a table. Large predicates generate tables with large numbers of rows, and are therefore expensive to compute.
8+
- The QL language is implemented using standard database operations and `relational algebra <https://en.wikipedia.org/wiki/Relational_algebra>`__ (such as join, projection, union, etc.). For further information about query languages and databases, see :doc:`About QL <../about-ql>`.
9+
- Queries is evaluated *bottom-up*, which means that a predicate is not evaluated until **all** of the predicates that it depends on are evaluated. For more information on query evaluation, see `Evaluation of QL programs <https://help.semmle.com/QL/ql-handbook/evaluation.html>`__ in the QL handbook.
10+
11+
Performance tips
12+
----------------
13+
14+
Follow the guidelines below to ensure that you don't get get tripped up by the most common CodeQL performance pitfalls.
15+
16+
Eliminate cartesian products
17+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18+
19+
The performance of a predicate can often be judged by considering roughly how many results it has.
20+
One way of creating badly performing predicates is by using two variables without relating them in any way, or only relating them using a negation.
21+
This leads to computing the `Cartesian product <https://en.wikipedia.org/wiki/Cartesian_product>`__ between the sets of possible values for each variable, potentially generating a huge table of results.
22+
23+
This can occur whenever you inadvertently fail to specify restrictions on your variables.
24+
25+
For instance, in the following case none of the parameters are related in the example predicate ``methodAndAClass``, and therefore the results are unrestricted::
26+
27+
// BAD! Cross-product
28+
predicate methodAndAClass(Method m, Class c) {
29+
any()
30+
}
31+
32+
This example shows a similar mistake in a member predicate::
33+
34+
class Foo extends Class {
35+
...
36+
// BAD! Does not use ‘this’
37+
Method getToString() {
38+
result.getName().matches("ToString")
39+
}
40+
...
41+
}
42+
43+
Here, the class ``Method getToString()`` is equivalent to ``predicate getToString(Class this, Method result)``, in which the parameters are unrestricted.
44+
To avoid making this mistake, ``this`` should be restricted in the member predicate ``getToString()`` on the class ``Foo``.
45+
46+
Finally, consider a predicate of the following form::
47+
48+
predicate p(T1 x1, T2 x2) {
49+
<disjunction 1> or
50+
<disjunction 2> or
51+
...
52+
}
53+
54+
In this situation, if ``x1`` and ``x2`` are not mentioned in all ``<disjunction i>`` terms, the compiler will produce the Cartesian product between what you wanted and all possible values of the unused parameter.
55+
56+
Use specific types
57+
~~~~~~~~~~~~~~~~~~
58+
59+
`Types <https://help.semmle.com/QL/ql-handbook/types.html>`__ provide an upper bound on the size of a relation.
60+
This helps the query optimizer be more effective, so it's generally good to use the most specific types possible. For example::
61+
62+
predicate foo(LoggingCall e)
63+
64+
is preferred over::
65+
66+
predicate foo(Expr e)
67+
68+
From the type context, the query optimizer deduces that some parts of the program are redundant and removes them, or **specializes** them.
69+
70+
Avoid complex recursion
71+
~~~~~~~~~~~~~~~~~~~~~~~
72+
73+
`Recursion <https://help.semmle.com/QL/ql-handbook/recursion.html>`__ is about self-referencing definitions.
74+
It can be extremely powerful as long as it is used appropriately.
75+
On the whole, you should try to make recursive predicates as simple as possible.
76+
That is, you should define a **base case** that allows the predicate to *bottom out*, along with a single **recursive call**::
77+
78+
int depth(Stmt s) {
79+
exists(Callable c | c.getBody() = s | result = 0) // base case
80+
or
81+
result = depth(s.getParent()) + 1 // recursive case
82+
}
83+
84+
.. pull-quote:: Note
85+
86+
The query optimizer has special data structures for dealing with `transitive closures <https://help.semmle.com/QL/ql-handbook/recursion.html#transitive-closures>`__.
87+
If possible, use a transitive closure over a simple recursive predicate, as it is likely to be computed faster.
88+
89+
Further information
90+
-------------------
91+
92+
- Find out more about QL in the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__ and `QL language specification <https://help.semmle.com/QL/ql-spec/language.html>`__.

docs/language/learn-ql/writing-queries/writing-queries.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Information for query writers
1515
../intro-to-data-flow
1616
select-statement
1717
../locations
18+
debugging-queries
1819

1920

2021
Visit `Learning CodeQL <https://help.semmle.com/QL/learn-ql/>`__ to find basic information about CodeQL. This includes information about the underlying query language QL, as well as help and advice on writing queries for specific programming languages.

0 commit comments

Comments
 (0)