Skip to content

Commit da39814

Browse files
committed
renamed MIPS to CU (compute unit)
1 parent ef64fb3 commit da39814

File tree

8 files changed

+91
-88
lines changed

8 files changed

+91
-88
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def place_task_after_2_seconds(env, node, task):
4444
yield env.timeout(2)
4545
task.allocate(node)
4646

47-
node = Node("node1", mips=100, power_model=PowerModelNode(max_power=30, static_power=10))
48-
task = Task(mips=100)
47+
node = Node("node1", cu=100, power_model=PowerModelNode(max_power=30, static_power=10))
48+
task = Task(cu=100)
4949

5050
env = simpy.Environment()
5151
# register our task placement process

examples/1_single_node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ def main():
2828
INFO Total power usage: 200.0 Ws
2929
"""
3030
# Initializing infrastructure and workload
31-
node = Node("node1", mips=100, power_model=PowerModelNode(max_power=30, static_power=10))
32-
task = Task(mips=100)
31+
node = Node("node1", cu=100, power_model=PowerModelNode(max_power=30, static_power=10))
32+
task = Task(cu=100)
3333

3434
measurements = []
3535

examples/2_application_placement.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ def main():
2626
2727
Log Output:
2828
INFO Placing Application(tasks=3):
29-
INFO - SourceTask(id=0, mips=100) on Node('sensor', mips=0/1000).
30-
INFO - ProcessingTask(id=1, mips=5000) on Node('fog', mips=0/400000).
31-
INFO - SinkTask(id=2, mips=100) on Node('cloud', mips=0/inf).
29+
INFO - SourceTask(id=0, cu=100) on Node('sensor', cu=0/1000).
30+
INFO - ProcessingTask(id=1, cu=5000) on Node('fog', cu=0/400000).
31+
INFO - SinkTask(id=2, cu=100) on Node('cloud', cu=0/inf).
3232
INFO - DataFlow(bit_rate=1000) on [Link('sensor' -> 'fog', bandwidth=0/30000000.0, latency=10)].
3333
INFO - DataFlow(bit_rate=200) on [Link('fog' -> 'cloud', bandwidth=0/1000000000.0, latency=5)].
3434
DEBUG 0: cloud_and_fog_meter: PowerMeasurement(dynamic=70002.125W, static=30W)
@@ -68,19 +68,19 @@ def create_infrastructure():
6868
"""Create the scenario's infrastructure graph.
6969
7070
It consists of three nodes:
71-
- A sensor that can compute up to 1000 million instructions per second (MIPS).
71+
- A sensor with a computational capacity of one compute unit (CU).
7272
It has a maximum power usage of 1.8 Watt and a power usage of 0.2 Watt when being idle.
73-
- A fog node which can compute up to 400000 MIPS; 200 Watt max and 30 Watt static power usage
74-
- A node representing a cloud data center with unlimited processing power that consumes 700 W/MIPS
73+
- A fog node which can compute up to 400 CU; 200 Watt max and 30 Watt static power usage
74+
- A node representing a cloud data center with unlimited processing power that consumes 0.5 W/CU
7575
7676
And two network links that connect the nodes:
7777
- A WiFi connection between the sensor and fog node that consumes 300 J/bit
7878
- A wide are network (WAN) connection between the fog node and cloud that consumes 6000 J/bit
7979
"""
8080
infrastructure = Infrastructure()
81-
sensor = Node("sensor", mips=1000, power_model=PowerModelNode(max_power=1.8, static_power=0.2))
82-
fog_node = Node("fog", mips=400000, power_model=PowerModelNode(max_power=200, static_power=30))
83-
cloud = Node("cloud", power_model=PowerModelNode(power_per_mips=700))
81+
sensor = Node("sensor", cu=1, power_model=PowerModelNode(max_power=1.8, static_power=0.2))
82+
fog_node = Node("fog", cu=400, power_model=PowerModelNode(max_power=200, static_power=30))
83+
cloud = Node("cloud", power_model=PowerModelNode(power_per_cu=0.5))
8484
wifi_link_up = Link(sensor, fog_node, latency=10, bandwidth=30e6, power_model=PowerModelLink(300))
8585
wan_link_up = Link(fog_node, cloud, latency=5, bandwidth=1e9, power_model=PowerModelLink(6000))
8686

@@ -93,16 +93,16 @@ def create_application(source_node: Node, sink_node: Node):
9393
"""Create the application running in the scenario.
9494
9595
It consists of three tasks and two data flows between these tasks:
96-
- A source task that is bound to the sensor node and requires 100 MIPS (for measuring data)
97-
- A processing task that receives 1000 bit/s from the source task, requires 5000 MIPS (for aggregating the data)
96+
- A source task that is bound to the sensor node and requires 0.1 CU (for measuring data)
97+
- A processing task that receives 1000 bit/s from the source task, requires 5 CU (for aggregating the data)
9898
and returns 200 bit/s to the sink task
99-
- A sink task that is bound to the cloud node and requires 500 MIPS (for storing the data)
99+
- A sink task that is bound to the cloud node and requires 0.5 CU (for storing the data)
100100
"""
101101
application = Application()
102102

103-
source_task = SourceTask(mips=100, bound_node=source_node)
104-
processing_task = ProcessingTask(mips=5000)
105-
sink_task = SinkTask(mips=100, bound_node=sink_node)
103+
source_task = SourceTask(cu=0.1, bound_node=source_node)
104+
processing_task = ProcessingTask(cu=5)
105+
sink_task = SinkTask(cu=0.5, bound_node=sink_node)
106106

107107
application.add_task(source_task)
108108
application.add_task(processing_task, incoming_data_flows=[(source_task, 1000)])

examples/smart_city_traffic/infrastructure.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515

1616
class Cloud(Node):
1717
def __init__(self):
18-
super().__init__("cloud", mips=CLOUD_MIPS, power_model=PowerModelNode(power_per_mips=CLOUD_WATT_PER_MIPS))
18+
super().__init__("cloud", cu=CLOUD_CU, power_model=PowerModelNode(power_per_cu=CLOUD_WATT_PER_CU))
1919

2020

2121
class FogNode(Node):
2222
def __init__(self, location: "Location"):
2323
# TODO Shutdown!
2424
global _fog_nodes_created
25-
super().__init__(f"fog_{_fog_nodes_created}", mips=FOG_MIPS,
25+
super().__init__(f"fog_{_fog_nodes_created}", cu=FOG_CU,
2626
power_model=PowerModelNode(max_power=FOG_MAX_POWER, static_power=FOG_STATIC_POWER))
2727
_fog_nodes_created += 1
2828
self.location = location
@@ -41,36 +41,33 @@ def add_task(self, task: "Task"):
4141

4242
def remove_task(self, task: "Task"):
4343
super().remove_task(task)
44-
if FOG_IDLE_SHUTDOWN and self.used_mips == 0:
44+
if FOG_IDLE_SHUTDOWN and self.used_cu == 0:
4545
self.shutdown = True
4646

4747

48-
49-
50-
5148
class TrafficLight(Node):
5249
def __init__(self, location: "Location", application_sink: Node):
5350
global _traffic_lights_created
54-
super().__init__(f"traffic_light_{_traffic_lights_created}", mips=0, power_model=PowerModelNode(0, 0))
51+
super().__init__(f"traffic_light_{_traffic_lights_created}", cu=0, power_model=PowerModelNode(0, 0))
5552
_traffic_lights_created += 1
5653
self.location = location
5754
self.application = self._create_cctv_application(application_sink)
5855

5956
def _create_cctv_application(self, application_sink: Node):
6057
application = Application()
61-
source_task = SourceTask(mips=0, bound_node=self)
58+
source_task = SourceTask(cu=0, bound_node=self)
6259
application.add_task(source_task)
63-
processing_task = ProcessingTask(mips=CCTV_PROCESSOR_MIPS)
60+
processing_task = ProcessingTask(cu=CCTV_PROCESSOR_CU)
6461
application.add_task(processing_task, incoming_data_flows=[(source_task, CCTV_SOURCE_TO_PROCESSOR_BIT_RATE)])
65-
sink_task = SinkTask(mips=0, bound_node=application_sink)
62+
sink_task = SinkTask(cu=0, bound_node=application_sink)
6663
application.add_task(sink_task, incoming_data_flows=[(processing_task, CCTV_PROCESSOR_TO_SINK_BIT_RATE)])
6764
return application
6865

6966

7067
class Taxi(Node):
7168
def __init__(self, env: simpy.Environment, mobility_model: "TaxiMobilityModel", application_sinks: List[Node]):
7269
global _taxis_created
73-
super().__init__(f"taxi_{_taxis_created}", mips=0, power_model=PowerModelNode(0, 0))
70+
super().__init__(f"taxi_{_taxis_created}", cu=0, power_model=PowerModelNode(0, 0))
7471
_taxis_created += 1
7572
self.env = env
7673
self.application = self._create_v2i_application(application_sinks)
@@ -82,12 +79,12 @@ def location(self) -> "Location":
8279

8380
def _create_v2i_application(self, application_sinks: List[Node]) -> Application:
8481
application = Application()
85-
source_task = SourceTask(mips=0, bound_node=self)
82+
source_task = SourceTask(cu=0, bound_node=self)
8683
application.add_task(source_task)
87-
processing_task = ProcessingTask(mips=V2I_PROCESSOR_MIPS)
84+
processing_task = ProcessingTask(cu=V2I_PROCESSOR_CU)
8885
application.add_task(processing_task, incoming_data_flows=[(source_task, V2I_SOURCE_TO_PROCESSOR_BIT_RATE)])
8986
for application_sink in application_sinks:
90-
sink_task = SinkTask(mips=0, bound_node=application_sink)
87+
sink_task = SinkTask(cu=0, bound_node=application_sink)
9188
application.add_task(sink_task, incoming_data_flows=[(processing_task, V2I_PROCESSOR_TO_SINK_BIT_RATE)])
9289
return application
9390

examples/smart_city_traffic/settings.py

Lines changed: 5 additions & 5 deletions
Large diffs are not rendered by default.

leaf/application.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,23 @@
88

99

1010
class Task(PowerAware):
11-
def __init__(self, mips: float):
11+
def __init__(self, cu: float):
1212
"""Task that can be placed on a :class:`Node`.
1313
1414
Tasks _can_ be connected via :class:`Link`s to build an :class:`Application`.
1515
1616
Args:
17-
mips: Million instructions per second required to execute the task.
17+
cu: Amount of compute units (CU) required to execute the task. CUs a imaginary unit for computational
18+
power to express differences between hardware platforms.
19+
20+
Million instructions per second required to execute the task.
1821
"""
1922
self.id: Optional[int] = None
20-
self.mips = mips
23+
self.cu = cu
2124
self.node: Optional[Node] = None
2225

2326
def __repr__(self):
24-
return f"{self.__class__.__name__}(id={self.id}, mips={self.mips})"
27+
return f"{self.__class__.__name__}(id={self.id}, cu={self.cu})"
2528

2629
def allocate(self, node: Node):
2730
"""Place the task on a certain node and allocate resources."""
@@ -39,48 +42,48 @@ def deallocate(self):
3942

4043
def measure_power(self) -> PowerMeasurement:
4144
try:
42-
return self.node.measure_power().multiply(self.mips / self.node.used_mips)
45+
return self.node.measure_power().multiply(self.cu / self.node.used_cu)
4346
except ZeroDivisionError:
4447
return PowerMeasurement(0, 0)
4548

4649

4750
class SourceTask(Task):
48-
def __init__(self, mips: float = 0, bound_node: Node = None):
51+
def __init__(self, cu: float = 0, bound_node: Node = None):
4952
"""Source task of an application that is bound to a certain node, e.g. a sensor generating data.
5053
5154
Source tasks never have incoming and always have outgoing data flows.
5255
5356
Args:
54-
mips: Million instructions per second required to execute the task.
57+
cu: Million instructions per second required to execute the task.
5558
bound_node: The node which the task is bound to. Cannot be None.
5659
"""
57-
super().__init__(mips)
60+
super().__init__(cu)
5861
if bound_node is None:
5962
raise ValueError("bound_node for SourceTask cannot be None")
6063
self.bound_node = bound_node
6164

6265

6366
class ProcessingTask(Task):
64-
def __init__(self, mips: float = 0):
67+
def __init__(self, cu: float = 0):
6568
"""Processing task of an application that can be freely placed on the infrastructure.
6669
6770
Processing tasks always have incoming and outgoing data flows.
6871
6972
Args:
70-
mips: Million instructions per second required to execute the task.
73+
cu: Million instructions per second required to execute the task.
7174
"""
72-
super().__init__(mips)
75+
super().__init__(cu)
7376

7477

7578
class SinkTask(Task):
76-
def __init__(self, mips: float = 0, bound_node: Node = None):
79+
def __init__(self, cu: float = 0, bound_node: Node = None):
7780
"""Sink task of an application that is bound to a certain node, e.g. a cloud server for storage.
7881
7982
Args:
80-
mips: Million instructions per second required to execute the task.
83+
cu: Million instructions per second required to execute the task.
8184
bound_node: The node which the task is bound to. Cannot be None.
8285
"""
83-
super().__init__(mips)
86+
super().__init__(cu)
8487
if bound_node is None:
8588
raise ValueError("bound_node for SourceTask cannot be None")
8689
self.bound_node = bound_node

leaf/infrastructure.py

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
class Node(PowerAware):
1010
def __init__(self, name: str,
11-
mips: Optional[float] = None,
12-
power_model: Optional["PowerModel"] = None):
11+
cu: Optional[float] = None,
12+
power_model: Optional["PowerModelNode"] = None):
1313
"""A compute node in the infrastructure graph.
1414
1515
This can represent any kind of node, e.g.
@@ -20,48 +20,51 @@ def __init__(self, name: str,
2020
2121
Args:
2222
name: Name of the node. This is used to refer to nodes when defining links.
23-
mips: Maximum processing power the node provides in "million instructions per second".
24-
If None, the node has unlimited processing power.
23+
cu: Maximum processing power the node provides in "compute units", a imaginary unit for computational power
24+
to express differences between hardware platforms. If None, the node has unlimited processing power.
2525
power_model: Power model which determines the power usage of the node.
2626
"""
2727
self.name = name
28-
if mips is None:
29-
self.mips = math.inf
28+
if cu is None:
29+
self.cu = math.inf
3030
else:
31-
self.mips = mips
32-
self.used_mips = 0
31+
self.cu = cu
32+
self.used_cu = 0
3333
self.tasks: List["Task"] = []
3434

3535
if power_model:
36+
if cu is None and power_model.max_power is not None:
37+
raise ValueError("Cannot use PowerModelNode with `max_power` on a compute node with unlimited "
38+
"processing power")
3639
self.power_model = power_model
3740
self.power_model.set_parent(self)
3841

3942
def __repr__(self):
40-
mips_repr = self.mips if self.mips is not None else "∞"
41-
return f"{self.__class__.__name__}('{self.name}', mips={self.used_mips}/{mips_repr})"
43+
cu_repr = self.cu if self.cu is not None else "∞"
44+
return f"{self.__class__.__name__}('{self.name}', cu={self.used_cu}/{cu_repr})"
4245

4346
def utilization(self) -> float:
4447
"""Return the current utilization of the resource in the range [0, 1]."""
4548
try:
46-
return self.used_mips / self.mips
49+
return self.used_cu / self.cu
4750
except ZeroDivisionError:
48-
assert self.used_mips == 0
51+
assert self.used_cu == 0
4952
return 0
5053

5154
def _add_task(self, task: "Task"):
5255
"""Add a task to the node.
5356
5457
Private as this is only called by leaf.application.Task and not part of the public interface.
5558
"""
56-
self._reserve_mips(task.mips)
59+
self._reserve_cu(task.cu)
5760
self.tasks.append(task)
5861

5962
def _remove_task(self, task: "Task"):
6063
"""Remove a task from the node.
6164
6265
Private as this is only called by leaf.application.Task and not part of the public interface.
6366
"""
64-
self._release_mips(task.mips)
67+
self._release_cu(task.cu)
6568
self.tasks.remove(task)
6669

6770
def measure_power(self) -> PowerMeasurement:
@@ -70,17 +73,17 @@ def measure_power(self) -> PowerMeasurement:
7073
except TypeError:
7174
raise RuntimeError(f"{self} has no power model.")
7275

73-
def _reserve_mips(self, mips: float):
74-
new_used_mips = self.used_mips + mips
75-
if new_used_mips > self.mips:
76-
raise ValueError(f"Cannot reserve {mips} mips on compute node {self}.")
77-
self.used_mips = new_used_mips
78-
79-
def _release_mips(self, mips: float):
80-
new_used_mips = self.used_mips - mips
81-
if new_used_mips < 0:
82-
raise ValueError(f"Cannot release {mips} mips on compute node {self}.")
83-
self.used_mips = new_used_mips
76+
def _reserve_cu(self, cu: float):
77+
new_used_cu = self.used_cu + cu
78+
if new_used_cu > self.cu:
79+
raise ValueError(f"Cannot reserve {cu} CU on compute node {self}.")
80+
self.used_cu = new_used_cu
81+
82+
def _release_cu(self, cu: float):
83+
new_used_cu = self.used_cu - cu
84+
if new_used_cu < 0:
85+
raise ValueError(f"Cannot release {cu} CU on compute node {self}.")
86+
self.used_cu = new_used_cu
8487

8588

8689
class Link(PowerAware):

leaf/power.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def set_parent(self, parent):
7272

7373

7474
class PowerModelNode(PowerModel):
75-
def __init__(self, max_power: float = None, power_per_mips: float = None, static_power: float = 0):
75+
def __init__(self, max_power: float = None, power_per_cu: float = None, static_power: float = 0):
7676
"""Power model for compute nodes with static and dynamic power usage.
7777
7878
Power usage is scaled linearly with resource usage.
@@ -82,26 +82,26 @@ def __init__(self, max_power: float = None, power_per_mips: float = None, static
8282
up to 150 Watt when under full load (`max_power=150`).
8383
8484
Args:
85-
max_power: Maximum power usage of the node under full load. Cannot be combined with `power_per_mips`.
86-
power_per_mips: Incremental power usage for each mips. Cannot be combined with `max_power`.
85+
max_power: Maximum power usage of the node under full load. Cannot be combined with `power_per_cu`.
86+
power_per_cu: Incremental power usage for each used compute unit. Cannot be combined with `max_power`.
8787
static_power: Idle power usage of the node without any load.
8888
"""
89-
if max_power is None and power_per_mips is None:
90-
raise ValueError("Either `max_power` or `power_per_mips` have to be stated.")
91-
if max_power is not None and power_per_mips is not None:
92-
raise ValueError("The parameters `max_power` or `power_per_mips` cannot be combined.")
89+
if max_power is None and power_per_cu is None:
90+
raise ValueError("Either `max_power` or `power_per_cu` have to be stated.")
91+
if max_power is not None and power_per_cu is not None:
92+
raise ValueError("The parameters `max_power` or `power_per_cu` cannot be combined.")
9393
self.max_power = max_power
94-
self.power_per_mips = power_per_mips
94+
self.power_per_cu = power_per_cu
9595
self.static_power = static_power
9696
self.node = None
9797

9898
def measure(self) -> PowerMeasurement:
9999
if self.max_power is not None:
100100
dynamic_power = (self.max_power - self.static_power) * self.node.utilization()
101-
elif self.power_per_mips is not None:
102-
dynamic_power = self.power_per_mips * self.node.used_mips
101+
elif self.power_per_cu is not None:
102+
dynamic_power = self.power_per_cu * self.node.used_cu
103103
else:
104-
raise RuntimeError("Invalid state of PowerModelNode: `max_power` and `power_per_mips` are undefined.")
104+
raise RuntimeError("Invalid state of PowerModelNode: `max_power` and `power_per_cu` are undefined.")
105105
return PowerMeasurement(dynamic=dynamic_power, static=self.static_power)
106106

107107
def set_parent(self, parent):

0 commit comments

Comments
 (0)