Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 16 additions & 28 deletions src/lbaf/Applications/LBAF_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
import importlib
import yaml

from ..Model.lbsRank import Rank
from ..Model.lbsObject import Object

try:
import vttv
USING_VTTV = True
Expand Down Expand Up @@ -415,44 +418,29 @@
"""Print list of implemented QOI based on the '-verbosity' command line argument."""
verbosity = int(self.__args.verbose)

# Initialize file paths
current_script_path = os.path.abspath(__file__)
target_dir = os.path.join(
os.path.dirname(os.path.dirname(current_script_path)), "Model")
rank_script_name = "lbsRank.py"
object_script_name = "lbsObject.py"

# Create list of all Rank QOI (Rank.get_*)
r_qoi_list = ["work"]
with open(os.path.join(target_dir, rank_script_name), 'r', encoding="utf-8") as f:
lines = f.readlines()
for line in lines:
if line[8:12] == "get_":
r_qoi_list.append(line[12:line.find("(")])

# Create list of all Object QOI (Object.get_*)
o_qoi_list = []
with open(os.path.join(target_dir, object_script_name), 'r', encoding="utf-8") as f:
lines = f.readlines()
for line in lines:
if line[8:12] == "get_":
o_qoi_list.append(line[12:line.find("(")])
def remove_get(name : str) -> str:
return name[4:] if name.startswith("get_") else name

r = Rank(self.__logger)
rank_qois = r.get_QOIs()
o = Object(seq_id=0)
object_qois = o.get_QOIs()

# Print QOI based on verbosity level
if verbosity > 0:
self.__logger.info("List of Implemented QOI:")
if verbosity == 1:
self.__logger.info("\tRank QOI:")
for r_qoi in r_qoi_list:
self.__logger.info(f"\t\t{r_qoi}")
for name, _ in rank_qois.items():
self.__logger.info("\t" + remove_get(name))

Check warning on line 435 in src/lbaf/Applications/LBAF_app.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Use lazy % formatting in logging functions (logging-not-lazy)
elif verbosity > 1:
self.__logger.info("\tRank QOI:")
for r_qoi in r_qoi_list:
self.__logger.info(f"\t\t{r_qoi}")
for name, _ in rank_qois.items():
self.__logger.info("\t" + remove_get(name))

Check warning on line 439 in src/lbaf/Applications/LBAF_app.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Use lazy % formatting in logging functions (logging-not-lazy)
self.__logger.info("")
self.__logger.info("\tObject QOI:")
for o_qoi in o_qoi_list:
self.__logger.info(f"\t\t{o_qoi}")
for name, _ in object_qois.items():
self.__logger.info("\t\t" + remove_get(name))

Check warning on line 443 in src/lbaf/Applications/LBAF_app.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Use lazy % formatting in logging functions (logging-not-lazy)

