Problem: Performance discrepancy when filtering by getCompilationUnit vs. getSignature in a query #14071
-
Hello, I wrote a query to:
My query works perfectly if you specify the signatures of the focal methods you're targeting at by adding something like However when you don't specify the focal method, and you run my query directly, it will be like going into an infinite loop, running forever without any query result returned. Then I thought, maybe it is because apache/shardingsphere-elasticjob is too complex for this task.(Actually it is not quite complex, the whole project has only 3500+ callees) So I tried scoping my query to a specific class/compilationUnit. That's when the problem I mentioned in the title came up. I chose a very simple class named This is very odd, since "DataSourceConfiguration" only has one focal method "equals(java.lang.Object)". There should not be any performance discrepancy between adding To sum up, limiting the scope from all classes of the whole project to only one class did not give us any improvement on running-forever problem. And using getCompilationUnit vs. getSignature to specify the same one method gives us performance discrepancy. Note that this performance discrepancy and running-forever problem disappeared when I commented out all the parts related to My query is down below. Any insights or assistance on this matter would be greatly appreciated. Thank you!!!
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
Your fundamental problem is that cycles in the call graph (due to real recursion, or apparent recursion via virtual dispatch that can't really happen in practice) mean that There are two options here depending on whether you care about such loops: take the shortest distance, or limit the distance to some fixed maximum value using a simple For shortest distance calculations there is a higher-order predicate import java
class TestMethod extends Method {
TestMethod() { this.getAnAnnotation().toString().matches("Test") }
}
class AssertMethodCall extends MethodAccess {
AssertMethodCall() {
this.getMethod().getDeclaringType().getQualifiedName().matches("%Assert%") and
this.getEnclosingCallable() instanceof TestMethod
}
}
predicate hasMethodCall(Expr expr) {
expr instanceof MethodAccess
or
exists(Expr child |
child = expr.getAChildExpr() and
hasMethodCall(child)
)
}
predicate isFocalMethod(Callable method) {
exists(AssertMethodCall assertMethodCall |
method = assertMethodCall.getAnArgument().getAChildExpr*().(MethodAccess).getMethod()
)
}
predicate polyCallsAsPredicate(Callable fromNode, Callable toNode) { fromNode.polyCalls(toNode) }
int getCallDepth(Callable start, Callable target) =
shortestDistances(isFocalMethod/1, polyCallsAsPredicate/2)(start, target, result)
from
Callable focalMethod, Callable callee, Callable directCaller, int depth_callee, Call c,
Interface interface
where
isFocalMethod(focalMethod) and
focalMethod.polyCalls+(callee) and
callee.fromSource() and
depth_callee = getCallDepth(focalMethod, callee) and
// Find the direct caller of 'callee' in the call graph
(
directCaller.polyCalls(callee) and focalMethod.polyCalls+(directCaller)
or
focalMethod = directCaller and depth_callee = 1
) and
c.getCaller() = directCaller and
(
//if c.Callee() here is an interface method, and check if it is implemented by callee
c.getCallee().getDeclaringType() = interface and
callee.getDeclaringType().getASupertype*() = interface and
c.getCallee().getSignature() = callee.getSignature()
or
// this is the case when c.Callee() is callee, not an interface
c.getCallee() = callee
)
select focalMethod.getSignature() as fm_Signature,
focalMethod.getCompilationUnit().getRelativePath() as fm_CompilationUnit, focalMethod.getBody(),
directCaller.getSignature(), directCaller.getBody(), callee.getBody(), callee.getSignature(),
depth_callee, c.getLocation().getStartLine() as where_callee_called_inside_its_directCaller
order by
// order the callees in a Breadth-first search way
fm_Signature, fm_CompilationUnit, depth_callee, where_callee_called_inside_its_directCaller Note I have excluded the other usage of So, why were your side conditions sometimes causing your original query to work anyhow? By default a predicate is evaluated for all possible values of its arguments and results; in the case of Ancillary notes on the QL above:
|
Beta Was this translation helpful? Give feedback.
-
Thank you very much!! I think the fundamental problem has been solved and I've understood that some method might have some inner loop that might cause I also want to consult you with 2 questions. I'll appreciate it a lot if you can help with them. Question 1: My previous query and the query you provided will leave |
Beta Was this translation helpful? Give feedback.
Your fundamental problem is that cycles in the call graph (due to real recursion, or apparent recursion via virtual dispatch that can't really happen in practice) mean that
getCallDepth
taken in isolation could calculate a potentially infinite number of answers for a particular (start, target) pair. In the simple example where f calls g and itself, then (f, g) has distance 1, 2, 3, 4, ...There are two options here depending on whether you care about such loops: take the shortest distance, or limit the distance to some fixed maximum value using a simple
result < 100
sort of side condition.For shortest distance calculations there is a higher-order predicate
shortestDistance
that takes a u…