1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
/*
* Copyright (c) 2021 AtLarge Research
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.opendc.harness.engine
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
import org.opendc.harness.api.ExperimentDefinition
import org.opendc.harness.engine.scheduler.ExperimentScheduler
import org.opendc.harness.engine.scheduler.ThreadPoolExperimentScheduler
import org.opendc.harness.engine.strategy.CartesianExperimentStrategy
import org.opendc.harness.engine.strategy.ExperimentStrategy
import org.opendc.harness.internal.CompositeExperimentExecutionListener
/**
* A builder class for conducting experiments via the [ExperimentEngine].
*/
public class ExperimentEngineLauncher private constructor(
private val strategy: ExperimentStrategy?,
private val scheduler: ExperimentScheduler?,
private val listeners: List<ExperimentExecutionListener>,
private val repeats: Int
) {
/**
* Construct an [ExperimentEngineLauncher] instance.
*/
public constructor() : this(null, null, emptyList(), 1)
/**
* Create an [ExperimentEngineLauncher] with the specified [strategy].
*/
public fun withScheduler(strategy: ExperimentStrategy): ExperimentEngineLauncher {
return ExperimentEngineLauncher(strategy, scheduler, listeners, repeats)
}
/**
* Create an [ExperimentEngineLauncher] with the specified [scheduler].
*/
public fun withScheduler(scheduler: ExperimentScheduler): ExperimentEngineLauncher {
return ExperimentEngineLauncher(strategy, scheduler, listeners, repeats)
}
/**
* Create an [ExperimentEngineLauncher] with the specified [listener] added.
*/
public fun withListener(listener: ExperimentExecutionListener): ExperimentEngineLauncher {
return ExperimentEngineLauncher(strategy, scheduler, listeners + listener, repeats)
}
/**
* Create an [ExperimentEngineLauncher] with the specified number of repeats.
*/
public fun withRepeats(repeats: Int): ExperimentEngineLauncher {
require(repeats > 0) { "Invalid number of repeats; must be greater than zero. " }
return ExperimentEngineLauncher(strategy, scheduler, listeners, repeats)
}
/**
* Launch the specified experiments via the [ExperimentEngine] and block execution until finished.
*/
public suspend fun run(experiments: Flow<ExperimentDefinition>) {
val engine = ExperimentEngine(createStrategy(), createScheduler(), createListener(), repeats)
experiments.collect { experiment -> engine.execute(experiment) }
}
/**
* Launch the specified experiments via the [ExperimentEngine] and block the current thread until finished.
*/
public fun runBlocking(experiments: Flow<ExperimentDefinition>) {
runBlocking {
run(experiments)
}
}
/**
* Return a string representation of this instance.
*/
public override fun toString(): String = "ExperimentEngineLauncher"
/**
* Create the [ExperimentStrategy] that explores the experiment design space.
*/
private fun createStrategy(): ExperimentStrategy {
return strategy ?: CartesianExperimentStrategy
}
/**
* Create the [ExperimentScheduler] that schedules the trials over the compute resources.
*/
private fun createScheduler(): ExperimentScheduler {
return scheduler ?: ThreadPoolExperimentScheduler(Runtime.getRuntime().availableProcessors())
}
/**
* Create the [ExperimentExecutionListener] that listens the to the execution of the experiments.
*/
private fun createListener(): ExperimentExecutionListener {
require(listeners.isNotEmpty()) { "No listeners registered." }
return CompositeExperimentExecutionListener(listeners)
}
}
|