def run(self, cfg=None, cfg_dir=None):
"""Run the LBAF application."""
Expand Down Expand Up @@ -584,7 +572,7 @@
# Execute runtime for specified phases
offline_LB_compatible = self.__parameters.json_params.get(
"offline_LB_compatible", False)
lb_iterations = self.__parameters.json_params.get(

Check warning on line 575 in src/lbaf/Applications/LBAF_app.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Unused variable 'lb_iterations' (unused-variable)
"lb_iterations", False)
rebalanced_phase = runtime.execute(
self.__parameters.algorithm.get("phase_id", 0),
Expand Down
1 change: 1 addition & 0 deletions src/lbaf/Execution/lbsAlgorithmBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@
#@HEADER
#
import abc
import os

Check warning on line 44 in src/lbaf/Execution/lbsAlgorithmBase.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Unused import os (unused-import)
from typing import Set

from ..IO.lbsStatistics import compute_function_statistics
from ..Model.lbsRank import Rank
from ..Model.lbsObject import Object

Check warning on line 49 in src/lbaf/Execution/lbsAlgorithmBase.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Unused Object imported from Model.lbsObject (unused-import)
from ..Model.lbsPhase import Phase
from ..Model.lbsWorkModelBase import WorkModelBase
from ..Utils.lbsLogging import Logger
Expand Down Expand Up @@ -91,7 +92,7 @@
self.__statistics = {
("ranks", lambda x: x.get_load()): {
"maximum load": "maximum"},
("ranks", lambda x: self._work_model.compute(x)): {

Check warning on line 95 in src/lbaf/Execution/lbsAlgorithmBase.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Lambda may not be necessary (unnecessary-lambda)
"total work": "sum"}}

def get_rebalanced_phase(self):
Expand Down
23 changes: 23 additions & 0 deletions src/lbaf/Model/lbsObject.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

from .lbsBlock import Block
from .lbsObjectCommunicator import ObjectCommunicator
from .lbsQOIDecorator import qoi

class Object:
"""A class representing an object with load and communicator
Expand Down Expand Up @@ -157,18 +158,22 @@ def __init__(
def __repr__(self):
return f"Object id: {self.get_id()}, load: {self.__load}"

@qoi
def get_id(self) -> int:
"""Return object bit-packed ID if available. Else return the object seq ID"""
return self.__packed_id if self.__packed_id is not None else self.__seq_id

@qoi
def get_packed_id(self) -> Optional[int]:
"""Return object bit-packed ID (seq_id, home_id, migratable)."""
return self.__packed_id

@qoi
def get_seq_id(self) -> Optional[int]:
"""Return object seq ID."""
return self.__seq_id

@qoi
def get_collection_id(self) -> Optional[int]:
"""Return object collection ID (required for migratable objects)."""
return self.__collection_id
Expand All @@ -177,6 +182,7 @@ def set_collection_id(self, collection_id: Optional[int]):
""" Set object collection ID (required for migratable objects)."""
self.__collection_id = collection_id

@qoi
def get_index(self) -> Optional[list]:
"""Return the object's index."""
return self.__index
Expand All @@ -189,14 +195,17 @@ def set_load(self, load: float):
""" Set object load."""
self.__load = load

@qoi
def get_load(self) -> float:
"""Return object load."""
return self.__load

@qoi
def get_size(self) -> float:
"""Return object size."""
return self.__size

@qoi
def get_overhead(self) -> float:
"""Return additional runtime memory of object."""
return self.__overhead
Expand All @@ -214,14 +223,17 @@ def get_received(self) -> dict:

return self.__communicator.get_received() if self.__communicator else {}

@qoi
def get_received_volume(self) -> float:
"""Return volume of communications received by object."""
return sum(v for v in self.__communicator.get_received().values()) if self.__communicator else 0

@qoi
def get_sent_volume(self) -> float:
"""Return volume of communications sent by object."""
return sum(v for v in self.__communicator.get_sent().values()) if self.__communicator else 0

@qoi
def get_max_volume(self) -> float:
"""Return the maximum bytes received or sent by object."""
return self.__communicator.get_max_volume() if self.__communicator else 0
Expand All @@ -230,6 +242,7 @@ def set_rank_id(self, r_id: int) -> None:
"""Assign object to rank ID"""
self.__rank_id = r_id

@qoi
def get_rank_id(self) -> int:
"""Return ID of rank to which object is currently assigned."""
return self.__rank_id
Expand All @@ -244,6 +257,7 @@ def get_shared_block(self) -> Optional[Block]:
"""Return shared memory block assigned to object."""
return self.__shared_block

@qoi
def get_shared_id(self) -> Optional[int]:
"""Return ID of shared memory block assigned to object."""
return self.__shared_block.get_id() if self.__shared_block is not None else None
Expand Down Expand Up @@ -274,3 +288,12 @@ def set_unused_params(self, unused_params: dict):
def get_unused_params(self) -> dict:
"""Return all current unused parameters."""
return self.__unused_params

def get_QOIs(self) -> list:
"""Get all methods decorated with the QOI decorator.
"""
qoi_methods : dict = {
name: getattr(self, name)
for name in dir(self)
if callable(getattr(self, name)) and not name.startswith("__") and hasattr(getattr(self, name), "is_qoi") }
return qoi_methods
50 changes: 50 additions & 0 deletions src/lbaf/Model/lbsQOIDecorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#
#@HEADER
###############################################################################
#
# lbsQOIDecorator.py
# DARMA/LB-analysis-framework => LB Analysis Framework
#
# Copyright 2019-2024 National Technology & Engineering Solutions of Sandia, LLC
# (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
# Government retains certain rights in this software.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Questions? Contact [email protected]
#
###############################################################################
#@HEADER
#

def qoi(func, *args, **kwargs):

Check warning on line 44 in src/lbaf/Model/lbsQOIDecorator.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Unused argument 'args' (unused-argument)

Check warning on line 44 in src/lbaf/Model/lbsQOIDecorator.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Unused argument 'kwargs' (unused-argument)
"""Decorator function to wrap getters that will be used as QOIs"""
def decorated_func(*args, **kwargs):
return_value = func(*args, **kwargs)
return return_value
decorated_func.is_qoi = True
return decorated_func
28 changes: 26 additions & 2 deletions src/lbaf/Model/lbsRank.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

from .lbsBlock import Block
from .lbsObject import Object

from .lbsQOIDecorator import qoi

class Rank:
"""A class representing a rank to which objects are assigned."""
Expand Down Expand Up @@ -99,12 +99,14 @@ def __lt__(self, other):
def __repr__(self):
return f"<Rank index: {self.__index}>"

@qoi
def get_id(self) -> int:
"""Return rank ID."""
return self.__index

@qoi
def get_size(self) -> float:
"""Return object size."""
"""Return rank working memory."""
return self.__size

def set_size(self, size):
Expand Down Expand Up @@ -162,26 +164,31 @@ def get_shared_block_with_id(self, b_id: int) -> Block:
return block
return None

@qoi
def get_number_of_shared_blocks(self) -> float:
"""Return number of shared memory blocks on rank."""
return len(self.__shared_blocks)

@qoi
def get_number_of_homed_blocks(self) -> float:
"""Return number of memory blocks on rank also homed there."""
return sum(
b.get_home_id() == self.get_id()
for b in self.__shared_blocks)

@qoi
def get_number_of_uprooted_blocks(self) -> float:
"""Return number of uprooted memory blocks on rank."""
return len(self.__shared_blocks) - self.get_number_of_homed_blocks()

@qoi
def get_homed_blocks_ratio(self) -> float:
"""Return fraction of memory blocks on rank also homed there."""
if len(self.__shared_blocks) > 0:
return self.get_number_of_homed_blocks() / len(self.__shared_blocks)
return math.nan

@qoi
def get_shared_memory(self):
"""Return total shared memory on rank."""
return float(sum(b.get_size() for b in self.__shared_blocks))
Expand All @@ -190,6 +197,7 @@ def get_objects(self) -> set:
"""Return all objects assigned to rank."""
return self.__migratable_objects.union(self.__sentinel_objects)

@qoi
def get_number_of_objects(self) -> int:
"""Return number of objects assigned to rank."""
return len(self.__sentinel_objects) + len(self.__migratable_objects)
Expand Down Expand Up @@ -241,18 +249,22 @@ def remove_migratable_object(self, o: Object):
"""Remove objects from migratable objects."""
self.__migratable_objects.remove(o)

@qoi
def get_load(self) -> float:
"""Return total load on rank."""
return sum(o.get_load() for o in self.__migratable_objects.union(self.__sentinel_objects))

@qoi
def get_migratable_load(self) -> float:
"""Return migratable load on rank."""
return sum(o.get_load() for o in self.__migratable_objects)

@qoi
def get_sentinel_load(self) -> float:
"""Return sentinel load on rank."""
return sum(o.get_load() for o in self.__sentinel_objects)

@qoi
def get_received_volume(self):
"""Return volume received by objects assigned to rank from other ranks."""
# Iterate over all objects assigned to rank
Expand All @@ -269,6 +281,7 @@ def get_received_volume(self):
# Return computed volume
return volume

@qoi
def get_sent_volume(self):
"""Return volume sent by objects assigned to rank to other ranks."""
# Iterate over all objects assigned to rank
Expand All @@ -287,6 +300,7 @@ def get_sent_volume(self):
# Return computed volume
return volume

@qoi
def get_max_object_level_memory(self) -> float:
"""Return maximum object-level memory on rank."""
# Iterate over all objects assigned to rank
Expand All @@ -302,6 +316,16 @@ def get_max_object_level_memory(self) -> float:
# Return maximum object-level memory for this rank
return total_size + max_overhead

@qoi
def get_max_memory_usage(self) -> float:
"""Return maximum memory usage on rank."""
return self.__size + self.get_shared_memory() + self.get_max_object_level_memory()

def get_QOIs(self) -> list:
"""Get all methods decorated with the QOI decorator.
"""
qoi_methods : dict = {
name: getattr(self, name)
for name in dir(self)
if callable(getattr(self, name)) and not name.startswith("__") and hasattr(getattr(self, name), "is_qoi") }
return qoi_methods
Loading