Skip to content

Commit

Permalink
Minor changes to denoise.py (#274)
Browse files Browse the repository at this point in the history
  • Loading branch information
smarr authored Nov 9, 2024
2 parents be6cabd + 65e5560 commit 9a95df7
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 86 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ jobs:
run: |
pip install mypy
mypy --install-types --non-interactive rebench
mypy rebench
if: matrix.python-version == '3.13'

- name: Upload coverage results to Coveralls
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ extend-exclude = '''
| /rebench/model/experiment.py
| /rebench/configurator.py
| /rebench/subprocess_kill.py
| /rebench/denoise.py
| /rebench/model/termination_check.py
| /rebench/tests/bugs/issue_27_invalid_run_not_handled_test.py
| /rebench/tests/bugs/issue_112_invocations_setting_ignored_test.py
Expand Down
211 changes: 131 additions & 80 deletions rebench/denoise.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import json
import os
import subprocess
import sys

from argparse import ArgumentParser
from math import log, floor
from multiprocessing import Pool
from subprocess import check_output, CalledProcessError, DEVNULL, STDOUT

from .output import output_as_str, UIError
from .subprocess_kill import kill_process
Expand All @@ -30,10 +30,13 @@ def get_which(self):
if os.path.isfile("/usr/bin/which"):
self._which_path = "/usr/bin/which"
else:
raise UIError("The basic `which` command was not found." +
" In many systems it is available at /usr/bin/which." +
" If it is elsewhere rebench-denoise will need to be" +
" adapted to support a different location.\n", None)
raise UIError(
"The basic `which` command was not found."
" In many systems it is available at /usr/bin/which."
" If it is elsewhere rebench-denoise will need to be"
" adapted to support a different location.\n",
None,
)

return self._which_path

Expand All @@ -44,19 +47,18 @@ def _absolute_path_for_command(self, command, arguments_for_successful_exe):
"""
try:
selected_cmd = output_as_str(
subprocess.check_output(
[self.get_which(), command],
shell=False, stderr=subprocess.DEVNULL)).strip()
check_output([self.get_which(), command], shell=False, stderr=DEVNULL)
).strip()
result_cmd = os.path.realpath(selected_cmd)
except subprocess.CalledProcessError:
except CalledProcessError:
result_cmd = command

try:
subprocess.check_output(
[result_cmd] + arguments_for_successful_exe,
shell=False, stderr=subprocess.DEVNULL)
check_output(
[result_cmd] + arguments_for_successful_exe, shell=False, stderr=DEVNULL
)
return result_cmd
except (subprocess.CalledProcessError, FileNotFoundError):
except (CalledProcessError, FileNotFoundError):
return False

def has_cset(self):
Expand All @@ -73,18 +75,21 @@ def set_cset(self, cset_path):

def has_denoise(self):
if self._denoise_path is None:
self._denoise_path = self._absolute_path_for_command("rebench-denoise", ["--version"])
self._denoise_path = self._absolute_path_for_command(
"rebench-denoise", ["--version"]
)

return self._denoise_path is not None and self._denoise_path is not False

def get_denoise(self):
if not self.has_denoise():
raise UIError("rebench-denoise not found. " +
"Was ReBench installed so that `rebench` and `rebench-denoise` " +
"are on the PATH? Python's bin directory for packages " +
"may need to be added to PATH manually.\n\n" +
"To use ReBench without rebench-denoise, use the --no-denoise option.\n",
None)
raise UIError(
"rebench-denoise not found. "
"Was ReBench installed so that `rebench` and `rebench-denoise` are on the PATH? "
"Python's bin directory for packages may need to be added to PATH manually.\n\n"
"To use ReBench without rebench-denoise, use the --no-denoise option.\n",
None,
)

return self._denoise_path

Expand All @@ -105,8 +110,7 @@ def _can_set_niceness():
our benchmarks.
"""
try:
output = subprocess.check_output(["nice", "-n-20", "echo", "test"],
stderr=subprocess.STDOUT)
output = check_output(["nice", "-n-20", "echo", "test"], stderr=STDOUT)
output = output_as_str(output)
except OSError:
return False
Expand Down Expand Up @@ -134,8 +138,9 @@ def _activate_shielding(num_cores):
return False

try:
output = subprocess.check_output([paths.get_cset(), "shield", "-c", core_spec, "-k", "on"],
stderr=subprocess.STDOUT)
output = check_output(
[paths.get_cset(), "shield", "-c", core_spec, "-k", "on"], stderr=STDOUT
)
output = output_as_str(output)
except OSError:
return False
Expand All @@ -151,13 +156,12 @@ def _activate_shielding(num_cores):

def _reset_shielding():
try:
output = subprocess.check_output([paths.get_cset(), "shield", "-r"],
stderr=subprocess.STDOUT)
output = check_output([paths.get_cset(), "shield", "-r"], stderr=STDOUT)
output = output_as_str(output)
return "cset: done" in output
except OSError:
return False
except subprocess.CalledProcessError:
except CalledProcessError:
return False


Expand All @@ -167,13 +171,17 @@ def _reset_shielding():


def _set_scaling_governor(governor, num_cores):
assert governor in (SCALING_GOVERNOR_POWERSAVE, SCALING_GOVERNOR_PERFORMANCE),\
"The scaling governor is expected to be performance or powersave, but was " + governor
assert governor in (SCALING_GOVERNOR_POWERSAVE, SCALING_GOVERNOR_PERFORMANCE), (
"The scaling governor is expected to be performance or powersave, but was "
+ governor
)

try:
for cpu_i in range(num_cores):
filename = "/sys/devices/system/cpu/cpu" + str(cpu_i) + "/cpufreq/scaling_governor"
with open(filename, "w") as gov_file: # pylint: disable=unspecified-encoding
filename = (
"/sys/devices/system/cpu/cpu" + str(cpu_i) + "/cpufreq/scaling_governor"
)
with open(filename, "w", encoding="utf-8") as gov_file:
gov_file.write(governor + "\n")
except IOError:
return "failed"
Expand All @@ -188,8 +196,9 @@ def _set_no_turbo(with_no_turbo):
value = "0"

try:
# pylint: disable-next=unspecified-encoding
with open("/sys/devices/system/cpu/intel_pstate/no_turbo", "w") as nt_file:
with open(
"/sys/devices/system/cpu/intel_pstate/no_turbo", "w", encoding="utf-8"
) as nt_file:
nt_file.write(value + "\n")
except IOError:
return "failed"
Expand All @@ -198,22 +207,25 @@ def _set_no_turbo(with_no_turbo):

def _configure_perf_sampling(for_profiling):
try:
# pylint: disable-next=unspecified-encoding
with open("/proc/sys/kernel/perf_cpu_time_max_percent", "w") as perc_file:
with open(
"/proc/sys/kernel/perf_cpu_time_max_percent", "w", encoding="utf-8"
) as perc_file:
if for_profiling:
perc_file.write("0\n")
else:
perc_file.write("1\n")

# pylint: disable-next=unspecified-encoding
with open("/proc/sys/kernel/perf_event_max_sample_rate", "w") as sample_file:
with open(
"/proc/sys/kernel/perf_event_max_sample_rate", "w", encoding="utf-8"
) as sample_file:
# for profiling we just disabled it above, and then don't need to set it
if not for_profiling:
sample_file.write("1\n")

if for_profiling:
# pylint: disable-next=unspecified-encoding
with open("/proc/sys/kernel/perf_event_paranoid", "w") as perf_file:
with open(
"/proc/sys/kernel/perf_event_paranoid", "w", encoding="utf-8"
) as perf_file:
perf_file.write("-1\n")
except IOError:
return "failed"
Expand All @@ -226,16 +238,19 @@ def _configure_perf_sampling(for_profiling):

def _restore_perf_sampling():
try:
# pylint: disable-next=unspecified-encoding
with open("/proc/sys/kernel/perf_cpu_time_max_percent", "w") as perc_file:
with open(
"/proc/sys/kernel/perf_cpu_time_max_percent", "w", encoding="utf-8"
) as perc_file:
perc_file.write("25\n")

# pylint: disable-next=unspecified-encoding
with open("/proc/sys/kernel/perf_event_max_sample_rate", "w") as sample_file:
with open(
"/proc/sys/kernel/perf_event_max_sample_rate", "w", encoding="utf-8"
) as sample_file:
sample_file.write("50000\n")

# pylint: disable-next=unspecified-encoding
with open("/proc/sys/kernel/perf_event_paranoid", "w") as perf_file:
with open(
"/proc/sys/kernel/perf_event_paranoid", "w", encoding="utf-8"
) as perf_file:
perf_file.write("3\n")
except IOError:
return "failed"
Expand All @@ -250,11 +265,13 @@ def _minimize_noise(num_cores, use_nice, use_shielding, for_profiling):
can_nice = _can_set_niceness() if use_nice else False
shielding = _activate_shielding(num_cores) if use_shielding else False

return {"scaling_governor": governor,
"no_turbo": no_turbo,
"perf_event_max_sample_rate": perf,
"can_set_nice": can_nice,
"shielding": shielding}
return {
"scaling_governor": governor,
"no_turbo": no_turbo,
"perf_event_max_sample_rate": perf,
"can_set_nice": can_nice,
"shielding": shielding,
}


def _restore_standard_settings(num_cores, use_shielding):
Expand All @@ -263,10 +280,12 @@ def _restore_standard_settings(num_cores, use_shielding):
perf = _restore_perf_sampling()
shielding = _reset_shielding() if use_shielding else False

return {"scaling_governor": governor,
"no_turbo": no_turbo,
"perf_event_max_sample_rate": perf,
"shielding": shielding}
return {
"scaling_governor": governor,
"no_turbo": no_turbo,
"perf_event_max_sample_rate": perf,
"shielding": shielding,
}


def _exec(num_cores, use_nice, use_shielding, args):
Expand Down Expand Up @@ -334,28 +353,54 @@ def _test(num_cores):

def _shell_options():
parser = ArgumentParser()
parser.add_argument('--version', action='version',
version="%(prog)s " + rebench_version)
parser.add_argument('--json', action='store_true', default=False,
help='Output results as JSON for processing')
parser.add_argument('--without-nice', action='store_false', default=True,
dest='use_nice', help="Don't try setting process niceness")
parser.add_argument('--without-shielding', action='store_false', default=True,
dest='use_shielding', help="Don't try shielding cores")
parser.add_argument('--for-profiling', action='store_true', default=False,
dest='for_profiling', help="Don't restrict CPU usage by profiler")
parser.add_argument('--cset-path', help="Absolute path to cset", default=None)
parser.add_argument('--num-cores', help="Number of cores. Is required.", default=None)
parser.add_argument('command',
help=("`minimize`|`restore`|`exec -- `|`kill pid`|`test`: "
"`minimize` sets system to reduce noise. " +
"`restore` sets system to the assumed original settings. " +
"`exec -- ` executes the given arguments. " +
"`kill pid` send kill signal to the process with given id " +
"and all child processes. " +
"`test` executes a computation for 20 seconds in parallel. " +
"it is only useful to test rebench-denoise itself."),
default=None)
parser.add_argument(
"--version", action="version", version="%(prog)s " + rebench_version
)
parser.add_argument(
"--json",
action="store_true",
default=False,
help="Output results as JSON for processing",
)
parser.add_argument(
"--without-nice",
action="store_false",
default=True,
dest="use_nice",
help="Don't try setting process niceness",
)
parser.add_argument(
"--without-shielding",
action="store_false",
default=True,
dest="use_shielding",
help="Don't try shielding cores",
)
parser.add_argument(
"--for-profiling",
action="store_true",
default=False,
dest="for_profiling",
help="Don't restrict CPU usage by profiler",
)
parser.add_argument("--cset-path", help="Absolute path to cset", default=None)
parser.add_argument(
"--num-cores", help="Number of cores. Is required.", default=None
)
parser.add_argument(
"command",
help=(
"`minimize`|`restore`|`exec -- `|`kill pid`|`test`: "
"`minimize` sets system to reduce noise. "
"`restore` sets system to the assumed original settings. "
"`exec -- ` executes the given arguments. "
"`kill pid` send kill signal to the process with given id "
"and all child processes. "
"`test` executes a computation for 20 seconds in parallel. "
"it is only useful to test rebench-denoise itself."
),
default=None,
)
return parser


Expand All @@ -375,7 +420,9 @@ def main_func():
result = {}

if args.command == "minimize" and num_cores is not None:
result = _minimize_noise(num_cores, args.use_nice, args.use_shielding, args.for_profiling)
result = _minimize_noise(
num_cores, args.use_nice, args.use_shielding, args.for_profiling
)
elif args.command == "restore" and num_cores is not None:
result = _restore_standard_settings(num_cores, args.use_shielding)
elif args.command == "exec":
Expand All @@ -394,10 +441,14 @@ def main_func():
if args.json:
print(json.dumps(result))
else:
print("Setting scaling_governor: ", result.get("scaling_governor", None))
print(
"Setting scaling_governor: ", result.get("scaling_governor", None)
)
print("Setting no_turbo: ", result.get("no_turbo", False))
print("Setting perf_event_max_sample_rate: ",
result.get("perf_event_max_sample_rate", None))
print(
"Setting perf_event_max_sample_rate: ",
result.get("perf_event_max_sample_rate", None),
)
print("")
print("Enabled core shielding: ", result.get("shielding", False))
print("")
Expand Down
9 changes: 5 additions & 4 deletions rebench/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,11 @@ def init_environment(denoise_result, ui):
ui.warning('Was not able to determine the type of CPU used and its clock speed.' +
' Thus, these details will not be recorded with the data.\n')

result["software"] = []
result["software"].append({"name": "kernel", "version": u_name[3]})
result["software"].append({"name": "kernel-release", "version": u_name[2]})
result["software"].append({"name": "architecture", "version": u_name[4]})
result["software"] = [
{"name": "kernel", "version": u_name[3]},
{"name": "kernel-release", "version": u_name[2]},
{"name": "architecture", "version": u_name[4]}
]

global _environment # pylint: disable=global-statement
_environment = result
Expand Down

0 comments on commit 9a95df7

Please sign in to comment.