Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

speed up slow build times for ./configure based projects #555

Merged
merged 14 commits into from
Mar 31, 2023
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
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