@@ -3,85 +3,90 @@ package org.opalj
3
3
package fpcf
4
4
package scheduling
5
5
6
+ import scala .collection .mutable
7
+ import scala .util .control .Breaks
8
+
6
9
/**
7
- * Independent Phase Merge Scheduling (IPMS) Strategy.
8
- * Merges independent batches to optimize parallelism .
10
+ * Base class for scheduling strategies that create multiple computation phases by merging multiple smaller independent
11
+ * phases into one .
9
12
*/
10
- abstract class IndependentPhaseMergeScheduling extends MaximumPhaseScheduling {
11
-
12
- override def mergeIndependentBatches (
13
- batchCount : Int ,
14
- dependencyGraph : Map [Int , List [Int ]],
15
- nodeIndexMap : Map [Int , List [Int ]]
16
- ): (Map [Int , List [Int ]], Map [Int , List [Int ]]) = {
17
- var transformingMap = nodeIndexMap
18
- var counter = batchCount
19
-
20
- var updatedGraph : Map [Int , List [Int ]] = dependencyGraph
21
- var couldBeMerged : List [(Int , Int )] = List .empty
22
-
23
- do {
24
- def getUses (batch : Int , graph : Map [Int , List [Int ]]): Set [Int ] = {
25
- val directUses = graph.getOrElse(batch, List .empty).toSet
26
- val recursiveUses = directUses.flatMap(otherBatch => getUses(otherBatch, graph))
27
- directUses ++ recursiveUses
28
- }
29
-
30
- var map : Map [Int , Set [Int ]] = Map .empty
31
- updatedGraph.foreach { batch =>
32
- val tempUses = getUses(batch._1, updatedGraph)
33
- map = map + (batch._1 -> tempUses)
34
- }
35
-
36
- couldBeMerged = List .empty
37
- map.foreach { batch =>
38
- map.foreach { subBatch =>
39
- if (subBatch != batch) {
40
- if ((! subBatch._2.contains(batch._1)) && (! batch._2.contains(subBatch._1))) {
41
- if (! couldBeMerged.contains((subBatch._1, batch._1))) {
42
- couldBeMerged = couldBeMerged :+ (batch._1, subBatch._1)
43
- }
44
-
45
- }
46
- }
47
-
13
+ abstract class PhaseMergeScheduling extends MultiplePhaseScheduling {
14
+
15
+ /**
16
+ * Merges independent batches based on a merging strategy.
17
+ */
18
+ override def refineDependencies (
19
+ initialPhaseDependencyGraph : Iterable [(Int , List [Int ])],
20
+ initialPhaseIndexToAnalyses : Iterable [(Int , Set [Int ])]
21
+ ): (Map [Int , List [Int ]], Map [Int , Set [Int ]]) = {
22
+ val phaseIndexToAnalyses = initialPhaseIndexToAnalyses.to(mutable.Map )
23
+ val phaseDependencyGraph = initialPhaseDependencyGraph.to(mutable.Map )
24
+
25
+ var nextPhaseIndex = initialPhaseIndexToAnalyses.size
26
+
27
+ def allDependencies (phase : Int ): Set [Int ] = {
28
+ val directDependencies = phaseDependencyGraph(phase).toSet
29
+ val indirectDependencies = directDependencies.flatMap(phase2 => allDependencies(phase2))
30
+ directDependencies ++ indirectDependencies
31
+ }
32
+
33
+ val breaks = new Breaks
34
+ import breaks .{break , breakable }
35
+ breakable {
36
+ while (true ) {
37
+ val independentPhases = for {
38
+ phaseIndex1 <- phaseDependencyGraph.keysIterator
39
+ dependencies1 = allDependencies(phaseIndex1)
40
+ phaseIndex2 <- phaseDependencyGraph.keysIterator
41
+ if ! dependencies1.contains(phaseIndex2)
42
+ if phaseIndex1 < phaseIndex2 // Ensure we only look at each pair once
43
+ dependencies2 = allDependencies(phaseIndex2)
44
+ if ! dependencies2.contains(phaseIndex1)
45
+ } yield (phaseIndex1, phaseIndex2)
46
+
47
+ if (independentPhases.isEmpty)
48
+ break()
49
+
50
+ val (phase1, phase2) = nextPhasesToMerge(independentPhases, phaseIndexToAnalyses)
51
+
52
+ phaseIndexToAnalyses(nextPhaseIndex) = phaseIndexToAnalyses(phase1) ++ phaseIndexToAnalyses(phase2)
53
+ phaseIndexToAnalyses -= phase1
54
+ phaseIndexToAnalyses -= phase2
55
+
56
+ phaseDependencyGraph(nextPhaseIndex) = (phaseDependencyGraph(phase1) ++ phaseDependencyGraph(phase2))
57
+ .distinct
58
+ phaseDependencyGraph -= phase1
59
+ phaseDependencyGraph -= phase2
60
+
61
+ // Map dependencies on old phases to new phase
62
+ phaseDependencyGraph.foreach { case (key, values) =>
63
+ phaseDependencyGraph(key) = values.map(v => if (v == phase1 || v == phase2) nextPhaseIndex else v)
48
64
}
49
65
66
+ nextPhaseIndex = nextPhaseIndex + 1
50
67
}
68
+ }
51
69
52
- if (couldBeMerged.nonEmpty) {
53
- val toBeMerged = nextPhasesToMerge(couldBeMerged, transformingMap)
54
-
55
- val tempTransformation_2 = (transformingMap.get(toBeMerged._1).head ++
56
- transformingMap.get(toBeMerged._2).head).distinct
57
- transformingMap =
58
- transformingMap - toBeMerged._1 - toBeMerged._2
59
- transformingMap = transformingMap + (counter -> tempTransformation_2)
60
-
61
- val tempGraph_2 : List [Int ] = (updatedGraph.get(toBeMerged._1).head ++
62
- updatedGraph.get(toBeMerged._2).head).distinct
63
- updatedGraph = updatedGraph - toBeMerged._1 - toBeMerged._2
64
- updatedGraph = updatedGraph + (counter -> tempGraph_2)
65
-
66
- def replaceIdInMap (oldId : Int , newId : Int ): Unit = {
67
- updatedGraph = updatedGraph.map { case (key, values) =>
68
- key -> values.map(v => if (v == oldId) newId else v)
69
- }
70
- }
70
+ (phaseDependencyGraph.toMap, phaseIndexToAnalyses.toMap)
71
+ }
71
72
72
- replaceIdInMap(toBeMerged._1, counter)
73
- replaceIdInMap(toBeMerged._2, counter)
74
- counter = counter + 1
75
- }
73
+ def nextPhasesToMerge (
74
+ independentPhases : Iterator [(Int , Int )],
75
+ phaseIndexToAnalyses : scala.collection.Map [Int , Set [Int ]]
76
+ ): (Int , Int )
77
+ }
76
78
77
- } while (couldBeMerged.nonEmpty)
79
+ /**
80
+ * Independent Phase Merge Scheduling (IPMS) Strategy.
81
+ * Merges independent batches to optimize parallelism.
82
+ */
83
+ object IndependentPhaseMergeScheduling extends PhaseMergeScheduling {
78
84
79
- (updatedGraph, transformingMap)
85
+ def nextPhasesToMerge (
86
+ independentPhases : Iterator [(Int , Int )],
87
+ phaseIndexToAnalyses : scala.collection.Map [Int , Set [Int ]]
88
+ ): (Int , Int ) = {
89
+ independentPhases.next()
80
90
}
81
91
82
- def nextPhasesToMerge (independentPhases : List [(Int , Int )], transformingMap : Map [Int , List [Int ]]): (Int , Int ) = {
83
- independentPhases.head
84
- }
85
92
}
86
-
87
- object IndependentPhaseMergeScheduling extends IndependentPhaseMergeScheduling
0 commit comments