Skip to content

Commit 7b61e25

Browse files
author
Dominik Helm
committed
Improve scheduling strategies
1 parent b362105 commit 7b61e25

File tree

7 files changed

+229
-306
lines changed

7 files changed

+229
-306
lines changed

OPAL/si/src/main/resources/reference.conf

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,8 @@ org.opalj {
2222

2323
AnalysisScenario {
2424
AnalysisAutoConfig = false
25-
26-
// For scheduling the analysis with different batching strategies
27-
// SPS - Single Phase Scheduling
28-
// MPS - Maximum Phase Scheduling
29-
// IPMS - Independent Phase Merge Scheduling
30-
// SPMS - Smallest Phase Merge Scheduling
31-
ScheduleStrategy = "org.opalj.fpcf.scheduling.SmallestPhaseMergeScheduling"
32-
ScheduleLazyInMultipleBatches = true
25+
SchedulingStrategy = "org.opalj.fpcf.scheduling.SmallestPhaseMergeScheduling"
26+
ScheduleLazyInMultiplePhases = true
3327
}
3428
}
3529

OPAL/si/src/main/scala/org/opalj/fpcf/AnalysisScenario.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package fpcf
55
import com.typesafe.config.Config
66

77
import org.opalj.fpcf.AnalysisScenario.AnalysisAutoConfigKey
8-
import org.opalj.fpcf.AnalysisScenario.AnalysisScheduleStrategyKey
8+
import org.opalj.fpcf.AnalysisScenario.AnalysisSchedulingStrategyKey
99
import org.opalj.fpcf.scheduling.SchedulingStrategy
1010
import org.opalj.graphs.Graph
1111
import org.opalj.log.LogContext
@@ -315,8 +315,7 @@ object AnalysisScenario {
315315
final val ConfigKeyPrefix = "org.opalj.fpcf.AnalysisScenario."
316316

317317
final val AnalysisAutoConfigKey = s"${ConfigKeyPrefix}AnalysisAutoConfig"
318-
final val AnalysisScheduleStrategyKey = s"${ConfigKeyPrefix}ScheduleStrategy"
319-
final val AnalysisScheduleLazyTransformerInMultipleBatchesKey = s"${ConfigKeyPrefix}ScheduleLazyInMultipleBatches"
318+
final val AnalysisSchedulingStrategyKey = s"${ConfigKeyPrefix}SchedulingStrategy"
320319

321320
/**
322321
* @param analyses The set of analyses that should be executed as part of this analysis scenario.

OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/IndependentPhaseMergeScheduling.scala

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,85 +3,90 @@ package org.opalj
33
package fpcf
44
package scheduling
55

6+
import scala.collection.mutable
7+
import scala.util.control.Breaks
8+
69
/**
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.
912
*/
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)
4864
}
4965

66+
nextPhaseIndex = nextPhaseIndex + 1
5067
}
68+
}
5169

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+
}
7172

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+
}
7678

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 {
7884

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()
8090
}
8191

82-
def nextPhasesToMerge(independentPhases: List[(Int, Int)], transformingMap: Map[Int, List[Int]]): (Int, Int) = {
83-
independentPhases.head
84-
}
8592
}
86-
87-
object IndependentPhaseMergeScheduling extends IndependentPhaseMergeScheduling

0 commit comments

Comments
 (0)