perfkitbenchmarker/linux_benchmarks/speccpu2017_benchmark.py (254 lines of code) (raw):
# Copyright 2018 PerfKitBenchmarker Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Runs SPEC CPU2017.
From the SPEC CPU2017 documentation:
The SPEC CPU 2017 benchmark package contains SPEC's next-generation,
industry-standardized, CPU intensive suites for measuring and comparing
compute intensive performance, stressing a system's processor,
memory subsystem and compiler.
SPEC CPU2017 homepage: http://www.spec.org/cpu2017/
"""
import os
import re
import time
from absl import flags
from perfkitbenchmarker import background_tasks
from perfkitbenchmarker import configs
from perfkitbenchmarker import data
from perfkitbenchmarker import errors
from perfkitbenchmarker import sample
from perfkitbenchmarker import vm_util
from perfkitbenchmarker.linux_packages import build_tools
from perfkitbenchmarker.linux_packages import speccpu
from perfkitbenchmarker.linux_packages import speccpu2017
INT_SUITE = [
'perlbench',
'gcc',
'mcf',
'omnetpp',
'xalancbmk',
'x264',
'deepsjeng',
'leela',
'exchange2',
'xz',
]
INTSPEED_SUITE = [benchmark + '_s' for benchmark in INT_SUITE]
INTRATE_SUITE = [benchmark + '_r' for benchmark in INT_SUITE]
COMMON_FP_SUITE = [
'bwaves',
'cactuBSSN',
'lbm',
'wrf',
'cam4',
'imagick',
'nab',
'fotonik3d',
'roms',
]
FPSPEED_SUITE = [benchmark + '_s' for benchmark in COMMON_FP_SUITE] + ['pop2_s']
FPRATE_SUITE = [benchmark + '_r' for benchmark in COMMON_FP_SUITE] + [
'namd_r',
'parest_r',
'povray_r',
'blender_r',
]
FLAGS = flags.FLAGS
flags.DEFINE_boolean(
'spec17_build_only',
False,
"Compile benchmarks only, but don't run benchmarks. "
'Defaults to False. The benchmark fails if the build '
'fails.',
)
flags.DEFINE_boolean(
'spec17_rebuild',
True,
'Rebuild spec binaries, defaults to True. Set to False '
'when using run_stage_iterations > 1 to avoid recompiling',
)
flags.DEFINE_string(
'spec17_gcc_flags',
'-O3',
'Flags to be used to override the default GCC -O3 used to compile SPEC.',
)
flags.DEFINE_boolean(
'spec17_best_effort',
False,
'Best effort run of spec. Allow missing results without failing.',
)
flags.DEFINE_string(
'spec17_numa_bind_config',
None,
'Name of the config file to use for specifying NUMA binding. '
'None by default. To enable numa binding, ensure the runspec_config file, '
'contains "include: numactl.inc". In addition, setting to "auto", will '
'attempt to pin to local numa node and pin each SIR copy to a '
'exclusive hyperthread.',
)
BENCHMARK_NAME = 'speccpu2017'
BENCHMARK_CONFIG = """
speccpu2017:
description: Runs SPEC CPU2017
vm_groups:
default:
vm_spec: *default_dual_core
disk_spec: *default_500_gb
"""
KB_TO_GB_MULTIPLIER = 1000000
LOG_FILENAME = {
'fprate': 'CPU2017.001.fprate.refrate.txt',
'fpspeed': 'CPU2017.001.fpspeed.refspeed.txt',
'intrate': 'CPU2017.001.intrate.refrate.txt',
'intspeed': 'CPU2017.001.intspeed.refspeed.txt',
}
def GetConfig(user_config):
return configs.LoadConfig(BENCHMARK_CONFIG, user_config, BENCHMARK_NAME)
def CheckPrerequisites(benchmark_config):
"""FDO is only allow with peak measurement.
Args:
benchmark_config: benchmark_config
Raises:
errors.Config.InvalidValue: On invalid flag setting.
"""
del benchmark_config # unused
if FLAGS.spec17_fdo and FLAGS.spec_runmode == 'base':
raise errors.Config.InvalidValue(
'Feedback Directed Optimization is not allowed with base report.'
)
def CheckVmPrerequisites(vm):
"""Checks the system memory on this vm.
Rate runs require 2 GB minimum system memory.
Speed runs require 16 GB minimum system memory.
Taken from https://www.spec.org/cpu2017/Docs/system-requirements.html
Args:
vm: virtual machine to run spec on.
Raises:
errors.Config.InvalidValue: On insufficient vm memory.
"""
available_memory = vm.total_free_memory_kb
if 'intspeed' in FLAGS.spec17_subset or 'fpspeed' in FLAGS.spec17_subset:
# AWS machines that advertise 16 GB have slightly less than that
if available_memory < 15.6 * KB_TO_GB_MULTIPLIER:
raise errors.Config.InvalidValue(
'Available memory of %s GB is insufficient for spec17 speed runs.'
% (available_memory / KB_TO_GB_MULTIPLIER)
)
if 'intrate' in FLAGS.spec17_subset or 'fprate' in FLAGS.spec17_subset:
if available_memory < 2 * KB_TO_GB_MULTIPLIER:
raise errors.Config.InvalidValue(
'Available memory of %s GB is insufficient for spec17 rate runs.'
% (available_memory / KB_TO_GB_MULTIPLIER)
)
def Prepare(benchmark_spec):
"""Installs SPEC CPU2017 on the target vm.
Args:
benchmark_spec: The benchmark specification. Contains all data that is
required to run the benchmark.
"""
vms = benchmark_spec.vms
background_tasks.RunThreaded(_Prepare, vms)
def _Prepare(vm):
CheckVmPrerequisites(vm)
vm.Install('speccpu2017')
if 'ampere' in FLAGS.runspec_config:
vm.Install('jemalloc')
# Set attribute outside of the install function, so benchmark will work
# even with --install_packages=False.
config = speccpu2017.GetSpecInstallConfig(vm.GetScratchDir())
setattr(vm, speccpu.VM_STATE_ATTR, config)
_GenIncFile(vm)
_GenNumactlIncFile(vm)
def _GenNumactlIncFile(vm):
"""Generates numactl.inc file."""
config = speccpu2017.GetSpecInstallConfig(vm.GetScratchDir())
if FLAGS.spec17_numa_bind_config:
remote_numactl_path = os.path.join(config.spec_dir, 'config', 'numactl.inc')
vm.Install('numactl')
if FLAGS.spec17_numa_bind_config == 'auto':
# Upload config and rename
numa_bind_cfg = [
'intrate,fprate:',
'submit = echo "${command}" > run.sh ; $BIND bash run.sh',
]
for idx in range(vm.NumCpusForBenchmark()):
numa_bind_cfg.append(
f'bind{idx} = /usr/bin/numactl --physcpubind=+{idx} --localalloc'
)
vm_util.CreateRemoteFile(
vm, '\n'.join(numa_bind_cfg), remote_numactl_path
)
else:
vm.PushFile(
data.ResourcePath(FLAGS.spec17_numa_bind_config), remote_numactl_path
)
vm.RemoteCommand(f'cat {remote_numactl_path}')
else:
cfg_file_path = getattr(vm, speccpu.VM_STATE_ATTR, config).cfg_file_path
vm.RemoteCommand(f'sed -i "/include: numactl.inc/d" {cfg_file_path}')
def _GenIncFile(vm):
"""Generates .inc files when not already included in tarballs."""
if re.search(
r'amd_(speed|rate)_aocc300_milan_(A1|B2).cfg', FLAGS.runspec_config
):
# python script requires stdin
vm.RemoteCommand("printf 'yes\nyes\nyes\n' > yes.txt")
config = FLAGS.runspec_config.split('.')[0]
cmd = (
f'cd /scratch/cpu2017 && sudo ./run_{config}.py '
'--tuning=base --exit_after_inc_gen < ~/yes.txt'
)
stdout, _ = vm.RemoteCommand(cmd)
if 'ERROR' in stdout:
raise errors.Benchmarks.PrepareException(
'Error during creation of .inc file.'
' This is likely due to a missing entry in cpu_info.json for the'
' given machine type and speccpu17 tar file. This will cause the'
' run to fail.'
)
def Run(benchmark_spec):
"""Runs SPEC CPU2017 on the target vm.
Args:
benchmark_spec: The benchmark specification. Contains all data that is
required to run the benchmark.
Returns:
A list of lists of sample.Sample objects.
"""
vms = benchmark_spec.vms
samples = []
grouped_samples = background_tasks.RunThreaded(_Run, vms)
for samples_list in grouped_samples:
samples.extend(samples_list)
return samples
def _OverwriteGccO3(vm):
config = speccpu2017.GetSpecInstallConfig(vm.GetScratchDir())
config_filepath = getattr(vm, speccpu.VM_STATE_ATTR, config).cfg_file_path
cmd = f"sed -i 's/-g -O3/{FLAGS.spec17_gcc_flags}/g' {config_filepath}"
vm.RemoteCommand(cmd)
return
def _Run(vm):
"""See base method.
Args:
vm: The vm to run the benchmark on.
Returns:
A list of sample.Sample objects.
"""
# Make changes e.g. compiler flags to spec config file.
if 'gcc' in FLAGS.runspec_config:
_OverwriteGccO3(vm)
# swap only if necessary; free local node memory and avoid remote memory;
# reset caches; set stack size to unlimited
# Also consider setting enable_transparent_hugepages flag to true
cmd = (
'echo 1 | sudo tee /proc/sys/vm/swappiness && '
'echo 1 | sudo tee /proc/sys/vm/zone_reclaim_mode && '
'sync ; echo 3 | sudo tee /proc/sys/vm/drop_caches && '
'ulimit -s unlimited && '
)
cmd += 'runcpu '
if FLAGS.spec17_build_only:
cmd += '--action build '
if FLAGS.spec17_rebuild:
cmd += '--rebuild '
version_specific_parameters = []
copies = FLAGS.spec17_copies or vm.NumCpusForBenchmark()
version_specific_parameters.append(f' --copies={copies} ')
version_specific_parameters.append(
' --threads=%s ' % (FLAGS.spec17_threads or vm.NumCpusForBenchmark())
)
version_specific_parameters.append(
' --define build_ncpus=%s ' % (vm.NumCpusForBenchmark())
)
if FLAGS.spec17_fdo:
version_specific_parameters.append('--feedback ')
vm.RemoteCommand('cd /scratch/cpu2017; mkdir fdo_profiles')
start_time = time.time()
stdout, _ = speccpu.Run(
vm, cmd, ' '.join(FLAGS.spec17_subset), version_specific_parameters
)
if not FLAGS.spec17_best_effort:
if 'Error' in stdout and 'Please review this file' in stdout:
raise errors.Benchmarks.RunError('Error during SPEC compilation.')
if FLAGS.spec17_build_only:
return [
sample.Sample(
'compilation_time',
time.time() - start_time,
's',
{
'spec17_subset': FLAGS.spec17_subset,
'gcc_version': build_tools.GetVersion(vm, 'gcc'),
},
)
]
partial_results = True
# Do not allow partial results if any benchmark subset is a full suite.
for benchmark_subset in FLAGS.benchmark_subset:
if benchmark_subset in ['intspeed', 'fpspeed', 'intrate', 'fprate']:
partial_results = False
log_files = set()
for test in FLAGS.spec17_subset:
if test in LOG_FILENAME:
log_files.add(LOG_FILENAME[test])
else:
if test in INTSPEED_SUITE:
log_files.add(LOG_FILENAME['intspeed'])
elif test in INTRATE_SUITE:
log_files.add(LOG_FILENAME['intrate'])
elif test in FPSPEED_SUITE:
log_files.add(LOG_FILENAME['fpspeed'])
elif test in FPRATE_SUITE:
log_files.add(LOG_FILENAME['fprate'])
for log_file in log_files:
vm.RemoteCommand(
f'cp {vm.GetScratchDir()}/cpu2017/result/{log_file} ~/{log_file}.log'
)
vm.PullFile(vm_util.GetTempDir(), f'~/{log_file}.log')
samples = speccpu.ParseOutput(vm, log_files, partial_results, None)
for item in samples:
item.metadata['vm_name'] = vm.name
item.metadata['spec17_gcc_flags'] = FLAGS.spec17_gcc_flags
item.metadata['spec17_numa_bind_config'] = FLAGS.spec17_numa_bind_config
item.metadata['spec17_copies'] = copies
return samples
def Cleanup(benchmark_spec):
"""Cleans up SPEC CPU2017 from the target vm.
Args:
benchmark_spec: The benchmark specification. Contains all data that is
required to run the benchmark.
"""
vms = benchmark_spec.vms
background_tasks.RunThreaded(speccpu.Uninstall, vms)