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

feat(autotuner): add --sanitize argument (to invoke ThreadSanitizer) #677

Merged
merged 1 commit into from
Oct 28, 2024
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
1 change: 1 addition & 0 deletions discopop_library/EmpiricalAutotuning/ArgumentClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class AutotunerArguments(GeneralArguments):
project_path: str
dot_dp_path: str
skip_cleanup: bool
sanitize: bool

def __post_init__(self) -> None:
self.__validate()
Expand Down
16 changes: 10 additions & 6 deletions discopop_library/EmpiricalAutotuning/Autotuner.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ def get_unique_configuration_id() -> int:

def run(arguments: AutotunerArguments) -> None:
logger.info("Starting discopop autotuner.")
debug_stats: List[Tuple[List[SUGGESTION_ID], float, int, bool, str]] = []
debug_stats: List[Tuple[List[SUGGESTION_ID], float, int, bool, bool, str]] = []
statistics_graph = StatisticsGraph()
statistics_step_num = 0

# get untuned reference result
reference_configuration = CodeConfiguration(arguments.project_path, arguments.dot_dp_path)
reference_configuration.execute(timeout=None, is_initial=True)
reference_configuration.execute(arguments, timeout=None, is_initial=True)
statistics_graph.set_root(
reference_configuration.get_statistics_graph_label(),
color=reference_configuration.get_statistics_graph_color(),
Expand All @@ -58,6 +58,7 @@ def run(arguments: AutotunerArguments) -> None:
cast(ExecutionResult, reference_configuration.execution_result).runtime,
cast(ExecutionResult, reference_configuration.execution_result).return_code,
cast(ExecutionResult, reference_configuration.execution_result).result_valid,
cast(ExecutionResult, reference_configuration.execution_result).thread_sanitizer,
reference_configuration.root_path,
)
)
Expand Down Expand Up @@ -141,7 +142,7 @@ def run(arguments: AutotunerArguments) -> None:
visited_configurations.append(current_config)
tmp_config = reference_configuration.create_copy(get_unique_configuration_id)
tmp_config.apply_suggestions(arguments, current_config)
tmp_config.execute(timeout=timeout_after)
tmp_config.execute(arguments, timeout=timeout_after)
statistics_graph.add_child(
"step "
+ str(statistics_step_num)
Expand All @@ -159,6 +160,7 @@ def run(arguments: AutotunerArguments) -> None:
cast(ExecutionResult, tmp_config.execution_result).runtime,
cast(ExecutionResult, tmp_config.execution_result).return_code,
cast(ExecutionResult, tmp_config.execution_result).result_valid,
cast(ExecutionResult, tmp_config.execution_result).thread_sanitizer,
tmp_config.root_path,
)
)
Expand Down Expand Up @@ -241,7 +243,7 @@ def run(arguments: AutotunerArguments) -> None:

# show debug stats
stats_str = "Configuration measurements:\n"
stats_str += "[time]\t[applied suggestions]\t[return code]\t[result valid]\t[path]\n"
stats_str += "[time]\t[applied suggestions]\t[return code]\t[result valid]\t[thread sanitizer]\t[path]\n"
for stats in sorted(debug_stats, key=lambda x: (x[1]), reverse=True):
stats_str += (
str(round(stats[1], 3))
Expand All @@ -254,19 +256,21 @@ def run(arguments: AutotunerArguments) -> None:
+ str(stats[3])
+ "\t"
+ str(stats[4])
+ "\t"
+ str(stats[5])
+ "\n"
)
logger.info(stats_str)

# export measurements for pdf creation
if False: # export all measurements
with open("measurements.csv", "w+") as f:
f.write("ID; time; return_code;\n")
f.write("ID; time; return_code; thread_sanitizer\n")
for stats in sorted(debug_stats, key=lambda x: x[1], reverse=True):
f.write(str(stats[0]) + "; " + str(round(stats[1], 3)) + "; " + str(stats[2]) + ";" + "\n")
else: # export only sequential and best measurement
with open("measurements.csv", "w+") as f:
f.write("ID; time; return_code;\n")
f.write("ID; time; return_code; thread_sanitizer\n")
# write sequential measurement
for stats in sorted(debug_stats, key=lambda x: x[1], reverse=True):
if str(stats[0]) == "[]":
Expand Down
21 changes: 19 additions & 2 deletions discopop_library/EmpiricalAutotuning/Classes/CodeConfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, root_path: str, config_dot_dp_path: str):
def __str__(self) -> str:
return self.root_path

