From 9f228d39011fbf9918d03eb2d483b85a6f3e552f Mon Sep 17 00:00:00 2001 From: CyrilFerlicot Date: Sat, 14 Feb 2026 00:25:10 +0100 Subject: [PATCH 1/4] New speed up by treating some nodes by batch. --- src/Famix-CallGraphs/FamixJavaCHABuilder.class.st | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st index dd33610..ea336bc 100644 --- a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st +++ b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st @@ -92,15 +92,15 @@ FamixJavaCHABuilder >> addErrorTo: aNode signature: signature kind: type excepti { #category : 'building' } FamixJavaCHABuilder >> build [ - self entryPoints do: [ :aNode | worklist at: aNode hash put: aNode ]. [ worklist isEmpty ] whileFalse: [ - | node | - node := worklist anyOne. - worklist removeKey: node hash. - visited at: node hash put: node. - self computeCallGraphOf: node ]. + "Usually we would treat only one node at a time but this is more costly than doing by batch" + | toVisit | + toVisit := worklist values. + worklist removeAll. + toVisit do: [ :node | visited at: node hash put: node ]. "If we do both things in the same loop sone nodes will be missing in `visited` and could lead to errors later." + toVisit do: [ :node | self computeCallGraphOf: node ] ]. "If errors occured, inspect them." graph errorNodes ifNotEmpty: [ :collection | collection inspect ]. From aa554984360f777e10698582ad384b0ec941c995 Mon Sep 17 00:00:00 2001 From: CyrilFerlicot Date: Sat, 14 Feb 2026 00:29:36 +0100 Subject: [PATCH 2/4] Add two caches --- src/Famix-CallGraphs/FamixJavaCHABuilder.class.st | 3 +++ src/Famix-CallGraphs/FamixJavaMethod.extension.st | 2 +- src/Famix-CallGraphs/FamixTMethod.extension.st | 4 ++-- .../FamixTWithInheritances.extension.st | 15 ++++++++------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st index ea336bc..dba82b1 100644 --- a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st +++ b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st @@ -105,6 +105,9 @@ FamixJavaCHABuilder >> build [ "If errors occured, inspect them." graph errorNodes ifNotEmpty: [ :collection | collection inspect ]. + (self entryPoints anyOne realMethod mooseModel allUsing: FamixTMethod) do: [ :type | type removeCache: #orderedParameters ]. + (self entryPoints anyOne realMethod mooseModel allUsing: FamixTWithInheritances) do: [ :type | type removeCache: #applicableConcretizations ]. + ^ graph ] diff --git a/src/Famix-CallGraphs/FamixJavaMethod.extension.st b/src/Famix-CallGraphs/FamixJavaMethod.extension.st index 63e4fb7..d89be78 100644 --- a/src/Famix-CallGraphs/FamixJavaMethod.extension.st +++ b/src/Famix-CallGraphs/FamixJavaMethod.extension.st @@ -12,7 +12,7 @@ FamixJavaMethod >> isEquivalentTo: aMethod [ self parameters size = aMethod parameters size ifFalse: [ ^ false ]. "We need to ensure the type of each parameter is the same after applying the concretizations to the type parameters" - self orederedParameters with: aMethod orederedParameters do: [ :param1 :param2 | + self orderedParameters with: aMethod orderedParameters do: [ :param1 :param2 | | realType1 realType2 | realType1 := param1 typing declaredType. diff --git a/src/Famix-CallGraphs/FamixTMethod.extension.st b/src/Famix-CallGraphs/FamixTMethod.extension.st index c731cc3..29a2ba7 100644 --- a/src/Famix-CallGraphs/FamixTMethod.extension.st +++ b/src/Famix-CallGraphs/FamixTMethod.extension.st @@ -7,7 +7,7 @@ FamixTMethod >> asCallGraphNode [ ] { #category : '*Famix-CallGraphs' } -FamixTMethod >> orederedParameters [ +FamixTMethod >> orderedParameters [ - ^ self parameters sorted: [ :method | method sourceAnchor startPos ] ascending + ^ self cacheAt: #orderedParameters ifAbsentPut: [ self parameters sorted: [ :method | method sourceAnchor startPos ] ascending ] ] diff --git a/src/Famix-CallGraphs/FamixTWithInheritances.extension.st b/src/Famix-CallGraphs/FamixTWithInheritances.extension.st index 127a01f..0163533 100644 --- a/src/Famix-CallGraphs/FamixTWithInheritances.extension.st +++ b/src/Famix-CallGraphs/FamixTWithInheritances.extension.st @@ -3,11 +3,12 @@ Extension { #name : 'FamixTWithInheritances' } { #category : '*Famix-CallGraphs' } FamixTWithInheritances >> applicableConcretizations [ - | concretizations | - concretizations := Set new. - self withSuperclassesDo: [ :class | - class superInheritances - select: [ :inheritance | inheritance isParametricAssociation ] - thenDo: [ :inheritance | concretizations addAll: inheritance concretizations ] ]. - ^ concretizations + ^ self cacheAt: #applicableConcretizations ifAbsentPut: [ + | concretizations | + concretizations := Set new. + self withSuperclassesDo: [ :class | + class superInheritances + select: [ :inheritance | inheritance isParametricAssociation ] + thenDo: [ :inheritance | concretizations addAll: inheritance concretizations ] ]. + concretizations ] ] From 73aa2284b9b60755b2bcc8b83c19385614bd45b2 Mon Sep 17 00:00:00 2001 From: CyrilFerlicot Date: Sat, 14 Feb 2026 00:32:13 +0100 Subject: [PATCH 3/4] Optimize method lookup of CHA --- .../FamixJavaCHABuilder.class.st | 1 + .../FamixJavaMethod.extension.st | 24 +++++++------------ src/Famix-CallGraphs/FamixTType.extension.st | 18 ++++++++++---- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st index dba82b1..82677de 100644 --- a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st +++ b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st @@ -105,6 +105,7 @@ FamixJavaCHABuilder >> build [ "If errors occured, inspect them." graph errorNodes ifNotEmpty: [ :collection | collection inspect ]. + (self entryPoints anyOne realMethod mooseModel allUsing: FamixTType) do: [ :type | type removeCache: #methodsMap ]. (self entryPoints anyOne realMethod mooseModel allUsing: FamixTMethod) do: [ :type | type removeCache: #orderedParameters ]. (self entryPoints anyOne realMethod mooseModel allUsing: FamixTWithInheritances) do: [ :type | type removeCache: #applicableConcretizations ]. diff --git a/src/Famix-CallGraphs/FamixJavaMethod.extension.st b/src/Famix-CallGraphs/FamixJavaMethod.extension.st index d89be78..6b176ef 100644 --- a/src/Famix-CallGraphs/FamixJavaMethod.extension.st +++ b/src/Famix-CallGraphs/FamixJavaMethod.extension.st @@ -1,13 +1,16 @@ Extension { #name : 'FamixJavaMethod' } { #category : '*Famix-CallGraphs' } -FamixJavaMethod >> isEquivalentTo: aMethod [ - "A method is equivalent if their name are the same and parameters are the same. +FamixJavaMethod >> isImplementationOf: aMethod [ + "Return true if I am an implemented method equivalent to the method as parameter. - There is a tricky case with parametrics because in case a type is a type parameter, we need to find its concretization to have the real type in the context." + A method is equivalent if their name are the same and parameters are the same. We already verified the name before getting here. - self name size = aMethod name size ifFalse: [ ^ false ]. "This might seems useless since we compare the name right after, but we pass so much in this method that it has an impact on perfs. It is faster to compare integer than strings" - self name = aMethod name ifFalse: [ ^ false ]. + There is a tricky case with parametrics because in case a type is a type parameter, we need to find its concretization to have the real type in the context." + + self isMethodDefinition ifFalse: [ ^ false ]. "We reject method declarations" + + self == aMethod ifTrue: [ ^ true ]. "fastest case" self parameters size = aMethod parameters size ifFalse: [ ^ false ]. @@ -36,14 +39,3 @@ FamixJavaMethod >> isEquivalentTo: aMethod [ ^ true ] - -{ #category : '*Famix-CallGraphs' } -FamixJavaMethod >> isImplementationOf: aMethod [ - "Return true if I am an implemented method equivalent to the method as parameter." - - self isMethodDefinition ifFalse: [ ^false ]. "Return false if I am a declaration and not an implementation." - - self == aMethod ifTrue: [ ^ true ]. "fastest case" - - ^ self isEquivalentTo: aMethod -] diff --git a/src/Famix-CallGraphs/FamixTType.extension.st b/src/Famix-CallGraphs/FamixTType.extension.st index 2024df7..374eedc 100644 --- a/src/Famix-CallGraphs/FamixTType.extension.st +++ b/src/Famix-CallGraphs/FamixTType.extension.st @@ -3,10 +3,20 @@ Extension { #name : 'FamixTType' } { #category : '*Famix-CallGraphs' } FamixTType >> implementesMethodEquivalentTo: aMethod [ - ^ self methods - detect: [ :method | method isImplementationOf: aMethod ] - ifFound: [ :method | true ] - ifNone: [ false ] + ^ (self methodsNamed: aMethod name) anySatisfy: [ :method | method isImplementationOf: aMethod ] +] + +{ #category : '*Famix-CallGraphs' } +FamixTType >> methodsNamed: aName [ + "We use a cache for speed reasons. We will probably get multiple time through here and we need to have a fast way to get the methods of the right name. + + Checking the size before the name for speed reasons." + + | nameSize | + nameSize := aName size. + ^ (self cacheAt: #methodsMap ifAbsentPut: [ IdentityDictionary new ]) + at: aName + ifAbsentPut: [ self methods select: [ :method | method name size = nameSize and: [ method name = aName ] ] ] ] { #category : '*Famix-CallGraphs' } From 08e4c083e4c7ffa1b2c4734ac14c43a9d7df0fda Mon Sep 17 00:00:00 2001 From: CyrilFerlicot Date: Sat, 14 Feb 2026 16:47:34 +0100 Subject: [PATCH 4/4] Fix integration to Famix-Bridge --- README.md | 6 ++++++ resources/documentation/UserDocumentation.md | 7 +++++++ src/Famix-CallGraphs/FamixJavaCHABuilder.class.st | 14 ++++++++++---- src/Famix-CallGraphs/MooseModel.extension.st | 10 ++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/Famix-CallGraphs/MooseModel.extension.st diff --git a/README.md b/README.md index d039f02..9bc81fb 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,12 @@ Or, using the moose name of the entity: (FamixJavaCHABuilder entryPoint: (tagcollections31 entityNamed: #'org.apache.commons.collections.map.HashedMap.readObject(ObjectInputStream)')) build. ``` +Some caches are created during the building. If you wish to save space, you can flush them executing: + +```smalltalk + model resetCallGraphCaches +``` + ### More Documentation You can find more documentation here: [documentation](resources/documentation/UserDocumentation.md) diff --git a/resources/documentation/UserDocumentation.md b/resources/documentation/UserDocumentation.md index 4e23d7f..e900353 100644 --- a/resources/documentation/UserDocumentation.md +++ b/resources/documentation/UserDocumentation.md @@ -30,6 +30,13 @@ Examples: (FamixJavaCHABuilder entryPoint: (tagcollections31 entityNamed: #'org.apache.commons.collections.map.HashedMap.readObject(ObjectInputStream)')) build. ``` +The algo is building some caches in the caches of the entities. Those caches are kept after building a call graph in case multiple needs to be built. +If you wish to flush the caches to same some memory once the call graph is built you can execute: + +```smalltalk + model resetCallGraphCaches +``` + ## CallGraph result Once a CallGraph algorithm was applied, one will get an instance of `FamixCallGraph` as a result. diff --git a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st index 82677de..10c72f5 100644 --- a/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st +++ b/src/Famix-CallGraphs/FamixJavaCHABuilder.class.st @@ -18,6 +18,16 @@ For example: build ``` +# Caching + +The algo is building some caches in the caches of the entities. Those caches are kept after building a call graph in case multiple needs to be built. + +If you wish to flush the caches to same some memory once the call graph is built you can execute: + +```st + model resetCallGraphCaches +``` + # Implementation detail Originaly the implementation was using an `OrderedCollection` for the worklist and a `Set` for the visited list. But to check if we already have a node created for a method and kind it was taking a lot of time. @@ -105,10 +115,6 @@ FamixJavaCHABuilder >> build [ "If errors occured, inspect them." graph errorNodes ifNotEmpty: [ :collection | collection inspect ]. - (self entryPoints anyOne realMethod mooseModel allUsing: FamixTType) do: [ :type | type removeCache: #methodsMap ]. - (self entryPoints anyOne realMethod mooseModel allUsing: FamixTMethod) do: [ :type | type removeCache: #orderedParameters ]. - (self entryPoints anyOne realMethod mooseModel allUsing: FamixTWithInheritances) do: [ :type | type removeCache: #applicableConcretizations ]. - ^ graph ] diff --git a/src/Famix-CallGraphs/MooseModel.extension.st b/src/Famix-CallGraphs/MooseModel.extension.st new file mode 100644 index 0000000..9902bdd --- /dev/null +++ b/src/Famix-CallGraphs/MooseModel.extension.st @@ -0,0 +1,10 @@ +Extension { #name : 'MooseModel' } + +{ #category : '*Famix-CallGraphs' } +MooseModel >> resetCallGraphCaches [ + "Flushes the caches used by callgraph builders." + + (self allUsing: FamixTType) do: [ :type | type removeCache: #methodsMap ]. + (self allUsing: FamixTMethod) do: [ :type | type removeCache: #orderedParameters ]. + (self allUsing: FamixTWithInheritances) do: [ :type | type removeCache: #applicableConcretizations ] +]