Skip to content

Commit

Permalink
Case Optimization Refresh (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
henryleberre authored Dec 12, 2023
1 parent 620c471 commit 49d7826
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 192 deletions.
19 changes: 12 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ macro(HANDLE_SOURCES target useCommon)
set(${target}_DIR "${CMAKE_SOURCE_DIR}/src/${target}")
set(common_DIR "${CMAKE_SOURCE_DIR}/src/common")

string(TOUPPER ${target} ${target}_UPPER)

# Gather src/[<target>,common]/*.f90
file(GLOB ${target}_F90s CONFIGURE_DEPENDS "${${target}_DIR}/*.f90")
set(${target}_SRCs ${${target}_F90s})
Expand All @@ -256,9 +258,9 @@ macro(HANDLE_SOURCES target useCommon)
endif()

# Locate src/[<target>,common]/include/*.fpp
if (EXISTS "${${target}_DIR}/include")
file(GLOB ${target}_incs CONFIGURE_DEPENDS "${${target}_DIR}/include/*.fpp")
endif()
file(GLOB ${target}_incs CONFIGURE_DEPENDS "${${target}_DIR}/include/*.fpp"
"${CMAKE_CURRENT_BINARY_DIR}/include/*.fpp")

if (${useCommon})
file(GLOB common_incs CONFIGURE_DEPENDS "${common_DIR}/include/*.fpp")
list(APPEND ${target}_incs ${common_incs})
Expand All @@ -272,11 +274,14 @@ macro(HANDLE_SOURCES target useCommon)
add_custom_command(
OUTPUT ${f90}
COMMAND ${FYPP_EXE} -m re
-I "${common_DIR}"
-I "${common_DIR}/include"
-I "${CMAKE_CURRENT_BINARY_DIR}/include"
-I "${${target}_DIR}/include"
-I "${common_DIR}/include"
-I "${common_DIR}"
-D MFC_${CMAKE_Fortran_COMPILER_ID}
-D MFC_${${target}_UPPER}
-D MFC_COMPILER="${CMAKE_Fortran_COMPILER_ID}"
-D MFC_CASE_OPTIMIZATION=False
--line-numbering
--no-folding
"${fpp}" "${f90}"
Expand Down Expand Up @@ -328,10 +333,10 @@ function(MFC_SETUP_TARGET)
"${CMAKE_SOURCE_DIR}/src/${ARGS_TARGET}/include")
endif()

string(TOUPPER "${ARGS_TARGET}" ARGS_TARGET_UPPER)
string(TOUPPER "${ARGS_TARGET}" ${ARGS_TARGET}_UPPER)
target_compile_definitions(
${ARGS_TARGET} PRIVATE MFC_${CMAKE_Fortran_COMPILER_ID}
MFC_${ARGS_TARGET_UPPER}
MFC_${${ARGS_TARGET}_UPPER}
)

if (MFC_MPI AND ARGS_MPI)
Expand Down
5 changes: 1 addition & 4 deletions src/pre_process/include/case.fpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
! This file was generated by MFC. It is only used when analytical patches are
! present in the input file. It is used to define the analytical patches with
! expressions that are evaluated at runtime from the input file.
! By default, no analytical patches are defined.

#:def analytical()

#:enddef
7 changes: 2 additions & 5 deletions src/simulation/include/case.fpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
! This file was generated by MFC. It is only used if the --case-optimization
! option is passed to ./mfc.sh run or test, enabling a GPU-oriented optimization
! that hard-codes certain case parameters from the input file.

#:set MFC_CASE_OPTIMIZATION = False
! This file is purposefully empty. It is only important for builds that make use
! of --case-optimization.
35 changes: 22 additions & 13 deletions toolchain/mfc/args.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re, os.path, argparse, dataclasses

from .build import TARGETS, DEFAULT_TARGETS, DEPENDENCY_TARGETS
from .common import format_list_to_string
from .common import MFCException, format_list_to_string
from .test.test import CASES as TEST_CASES
from .packer import packer

Expand Down Expand Up @@ -45,7 +45,7 @@ def add_common_arguments(p, mask = None):

if "t" not in mask:
p.add_argument("-t", "--targets", metavar="TARGET", nargs="+", type=str.lower, choices=[ _.name for _ in TARGETS ],
default=[ _.name for _ in DEFAULT_TARGETS ],
default=[ _.name for _ in sorted(DEFAULT_TARGETS, key=lambda t: t.runOrder) ],
help=f"Space separated list of targets to act upon. Allowed values are: {format_list_to_string([ _.name for _ in TARGETS ])}.")

if "m" not in mask:
Expand All @@ -70,6 +70,8 @@ def add_common_arguments(p, mask = None):

# === BUILD ===
add_common_arguments(build, "g")
build.add_argument("-i", "--input", type=str, default=None, help="(GPU Optimization) Build a version of MFC optimized for a case.")
build.add_argument("--case-optimization", action="store_true", default=False, help="(GPU Optimization) Compile MFC targets with some case parameters hard-coded (requires --input).")

# === CLEAN ===
add_common_arguments(clean, "jg")
Expand Down Expand Up @@ -128,16 +130,17 @@ def add_common_arguments(p, mask = None):
args: dict = vars(parser.parse_args())

# Add default arguments of other subparsers
def append_defaults_to_data(name: str, parser):
if args["command"] != name:
vals, errs = parser.parse_known_args(["-i None"])
for key,val in vars(vals).items():
if not key in args:
args[key] = val
for name, parser in [("run", run), ("test", test), ("build", build),
("clean", clean), ("bench", bench), ("count", count)]:
if args["command"] == name:
continue

for a, b in [("run", run ), ("test", test ), ("build", build),
("clean", clean), ("bench", bench), ("count", count)]:
append_defaults_to_data(a, b)
vals, errs = parser.parse_known_args(["-i", "None"])
for key, val in vars(vals).items():
if key == "input":
args[key] = args.get(key)
elif not key in args:
args[key] = args.get(key, val)

if args["command"] is None:
parser.print_help()
Expand All @@ -146,11 +149,17 @@ def append_defaults_to_data(name: str, parser):
# "Slugify" the name of the job
args["name"] = re.sub(r'[\W_]+', '-', args["name"])

# build's --case-optimization and --input depend on each other
if args["command"] == "build":
if (args["input"] is not None) ^ args["case_optimization"] :
raise MFCException(f"./mfc.sh build's --case-optimization requires --input")

# Input files to absolute paths
for e in ["input", "input1", "input2"]:
if e not in args:
continue
if args[e] is not None:

if args.get(e) is not None:
args[e] = os.path.abspath(args[e])

return args
151 changes: 86 additions & 65 deletions toolchain/mfc/build.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import re, os, typing, dataclasses
import re, os, typing, hashlib, dataclasses

from .common import MFCException, system, delete_directory, create_directory
from .state import ARG, CFG
from .printer import cons
from .run.input import MFCInputFile

from .run import input

@dataclasses.dataclass
class MFCTarget:
Expand All @@ -26,19 +25,27 @@ def compute(self) -> typing.Set:
isDefault: bool # Should it be built by default? (unspecified -t | --targets)
isRequired: bool # Should it always be built? (no matter what -t | --targets is)
requires: Dependencies # Build dependencies of the target
runOrder: int # For MFC Targets: Order in which targets should logically run

def __hash__(self) -> int:
return hash(self.name)

# Get path to directory that will store the build files
def get_build_dirpath(self) -> str:
subdir = 'dependencies' if self.isDependency else CFG().make_slug()

if not self.isDependency and ARG("case_optimization"):
m = hashlib.sha256()
m.update(input.load().get_fpp(self).encode())
subdir = f"{subdir}-{m.hexdigest()[:6]}"

return os.sep.join([
os.getcwd(),
"build",
[CFG().make_slug(), 'dependencies'][int(self.isDependency)],
subdir,
self.name
])

# Get the directory that contains the target's CMakeLists.txt
def get_cmake_dirpath(self) -> str:
# The CMakeLists.txt file is located:
Expand Down Expand Up @@ -84,31 +91,16 @@ def get_configuration_txt(self) -> typing.Optional[dict]:

return None


def build(self, history: typing.Set[str] = None):
if history is None:
history = set()

if self.name in history:
return

history.add(self.name)

build_targets(REQUIRED_TARGETS, history)

cons.print(f"[bold]Building [magenta]{self.name}[/magenta]:[/bold]")
cons.indent()

def is_buildable(self) -> bool:
if ARG("no_build"):
cons.print("--no-build specified, skipping...")
cons.unindent()
return
return False

if self.isDependency and ARG(f"no_{self.name}"):
cons.print(f"--no-{self.name} given, skipping...")
cons.unindent()
return
return False

return True

def configure(self):
build_dirpath = self.get_build_dirpath()
cmake_dirpath = self.get_cmake_dirpath()
install_dirpath = self.get_install_dirpath()
Expand Down Expand Up @@ -144,42 +136,35 @@ def build(self, history: typing.Set[str] = None):
flags.append(f"-DMFC_OpenACC={'ON' if ARG('gpu') else 'OFF'}")

configure = ["cmake"] + flags + ["-S", cmake_dirpath, "-B", build_dirpath]
build = ["cmake", "--build", build_dirpath,
"--target", self.name,
"-j", ARG("jobs"),
"--config", 'Debug' if ARG('debug') else 'Release']
if ARG('verbose'):
build.append("--verbose")

install = ["cmake", "--install", build_dirpath]
delete_directory(build_dirpath)
create_directory(build_dirpath)

if not self.is_configured():
build_targets(self.requires.compute(), history)
if system(configure, no_exception=True) != 0:
raise MFCException(f"Failed to configure the [bold magenta]{self.name}[/bold magenta] target.")


def build(self):
input.load({}).generate_fpp(self)

delete_directory(build_dirpath)
create_directory(build_dirpath)
build = ["cmake", "--build", self.get_build_dirpath(),
"--target", self.name,
"-j", ARG("jobs"),
"--config", 'Debug' if ARG('debug') else 'Release']
if ARG('verbose'):
build.append("--verbose")

if system(configure, no_exception=True) != 0:
raise MFCException(f"Failed to configure the [bold magenta]{self.name}[/bold magenta] target.")
system(build, exception_text=f"Failed to build the [bold magenta]{self.name}[/bold magenta] target.")

if not self.isDependency and ARG("command") == "build":
MFCInputFile("", "", {}).generate(self, bOnlyFPPs = True)
def install(self):
install = ["cmake", "--install", self.get_build_dirpath()]

system(build, exception_text=f"Failed to build the [bold magenta]{self.name}[/bold magenta] target.")
system(install, exception_text=f"Failed to install the [bold magenta]{self.name}[/bold magenta] target.")

cons.print(no_indent=True)
cons.unindent()

def clean(self):
cons.print(f"[bold]Cleaning [magenta]{self.name}[/magenta]:[/bold]")
cons.indent()

build_dirpath = self.get_build_dirpath()

if not os.path.isdir(build_dirpath):
cons.print("Target not configured. Nothing to clean.")
cons.unindent()
return

clean = ["cmake", "--build", build_dirpath, "--target", "clean",
Expand All @@ -190,17 +175,15 @@ def clean(self):

system(clean, exception_text=f"Failed to clean the [bold magenta]{self.name}[/bold magenta] target.")

cons.unindent()


FFTW = MFCTarget('fftw', ['-DMFC_FFTW=ON'], True, False, False, MFCTarget.Dependencies([], [], []))
HDF5 = MFCTarget('hdf5', ['-DMFC_HDF5=ON'], True, False, False, MFCTarget.Dependencies([], [], []))
SILO = MFCTarget('silo', ['-DMFC_SILO=ON'], True, False, False, MFCTarget.Dependencies([HDF5], [], []))
PRE_PROCESS = MFCTarget('pre_process', ['-DMFC_PRE_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([], [], []))
SIMULATION = MFCTarget('simulation', ['-DMFC_SIMULATION=ON'], False, True, False, MFCTarget.Dependencies([], [FFTW], []))
POST_PROCESS = MFCTarget('post_process', ['-DMFC_POST_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([FFTW, SILO], [], []))
SYSCHECK = MFCTarget('syscheck', ['-DMFC_SYSCHECK=ON'], False, False, True, MFCTarget.Dependencies([], [], []))
DOCUMENTATION = MFCTarget('documentation', ['-DMFC_DOCUMENTATION=ON'], False, False, False, MFCTarget.Dependencies([], [], []))
FFTW = MFCTarget('fftw', ['-DMFC_FFTW=ON'], True, False, False, MFCTarget.Dependencies([], [], []), -1)
HDF5 = MFCTarget('hdf5', ['-DMFC_HDF5=ON'], True, False, False, MFCTarget.Dependencies([], [], []), -1)
SILO = MFCTarget('silo', ['-DMFC_SILO=ON'], True, False, False, MFCTarget.Dependencies([HDF5], [], []), -1)
PRE_PROCESS = MFCTarget('pre_process', ['-DMFC_PRE_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([], [], []), 0)
SIMULATION = MFCTarget('simulation', ['-DMFC_SIMULATION=ON'], False, True, False, MFCTarget.Dependencies([], [FFTW], []), 1)
POST_PROCESS = MFCTarget('post_process', ['-DMFC_POST_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([FFTW, SILO], [], []), 2)
SYSCHECK = MFCTarget('syscheck', ['-DMFC_SYSCHECK=ON'], False, False, True, MFCTarget.Dependencies([], [], []), -1)
DOCUMENTATION = MFCTarget('documentation', ['-DMFC_DOCUMENTATION=ON'], False, False, False, MFCTarget.Dependencies([], [], []), -1)

TARGETS = { FFTW, HDF5, SILO, PRE_PROCESS, SIMULATION, POST_PROCESS, SYSCHECK, DOCUMENTATION }

Expand Down Expand Up @@ -230,17 +213,55 @@ def get_dependency_install_dirpath() -> str:
raise MFCException("No dependency target found.")


def build_target(target: typing.Union[MFCTarget, str], history: typing.Set[str] = None):
if history is None:
history = set()

t = get_target(target)

if t.name in history or not t.is_buildable():
return

history.add(t.name)

build_targets(t.requires.compute(), history)

if not t.is_configured():
t.configure()

t.build()
t.install()

def build_targets(targets: typing.Iterable[typing.Union[MFCTarget, str]], history: typing.Set[str] = None):
if history is None:
history = set()

for target in list(REQUIRED_TARGETS) + targets:
build_target(target, history)


def clean_target(target: typing.Union[MFCTarget, str], history: typing.Set[str] = None):
if history is None:
history = set()

t = get_target(target)

for target in targets:
get_target(target).build(history)
if t.name in history or not t.is_buildable():
return

history.add(t.name)

t.clean()


def clean_targets(targets: typing.Iterable[typing.Union[MFCTarget, str]], history: typing.Set[str] = None):
if history is None:
history = set()

def clean_targets(targets: typing.Iterable[typing.Union[MFCTarget, str]]):
for target in targets:
get_target(target).clean()
for target in list(REQUIRED_TARGETS) + targets:
t = get_target(target)
if t.is_configured():
t.clean()


def get_configured_targets() -> typing.List[MFCTarget]:
Expand Down
1 change: 1 addition & 0 deletions toolchain/mfc/run/engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def run(self, targets: typing.List[MFCTarget]) -> None:

cons.print(f"[bold green]Done[/bold green] (in {datetime.timedelta(seconds=end_time - start_time)})")

cons.print()
cons.unindent()


Expand Down
Loading

0 comments on commit 49d7826

Please sign in to comment.