Skip to content

Commit

Permalink
Merge pull request #555 from Kaufi-Jonas/j-optimize_perf
Browse files Browse the repository at this point in the history
speed up slow build times for ./configure based projects
  • Loading branch information
simbuerg authored Mar 31, 2023
2 parents 480223c + eabd76a commit 63612a6
Show file tree
Hide file tree
Showing 20 changed files with 159 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[settings]
known_third_party = attr,dill,faker,git,jinja2,mock,parse,pathos,pkg_resources,plumbum,psutil,pygit2,pygtrie,pyparsing,pytest,pytest_git,result,rich,schema,setuptools,six,sqlalchemy,yaml
known_third_party = attr,dill,faker,git,jinja2,mock,parse,plumbum,psutil,pygtrie,pyparsing,pytest,pytest_git,result,rich,schema,setuptools,six,sqlalchemy,yaml
multi_line_output=3
use_parentheses = True
include_trailing_comma: True
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,4 @@ int-import-graph=

[EXCEPTIONS]

overgeneral-exceptions=Exception
overgeneral-exceptions=builtins.Exception
4 changes: 2 additions & 2 deletions benchbuild/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

def __init__() -> None:
"""Initialize all plugins and settings."""
__PLUGINS__.discover()
__SETTINGS__.setup_config(CFG)
if __PLUGINS__.discover():
__SETTINGS__.setup_config(CFG)


__init__()
Expand Down
4 changes: 3 additions & 1 deletion benchbuild/environments/adapters/podman.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from plumbum import local, ProcessExecutionError
from result import Result, Err, Ok
from rich import print
from rich.markdown import Markdown

