Skip to content

Implementation of several scheduling methods to improve the performance of static analyses #263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 44 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
aa8d780
Fixing Error with "EagerFieldAccessInformationAnalysis", so the analy…
19Vik19 Sep 5, 2024
0d3ba57
Next Attempt
19Vik19 Sep 8, 2024
3edb263
Revert "Next Attempt"
19Vik19 Sep 8, 2024
3734dc6
Bereit für nächsten Versuch mit graph und zyklen finden ...
19Vik19 Oct 27, 2024
72b54c1
First completed approach
19Vik19 Dec 2, 2024
e72ffbd
Alle Strategien bis auf strategie 3
19Vik19 Dec 12, 2024
6ed388a
Strategie 3 funktioniert, tests wurden in PropertyComputationsSchedul…
19Vik19 Dec 20, 2024
1c1d60d
.
19Vik19 Dec 23, 2024
8d3bcb2
Merge branch '02122024' into lastbranch2024
19Vik19 Dec 23, 2024
e9a16fd
Fieldimmutability dependencies fix
19Vik19 Dec 23, 2024
b67b8c9
Immutability fix after merge
19Vik19 Dec 24, 2024
b5369ce
xcorpus runner
19Vik19 Dec 30, 2024
15f87cd
Neues Jahr neues Glück :)
19Vik19 Jan 1, 2025
7112bd4
constructor CallGraph in class CallGraph is not private anymore. For …
19Vik19 Jan 17, 2025
b885136
Prepared for testing strategie 3 true
19Vik19 Jan 18, 2025
a4bc582
Last Check
19Vik19 Jan 18, 2025
549c800
Small rollbacks
19Vik19 Jan 18, 2025
9dd0e39
Merge branch 'develop' into AUFGERÄUMT&GEUPDATED190125
19Vik19 Jan 19, 2025
091a44b
Minor changes, cleanup
19Vik19 Jan 19, 2025
222bd23
Cleanup
19Vik19 Jan 19, 2025
ac69a41
Cleanup
19Vik19 Jan 19, 2025
64783db
Documentation config
19Vik19 Jan 19, 2025
a97eef9
Bug fixes
19Vik19 Jan 20, 2025
b581e28
Cleanup and config with string-based strategy
19Vik19 Jan 20, 2025
e6f08b4
Created new Runner for immutability and Purity to schedule all analys…
19Vik19 Jan 20, 2025
688ea35
Cleanup
19Vik19 Jan 21, 2025
ba21166
Merge branch 'AUFGERÄUMT&GEUPDATED190125' into Preparation_for_the_me…
19Vik19 Jan 21, 2025
2e1f95f
Cleanup
19Vik19 Jan 21, 2025
fd3e6fa
Documentation
19Vik19 Jan 21, 2025
93e5369
Implementation of an extra check to prevent a NullPointerException fo…
19Vik19 Jan 21, 2025
a5e777c
Changed Config option “fpcf.AnalysisScenario.ScheduleStrategy” to Cla…
19Vik19 Jan 21, 2025
10ecf4f
Added config for measurement (ImmutabilityRunner - MPS - true)
19Vik19 Jan 21, 2025
517f288
Improvements
19Vik19 Jan 21, 2025
17f8770
SPS Scheduling Problem - Debugging branch
19Vik19 Feb 1, 2025
beaeaab
Formatting
Feb 3, 2025
dce5ad2
Formating
19Vik19 Feb 26, 2025
88c1e7b
Merge branch 'develop' into afterBA
19Vik19 Feb 26, 2025
5d89f47
Formatting
19Vik19 Mar 11, 2025
1767f13
Formatting
19Vik19 Mar 22, 2025
e0a44d9
Improvements, OPAL-standard config implementation
19Vik19 Mar 23, 2025
c369bbc
Minor improvements
19Vik19 Mar 23, 2025
4d5e53a
Added code documentation. Small fixes
19Vik19 Mar 23, 2025
2bb7618
reset test cases
19Vik19 Apr 18, 2025
9d1ad3b
.
19Vik19 May 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ object Immutability {

var projectTime: Seconds = Seconds.None
var analysisTime: Seconds = Seconds.None
val callGraphTime: Seconds = Seconds.None
var callGraphTime: Seconds = Seconds.None

val project = time {
Project(
Expand Down Expand Up @@ -182,7 +182,9 @@ object Immutability {
val propertyStore = project.get(PropertyStoreKey)
val analysesManager = project.get(FPCFAnalysesManagerKey)

project.get(callgraphKey)
time {
project.get(callgraphKey)
} { t => callGraphTime = t.toSeconds }

time {
analysesManager.runAll(
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion OPAL/si/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ org.opalj {
// to support the debugging of analyses developed using the property store.
// I.e., debug performs a wide range of additionaly checks to identify errors as
// early as possible.
fpcf.PropertyStore.Debug = false
fpcf.PropertyStore.Debug = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change

fpcf.PropertyStore.TraceFallbacks = false
fpcf.PropertyStore.TraceSuppressedNotifications = false
fpcf.PropertyStore.Default = "Parallel"
Expand All @@ -18,4 +18,12 @@ org.opalj {
fpcf.par.PKECPropertyStore.MaxEvaluationDepth = 32

fpcf.AnalysisScenario.AnalysisAutoConfig = false

// For scheduling the analysis with different batching strategies
// SPS - Single Phase Scheduling
// MPS - Maximum Phase Scheduling
// IPMS - Independent Phase Merge Scheduling
// SPMS - Smallest Phase Merge Scheduling
fpcf.AnalysisScenario.ScheduleStrategy = "SPS"
fpcf.AnalysisScenario.AnalysisScheduleLazyTransformerInMultipleBatches = true
}
927 changes: 875 additions & 52 deletions OPAL/si/src/main/scala/org/opalj/fpcf/AnalysisScenario.scala

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -620,13 +620,12 @@ abstract class PropertyStore {
// Save the information about the finalization order (of properties which are
// collaboratively computed).
val cleanUpSubPhase =
(propertyKindsComputedInThisPhase -- finalizationOrder.flatten.toSet) + AnalysisKey
this.subPhaseFinalizationOrder =
if (cleanUpSubPhase.isEmpty) {
finalizationOrder.toArray
(propertyKindsComputedInThisPhase -- finalizationOrder.flatten.toSet)
this.subPhaseFinalizationOrder = (if (cleanUpSubPhase.isEmpty) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not pretty. Can it be cleaned up? At least it should be documented what it's doing.

finalizationOrder.zipWithIndex.map(x => if(x._2 == 0) x._1 :+ AnalysisKey else x._1)
} else {
(finalizationOrder :+ cleanUpSubPhase.toList).toArray
}
(finalizationOrder.zipWithIndex.map(x => if(x._2 == 0) x._1 :+ AnalysisKey else x._1) :+ cleanUpSubPhase.toList)
}).toArray

subPhaseId = 0
hasSuppressedNotifications = suppressInterimUpdates.nonEmpty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import org.scalatest.BeforeAndAfterEach
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.junit.JUnitRunner

import org.opalj.fpcf.fixtures.PropertyStoreConfigurationRecorder
import org.opalj.log.GlobalLogContext

Expand All @@ -34,8 +33,8 @@ class PropertyComputationsSchedulerTest extends AnyFunSpec with Matchers with Be
override def uses(ps: PropertyStore): Set[PropertyBounds] = uses
}

val pks: Array[PropertyKind] = new Array[PropertyKind](12)
(0 to 11).foreach { i => pks(i) = PropertyKey.create[Null, Null]("p" + (i)) }
val pks: Array[PropertyKind] = new Array[PropertyKind](21)
(0 to 20).foreach { i => pks(i) = PropertyKey.create[Null, Null]("p" + (i)) }

val c1 = BasicComputationSpecification(
"c1",
Expand All @@ -44,6 +43,114 @@ class PropertyComputationsSchedulerTest extends AnyFunSpec with Matchers with Be
derivesEagerly = Set(PropertyBounds.lub(pks(1)))
)

def analysis1IsScheduledBeforeOrAtTheSameBatchAsAnalysis2(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these methods for? They don't seem to be used

schedule: Schedule[Unit],
analysis1: BasicComputationSpecification,
analysis2: BasicComputationSpecification
): Unit = {
var analysis1AlreadyComputed = false
var analysis1phase = 0
schedule.batches.foreach { batch =>
if (batch.scheduled.contains(analysis1)) {
if (!analysis1AlreadyComputed) {
analysis1AlreadyComputed = true
}
} else if (!analysis1AlreadyComputed) {
analysis1phase += 1
}
}

var analysis2AlreadyComputed = false
var analysis2phase = 0
schedule.batches.foreach { batch =>
if (batch.scheduled.contains(analysis2)) {
if (!analysis2AlreadyComputed) {
analysis2AlreadyComputed = true
}
} else if (!analysis2AlreadyComputed) {
analysis2phase += 1
}
}

if (analysis1phase <= analysis2phase) {
println("Success")
} else {
fail(s"$analysis1 is not scheduled before or in the same batch as $analysis2!")
}
}

def analysis1IsScheduledBeforeAnalysis2(
schedule: Schedule[Unit],
analysis1: BasicComputationSpecification,
analysis2: BasicComputationSpecification
): Unit = {
var analysis1AlreadyComputed = false
var analysis1phase = 0
schedule.batches.foreach { batch =>
if (batch.scheduled.contains(analysis1)) {
if (!analysis1AlreadyComputed) {
analysis1AlreadyComputed = true
}
} else if (!analysis1AlreadyComputed) {
analysis1phase += 1
}
}

var analysis2AlreadyComputed = false
var analysis2phase = 0
schedule.batches.foreach { batch =>
if (batch.scheduled.contains(analysis2)) {
if (!analysis2AlreadyComputed) {
analysis2AlreadyComputed = true
}
} else if (!analysis2AlreadyComputed) {
analysis2phase += 1
}
}

if (analysis1phase < analysis2phase) {
println("Success")
} else {
fail(s"$analysis1 is not scheduled before $analysis2!")
}
}

def analysis1IsScheduledInTheSameBatchAsAnalysis2(
schedule: Schedule[Unit],
analysis1: BasicComputationSpecification,
analysis2: BasicComputationSpecification
): Unit = {
var analysis1AlreadyComputed = false
var analysis1phase = 0
schedule.batches.foreach { batch =>
if (batch.scheduled.contains(analysis1)) {
if (!analysis1AlreadyComputed) {
analysis1AlreadyComputed = true
}
} else if (!analysis1AlreadyComputed) {
analysis1phase += 1
}
}

var analysis2AlreadyComputed = false
var analysis2phase = 0
schedule.batches.foreach { batch =>
if (batch.scheduled.contains(analysis2)) {
if (!analysis2AlreadyComputed) {
analysis2AlreadyComputed = true
}
} else if (!analysis2AlreadyComputed) {
analysis2phase += 1
}
}

if (analysis1phase == analysis2phase) {
println("Success")
} else {
fail(s"$analysis1 is not scheduled in the same batch as $analysis2!")
}
}

// **********************************************************************************************
//
// TESTS
Expand All @@ -68,6 +175,94 @@ class PropertyComputationsSchedulerTest extends AnyFunSpec with Matchers with Be
ps.phaseConfigurations.head should be((Set.empty, Set.empty, Map.empty))
}

it("an mixed analysis scenario") {
val lazy1 = BasicComputationSpecification(
"lazy1",
LazyComputation,
derivesLazily = Option(PropertyBounds.lub(pks(10)))
)
val eager1 = BasicComputationSpecification(
"eager1",
EagerComputation,
derivesEagerly = Set(PropertyBounds.lub(pks(1))),
uses = Set(PropertyBounds.lub(pks(10)))
)
val eager4 = BasicComputationSpecification(
"eager4",
EagerComputation,
derivesCollaboratively = Set(PropertyBounds.lub(pks(13))),
uses = Set(PropertyBounds.lub(pks(1)))
)
val transformer1 =
BasicComputationSpecification(
"transformer1",
Transformer,
derivesLazily = Option(PropertyBounds.lub(pks(7))),
uses = Set(PropertyBounds.finalP(pks(1)))
)
val eager5 = BasicComputationSpecification(
"eager5",
EagerComputation,
derivesCollaboratively = Set(PropertyBounds.lub(pks(13))),
uses = Set(PropertyBounds.lub(pks(7)))
)
val eager6 = BasicComputationSpecification(
"eager6",
EagerComputation,
derivesCollaboratively = Set(PropertyBounds.lub(pks(13)))
)
val lazy2 = BasicComputationSpecification(
"lazy2",
LazyComputation,
derivesLazily = Option(PropertyBounds.lub(pks(11))),
uses = Set(PropertyBounds.lub(pks(13)))
)
val lazy3 = BasicComputationSpecification(
"lazy3",
LazyComputation,
derivesLazily = Option(PropertyBounds.lub(pks(12))),
uses = Set(PropertyBounds.lub(pks(13)))
)
val eager2 = BasicComputationSpecification(
"eager2",
EagerComputation,
derivesEagerly = Set(PropertyBounds.lub(pks(2))),
uses = Set(PropertyBounds.lub(pks(12)))
)
val eager3 = BasicComputationSpecification(
"eager3",
EagerComputation,
derivesEagerly = Set(PropertyBounds.lub(pks(3))),
uses = Set(PropertyBounds.lub(pks(13)))
)
val triggered1 =
BasicComputationSpecification(
"triggered1",
TriggeredComputation,
derivesEagerly = Set(PropertyBounds.lub(pks(4))),
uses = Set(PropertyBounds.lub(pks(3)), PropertyBounds.lub(pks(6)))
)
val triggered2 =
BasicComputationSpecification(
"triggered2",
TriggeredComputation,
derivesEagerly = Set(PropertyBounds.lub(pks(5))),
uses = Set(PropertyBounds.lub(pks(4)))
)
val triggered3 =
BasicComputationSpecification(
"triggered3",
TriggeredComputation,
derivesEagerly = Set(PropertyBounds.lub(pks(6))),
uses = Set(PropertyBounds.lub(pks(5)))
)

val ps = new PropertyStoreConfigurationRecorder()
val scenario = AnalysisScenario(Set(eager1, eager3, eager4, eager5, eager6, eager2, lazy1, lazy2, lazy3, transformer1, triggered1, triggered2, triggered3), ps)
val schedule = scenario.computeSchedule(ps)

print(schedule.batches)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a test if it just prints stuff

}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class FieldImmutabilityAnalysis private[analyses] (val project: SomeProject)
* Query type iterator for concrete class types.
* Note: This only works precisely in a closed world assumption!!
*/
def queryTypeIterator(implicit state: State, typeIterator: TypeIterator): Unit = {
def queryTypeIterator()(implicit state: State, typeIterator: TypeIterator): Unit = {
val actualTypes = typeIterator.typesProperty(state.field, typeIterator)

typeIterator.foreachType(state.field, actualTypes) { actualType => determineClassImmutability(actualType) }
Expand Down Expand Up @@ -173,7 +173,7 @@ class FieldImmutabilityAnalysis private[analyses] (val project: SomeProject)
}
}

def determineTypeImmutability(implicit state: State): Unit = {
def determineTypeImmutability()(implicit state: State): Unit = {
if (state.field.fieldType == ObjectType.Object) {
// in case of a field with type object: field immutability stays NonTransitivelyImmutable
if (state.genericTypeParameters.isEmpty)
Expand Down Expand Up @@ -269,13 +269,13 @@ class FieldImmutabilityAnalysis private[analyses] (val project: SomeProject)
def c(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = {

if (state.hasDependee(eps.toEPK)) {
typeIterator.continuation(state.field, eps) {
actualType => determineClassImmutability(actualType)
}
if (eps.isFinal)
state.removeDependee(eps.toEPK)
else
state.updateDependency(eps)
typeIterator.continuation(state.field, eps) {
actualType => determineClassImmutability(actualType.asObjectType)
}
} else {

state.fieldImmutabilityDependees =
Expand Down Expand Up @@ -321,9 +321,9 @@ class FieldImmutabilityAnalysis private[analyses] (val project: SomeProject)

if (field.fieldType.isReferenceType) {
if (typeExtensibility(ObjectType.Object).isNo)
queryTypeIterator
queryTypeIterator()
else
determineTypeImmutability
determineTypeImmutability()
}

createResult()
Expand Down
Loading