def execute(self, timeout: Optional[float], is_initial: bool = False) -> None:
def execute(self, arguments: AutotunerArguments, timeout: Optional[float], is_initial: bool = False) -> None:
# create timeout string
timeout_string = "" if timeout is None else "timeout " + str(timeout) + " "
if is_initial:
Expand Down Expand Up @@ -77,12 +77,29 @@ def execute(self, timeout: Optional[float], is_initial: bool = False) -> None:
if validity_check_result.returncode != 0:
result_valid = False

# check for result validity using thread sanitizer
thread_sanitizer_valid = True
if os.path.exists(os.path.join(self.root_path, "DP_SANITIZE.sh")) and arguments.sanitize:
logger.info("Checking thread sanity: " + str(self))
thread_sanitizer_result = subprocess.run(
"./DP_SANITIZE.sh && ./DP_EXECUTE.sh",
cwd=self.root_path,
executable="/bin/bash",
shell=True,
capture_output=True,
)
thread_sanitizer_output = str(thread_sanitizer_result.stdout.decode("utf-8"))
logger.getChild("ThreadSanitizerOutput").debug(thread_sanitizer_output)
if "WARNING: ThreadSanitizer: data race" in thread_sanitizer_output:
thread_sanitizer_valid = False

# reporting
logger.debug("Execution took " + str(round(required_time, 4)) + " s")
logger.debug("Execution return code: " + str(result.returncode))
logger.debug("Execution result valid: " + str(result_valid))
logger.debug("ThreadSanitizer valid: " + str(thread_sanitizer_valid))

self.execution_result = ExecutionResult(required_time, result.returncode, result_valid)
self.execution_result = ExecutionResult(required_time, result.returncode, result_valid, thread_sanitizer_valid)

def create_copy(self, get_new_configuration_id: Callable[[], int]) -> CodeConfiguration:
# create a copy of the project folder
Expand Down
14 changes: 12 additions & 2 deletions discopop_library/EmpiricalAutotuning/Classes/ExecutionResult.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,23 @@ class ExecutionResult(object):
runtime: float
return_code: int
result_valid: bool
thread_sanitizer: bool

def __init__(self, runtime: float, return_code: int, result_valid: bool):
def __init__(self, runtime: float, return_code: int, result_valid: bool, thread_sanitizer: bool):
self.runtime = runtime
self.return_code = return_code
self.result_valid = result_valid
self.thread_sanitizer = thread_sanitizer

def __str__(self) -> str:
return (
"" + "time: " + str(self.runtime) + " code: " + str(self.return_code) + " valid: " + str(self.result_valid)
""
+ "time: "
+ str(self.runtime)
+ " code: "
+ str(self.return_code)
+ " valid: "
+ str(self.result_valid)
+ " TSAN: "
+ str(self.thread_sanitizer)
)
8 changes: 5 additions & 3 deletions discopop_library/EmpiricalAutotuning/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def parse_args() -> AutotunerArguments:
parser = ArgumentParser(description="DiscoPoP Autotuner")

# fmt: off

parser.add_argument("--log", type=str, default="WARNING", help="Specify log level: DEBUG, INFO, WARNING, ERROR, CRITICAL")
parser.add_argument("--write-log", action="store_true", help="Create Logfile.")
parser.add_argument("--project-path", type=str, default=os.getcwd(), help="Root path of the project to be tuned. \
Expand All @@ -27,8 +27,9 @@ def parse_args() -> AutotunerArguments:
DP_EXECUTE.sh may return not 0, if either the execution or validation of the result failed. \
A third script DP_VALIDATE.sh might be added to add a validation step, where return code 0 is interpreted as a success, i.e. a valid result.")
parser.add_argument("--dot-dp-path", type=str, default=os.path.join(os.getcwd(), ".discopop"), help="Path to the .discopop folder.")
parser.add_argument("--skip-cleanup", action="store_true", help="disable the deletion of created code variants. May require a lot of disk space." )
# fmt: on
parser.add_argument("--skip-cleanup", action="store_true", help="Disable the deletion of created code variants. May require a lot of disk space." )
parser.add_argument("--sanitize", action="store_true", help="Enable the invocation of ThreadSanitizer if DP_SANITIZE.sh is provided." )
# fmt: is provided.

arguments = parser.parse_args()

Expand All @@ -38,6 +39,7 @@ def parse_args() -> AutotunerArguments:
project_path=arguments.project_path,
dot_dp_path=arguments.dot_dp_path,
skip_cleanup=arguments.skip_cleanup,
sanitize=arguments.sanitize,
)


Expand Down
Loading