from benchbuild.environments.adapters import buildah
from benchbuild.environments.adapters.common import (
Expand Down Expand Up @@ -173,6 +172,9 @@ def _create(
'--mount', f'type=bind,src={source},target={target}']

if interactive:
# pylint: disable=import-outside-toplevel
from rich.markdown import Markdown

entrypoint = buildah.find_entrypoint(image.name)
print(
Markdown(
Expand Down
3 changes: 2 additions & 1 deletion benchbuild/environments/service_layer/debug.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from plumbum import ProcessExecutionError
from rich import print
from rich.markdown import Markdown

from benchbuild.environments.adapters.common import bb_buildah
from benchbuild.environments.domain import events
Expand All @@ -25,6 +24,8 @@ def debug_image_kept(
"""
Spawn a debug session of the kept image and provide diagnostics.
"""
# pylint: disable=import-outside-toplevel
from rich.markdown import Markdown
with uow:
container = uow.create(event.image_name, event.failed_image_name)
if container is None:
Expand Down
10 changes: 6 additions & 4 deletions benchbuild/extensions/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
from plumbum.commands.base import BoundCommand

from benchbuild.extensions import base
from benchbuild.settings import CFG
from benchbuild.utils import db, run

if TYPE_CHECKING:
from benchbuild.project import Project
from benchbuild.experiment import Experiment
from benchbuild.project import Project

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -65,9 +66,10 @@ def __call__(
default_flow_style=False
)
)
db.persist_config(
run_info.db_run, run_info.session, self.config
)
if CFG["db"]["enabled"]:
db.persist_config(
run_info.db_run, run_info.session, self.config
)

if run_info.has_failed:
with run.track_execution(
Expand Down
11 changes: 6 additions & 5 deletions benchbuild/extensions/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from plumbum import local

from benchbuild.extensions import base
from benchbuild.settings import CFG
from benchbuild.utils import db, run
from benchbuild.utils.settings import get_number_of_jobs

Expand Down Expand Up @@ -45,9 +46,10 @@ def __call__(self, binary_command, *args, **kwargs):
)
self.config['baseline'] = \
os.getenv("BB_IS_BASELINE", "False")
db.persist_config(
run_info.db_run, run_info.session, self.config
)
if CFG["db"]["enabled"]:
db.persist_config(
run_info.db_run, run_info.session, self.config
)
res = self.call_next(binary_command, *args, **kwargs)
res.append(run_info)
return res
Expand All @@ -69,6 +71,7 @@ def __init__(self, *extensions, limit="10m", **kwargs):
self.limit = limit

def __call__(self, binary_command, *args, **kwargs):
# pylint: disable=import-outside-toplevel
from benchbuild.utils.cmd import timeout
return self.call_next(
timeout[self.limit, binary_command], *args, **kwargs
Expand All @@ -83,8 +86,6 @@ class SetThreadLimit(base.Extension):
"""

def __call__(self, binary_command, *args, **kwargs):
from benchbuild.settings import CFG

config = self.config
if config is not None and 'jobs' in config.keys():
jobs = get_number_of_jobs(config)
Expand Down
5 changes: 5 additions & 0 deletions benchbuild/extensions/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import parse

from benchbuild.extensions import base
from benchbuild.settings import CFG
from benchbuild.utils import db
from benchbuild.utils.cmd import time

Expand All @@ -20,6 +21,10 @@ def __call__(self, binary_command, *args, may_wrap=True, **kwargs):

def handle_timing(run_infos):
"""Takes care of the formating for the timing statistics."""
if not CFG["db"]["enabled"]:
return run_infos

# pylint: disable=import-outside-toplevel
from benchbuild.utils import schema as s

session = s.Session()
Expand Down
6 changes: 5 additions & 1 deletion benchbuild/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@
LOG = logging.getLogger(__name__)


def discover() -> None:
def discover() -> bool:
"""Import all plugins listed in our configuration."""
something_imported = False
if CFG["plugins"]["autoload"]:
experiment_plugins = CFG["plugins"]["experiments"].value
project_plugins = CFG["plugins"]["projects"].value

for plugin in itertools.chain(experiment_plugins, project_plugins):
something_imported = True
try:
importlib.import_module(plugin)
except ImportError as import_error:
LOG.error("Could not find '%s'", import_error.name)
LOG.debug("ImportError: %s", import_error)

return something_imported
6 changes: 3 additions & 3 deletions benchbuild/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,8 @@ def __default_primary_source(self) -> str: # pylint: disable=unused-private-mem
runtime_extension = attr.ib(default=None)

def __attrs_post_init__(self) -> None:
db.persist_project(self)
if CFG["db"]["enabled"]:
db.persist_project(self)

# select container image
if isinstance(type(self).CONTAINER, ContainerImage):
Expand All @@ -345,7 +346,7 @@ def __attrs_post_init__(self) -> None:
)
else:
primary_source = primary(*self.SOURCE)
if isinstance(primary_source,source.BaseVersionFilter):
if isinstance(primary_source, source.BaseVersionFilter):
primary_source = primary_source.child
if not isinstance(primary_source, Git):
raise AssertionError(
Expand All @@ -361,7 +362,6 @@ def __attrs_post_init__(self) -> None:
self.container = copy.deepcopy(image)
break


def clean(self) -> None:
"""Clean the project build directory."""
builddir_p = local.path(self.builddir)
Expand Down
9 changes: 8 additions & 1 deletion benchbuild/res/wrapping/run_compiler.py.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ os.environ["OPENBLAS_NUM_THREADS"] = "4"

from plumbum import TEE, local

# Performance optimization for benchbuild: don't import any experiments or
# projects. Everything necessary should be imported when loading (unpickling)
# the project and the compiler.
os.environ["BB_PLUGINS_AUTOLOAD"] = "False"

from benchbuild.settings import CFG
from benchbuild.utils import log
from benchbuild.utils.db import persist_project
from benchbuild.utils.run import exit_code_from_run_infos
Expand All @@ -24,7 +30,8 @@ def update_project(argv):
name = project_p.basename
break
PROJECT.name = name
persist_project(PROJECT)
if CFG["db"]["enabled"]:
persist_project(PROJECT)


def main(argv):
Expand Down
5 changes: 5 additions & 0 deletions benchbuild/res/wrapping/run_dynamic.py.inc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import sys

from plumbum import TEE, local

# Performance optimization for benchbuild: don't import any experiments or
# projects. Everything necessary should be imported when loading (unpickling)
# the project.
os.environ["BB_PLUGINS_AUTOLOAD"] = "False"

from benchbuild.utils import log
from benchbuild.utils.db import persist_project
from benchbuild.utils.run import exit_code_from_run_infos
Expand Down
6 changes: 6 additions & 0 deletions benchbuild/res/wrapping/run_static.py.inc
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
#!{{ python|default("/usr/bin/env python3") }}
#
import os
import sys

from plumbum import TEE, local

# Performance optimization for benchbuild: don't import any experiments or
# projects. Everything necessary should be imported when loading (unpickling)
# the project.
os.environ["BB_PLUGINS_AUTOLOAD"] = "False"

from benchbuild.utils import log
from benchbuild.utils.run import exit_code_from_run_infos
from benchbuild.utils.wrapping import load
Expand Down
8 changes: 6 additions & 2 deletions benchbuild/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,13 @@
}

CFG['db'] = {
"enabled": {
"desc": "Whether the database is enabled.",
"default": False
},
"connect_string": {
"desc": "sqlalchemy connect string",
"default": "sqlite://"
"default": ""
},
"rollback": {
"desc": "Rollback all operations after benchbuild completes.",
Expand Down Expand Up @@ -381,7 +385,7 @@
"storage_driver": {
"default": "vfs",
"desc": "Storage driver for containers."
},
},
"input": {
"default": "container.tar.bz2",
"desc": "Input container file/folder."
Expand Down
51 changes: 33 additions & 18 deletions benchbuild/utils/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
import typing as tp
from datetime import datetime

import pathos.multiprocessing as mp
import sqlalchemy as sa
from plumbum import ProcessExecutionError

from benchbuild import command, signals, source
Expand Down Expand Up @@ -250,7 +248,7 @@ def clean_mountpoints(root: str) -> None:
root: All UnionFS-mountpoints under this directory will be
unmounted.
"""
import psutil
import psutil # pylint: disable=import-outside-toplevel

umount_paths = []
real_root = os.path.realpath(root)
Expand Down Expand Up @@ -341,22 +339,27 @@ def __init__(
self.experiment = experiment

def __call__(self) -> StepResult:
group, session = run.begin_run_group(self.project, self.experiment)
signals.handlers.register(run.fail_run_group, group, session)
if CFG["db"]["enabled"]:
group, session = run.begin_run_group(self.project, self.experiment)
signals.handlers.register(run.fail_run_group, group, session)
try:
self.project.run_tests()
run.end_run_group(group, session)
if CFG["db"]["enabled"]:
run.end_run_group(group, session)
self.status = StepResult.OK
except ProcessExecutionError:
run.fail_run_group(group, session)
if CFG["db"]["enabled"]:
run.fail_run_group(group, session)
self.status = StepResult.ERROR
raise
except KeyboardInterrupt:
run.fail_run_group(group, session)
if CFG["db"]["enabled"]:
run.fail_run_group(group, session)
self.status = StepResult.ERROR
raise
finally:
signals.handlers.deregister(run.fail_run_group)
if CFG["db"]["enabled"]:
signals.handlers.deregister(run.fail_run_group)

return self.status

Expand Down Expand Up @@ -444,6 +447,7 @@ def __init__(
def begin_transaction(
self,
) -> tp.Tuple["benchbuild.utils.schema.Experiment", tp.Any]:
import sqlalchemy as sa # pylint: disable=import-outside-toplevel
experiment, session = db.persist_experiment(self.experiment)
if experiment.begin is None:
experiment.begin = datetime.now()
Expand All @@ -467,6 +471,7 @@ def begin_transaction(
def end_transaction(
experiment: "benchbuild.utils.schema.Experiment", session: tp.Any
) -> None:
import sqlalchemy as sa # pylint: disable=import-outside-toplevel
try:
experiment.end = max(experiment.end, datetime.now())
session.add(experiment)
Expand All @@ -475,6 +480,9 @@ def end_transaction(
LOG.error(inv_req)

def __run_children(self, num_processes: int) -> tp.List[StepResult]:
# pylint: disable=import-outside-toplevel
import pathos.multiprocessing as mp

results = []
actions = self.actions

Expand All @@ -496,12 +504,14 @@ def __run_children(self, num_processes: int) -> tp.List[StepResult]:
def __call__(self) -> StepResult:
results = []
session = None
experiment, session = self.begin_transaction()
if CFG["db"]["enabled"]:
experiment, session = self.begin_transaction()
try:
results = self.__run_children(int(CFG["parallel_processes"]))
finally:
self.end_transaction(experiment, session)
signals.handlers.deregister(self.end_transaction)
if CFG["db"]["enabled"]:
self.end_transaction(experiment, session)
signals.handlers.deregister(self.end_transaction)
self.status = max(results) if results else StepResult.OK
return self.status

Expand Down Expand Up @@ -636,22 +646,27 @@ def __init__(
])

def __call__(self) -> StepResult:
group, session = run.begin_run_group(self.project, self.experiment)
signals.handlers.register(run.fail_run_group, group, session)
if CFG["db"]["enabled"]:
group, session = run.begin_run_group(self.project, self.experiment)
signals.handlers.register(run.fail_run_group, group, session)
try:
self.status = max([workload() for workload in self.actions],
default=StepResult.OK)
run.end_run_group(group, session)
if CFG["db"]["enabled"]:
run.end_run_group(group, session)
except ProcessExecutionError:
run.fail_run_group(group, session)
if CFG["db"]["enabled"]:
run.fail_run_group(group, session)
self.status = StepResult.ERROR
raise
except KeyboardInterrupt:
run.fail_run_group(group, session)
if CFG["db"]["enabled"]:
run.fail_run_group(group, session)
self.status = StepResult.ERROR
raise
finally:
signals.handlers.deregister(run.fail_run_group)
if CFG["db"]["enabled"]:
signals.handlers.deregister(run.fail_run_group)

return self.status

Expand Down
Loading

0 comments on commit 63612a6

Please sign in to comment.