diff --git a/lib/cpu_config.rb b/lib/cpu_config.rb index f8e57827..b99c21a8 100644 --- a/lib/cpu_config.rb +++ b/lib/cpu_config.rb @@ -2,6 +2,11 @@ # Manages CPU frequency and turbo boost configuration for benchmark consistency class CPUConfig + # These scripts are separate commands so that sudoers can allow just them + # with NOPASSWD, skipping the password prompt. + TURBO_BOOST_COMMAND = File.expand_path('../misc/turbo_boost', __dir__) + MAXIMIZE_FREQUENCY_COMMAND = File.expand_path('../misc/maximize_frequency', __dir__) + class << self # Configure CPU for benchmarking: disable frequency scaling and verify settings def configure_for_benchmarking(turbo:) @@ -42,11 +47,14 @@ def frequency_maximized? end def disable_turbo_boost - # Override in subclasses + # sudo requires the flag '-S' in order to take input from stdin + BenchmarkRunner.check_call("sudo -S #{TURBO_BOOST_COMMAND} off") + at_exit { sudo_prefer_quiet("#{TURBO_BOOST_COMMAND} on") } end def maximize_frequency - # Override in subclasses + # Disabling Turbo Boost reduces the CPU frequency, so this should be run after that. + BenchmarkRunner.check_call("sudo -S #{MAXIMIZE_FREQUENCY_COMMAND}") end def check_pstate(turbo:) @@ -75,17 +83,6 @@ class IntelCPUConfig < CPUConfig private - def disable_turbo_boost - # sudo requires the flag '-S' in order to take input from stdin - BenchmarkRunner.check_call("sudo -S sh -c 'echo #{TURBO_DISABLED_VALUE} > #{NO_TURBO_PATH}'") - at_exit { sudo_prefer_quiet("sh -c 'echo 0 > #{NO_TURBO_PATH}'") } - end - - def maximize_frequency - # Disabling Turbo Boost reduces the CPU frequency, so this should be run after that. - BenchmarkRunner.check_call("sudo -S sh -c 'echo #{FREQUENCY_MAXIMIZED_VALUE} > #{MIN_PERF_PCT_PATH}'") - end - def turbo_disabled? @turbo_disabled ||= File.exist?(NO_TURBO_PATH) && File.read(NO_TURBO_PATH).strip == TURBO_DISABLED_VALUE @@ -99,13 +96,13 @@ def frequency_maximized? def check_pstate(turbo:) unless turbo || turbo_disabled? puts("You forgot to disable turbo:") - puts(" sudo sh -c 'echo #{TURBO_DISABLED_VALUE} > #{NO_TURBO_PATH}'") + puts(" sudo #{TURBO_BOOST_COMMAND} off") exit(-1) end unless frequency_maximized? puts("You forgot to set the min perf percentage to 100:") - puts(" sudo sh -c 'echo #{FREQUENCY_MAXIMIZED_VALUE} > #{MIN_PERF_PCT_PATH}'") + puts(" sudo #{MAXIMIZE_FREQUENCY_COMMAND}") exit(-1) end end @@ -117,21 +114,10 @@ class AMDCPUConfig < CPUConfig BOOST_PATH = "#{CPUFREQ_DIR}/boost" SCALING_GOVERNOR_GLOB = '/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor' TURBO_DISABLED_VALUE = '0' - TURBO_ENABLED_VALUE = '1' PERFORMANCE_GOVERNOR = 'performance' private - def disable_turbo_boost - # sudo requires the flag '-S' in order to take input from stdin - BenchmarkRunner.check_call("sudo -S sh -c 'echo #{TURBO_DISABLED_VALUE} > #{BOOST_PATH}'") - at_exit { sudo_prefer_quiet("sh -c 'echo #{TURBO_ENABLED_VALUE} > #{BOOST_PATH}'") } - end - - def maximize_frequency - BenchmarkRunner.check_call("sudo -S cpupower frequency-set -g performance") - end - def turbo_disabled? @turbo_disabled ||= File.exist?(BOOST_PATH) && File.read(BOOST_PATH).strip == TURBO_DISABLED_VALUE @@ -146,13 +132,13 @@ def frequency_maximized? def check_pstate(turbo:) unless turbo || turbo_disabled? puts("You forgot to disable boost:") - puts(" sudo sh -c 'echo #{TURBO_DISABLED_VALUE} > #{BOOST_PATH}'") + puts(" sudo #{TURBO_BOOST_COMMAND} off") exit(-1) end unless frequency_maximized? puts("You forgot to set the performance governor:") - puts(" sudo cpupower frequency-set -g #{PERFORMANCE_GOVERNOR}") + puts(" sudo #{MAXIMIZE_FREQUENCY_COMMAND}") exit(-1) end end diff --git a/misc/maximize_frequency b/misc/maximize_frequency new file mode 100755 index 00000000..6e3062b3 --- /dev/null +++ b/misc/maximize_frequency @@ -0,0 +1,18 @@ +#!/bin/sh +# Maximize the minimum CPU frequency for stable benchmark results: +# min_perf_pct=100 on Intel pstate, or the performance governor on AMD cpufreq. +# run_benchmarks.rb runs this with sudo. Allowing this command in sudoers +# with NOPASSWD skips the password prompt. +set -eu + +INTEL_MIN_PERF_PCT=/sys/devices/system/cpu/intel_pstate/min_perf_pct +AMD_BOOST=/sys/devices/system/cpu/cpufreq/boost + +if [ -e "$INTEL_MIN_PERF_PCT" ]; then + echo 100 > "$INTEL_MIN_PERF_PCT" +elif [ -e "$AMD_BOOST" ]; then + cpupower frequency-set -g performance +else + echo "$0: no CPU frequency control found under /sys" >&2 + exit 1 +fi diff --git a/misc/turbo_boost b/misc/turbo_boost new file mode 100755 index 00000000..bda3395a --- /dev/null +++ b/misc/turbo_boost @@ -0,0 +1,23 @@ +#!/bin/sh +# Enable or disable CPU turbo boost (Intel pstate or AMD cpufreq). +# run_benchmarks.rb runs this with sudo. Allowing this command in sudoers +# with NOPASSWD skips the password prompt. +set -eu + +INTEL_NO_TURBO=/sys/devices/system/cpu/intel_pstate/no_turbo +AMD_BOOST=/sys/devices/system/cpu/cpufreq/boost + +case "${1:-}" in + on) intel_value=0 amd_value=1 ;; + off) intel_value=1 amd_value=0 ;; + *) echo "Usage: $0 {on|off}" >&2; exit 1 ;; +esac + +if [ -e "$INTEL_NO_TURBO" ]; then + echo "$intel_value" > "$INTEL_NO_TURBO" +elif [ -e "$AMD_BOOST" ]; then + echo "$amd_value" > "$AMD_BOOST" +else + echo "$0: no turbo boost control found under /sys" >&2 + exit 1 +fi diff --git a/test/cpu_config_test.rb b/test/cpu_config_test.rb index c49d8861..6999d954 100644 --- a/test/cpu_config_test.rb +++ b/test/cpu_config_test.rb @@ -163,7 +163,7 @@ end assert_equal 1, cleanup_commands.length, "at_exit block should call check_call once" - assert_equal "sudo -n sh -c 'echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo'", cleanup_commands[0][:cmd] + assert_equal "sudo -n #{CPUConfig::TURBO_BOOST_COMMAND} on", cleanup_commands[0][:cmd] assert_equal({ quiet: true, raise_error: false }, cleanup_commands[0][:opts]) end @@ -192,7 +192,7 @@ assert_equal(-1, exit_code) assert_includes output[0], "You forgot to disable turbo" - assert_includes output[0], "sudo sh -c 'echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo'" + assert_includes output[0], "sudo #{CPUConfig::TURBO_BOOST_COMMAND} off" end it 'exits when Intel min perf is not 100%' do @@ -220,7 +220,7 @@ assert_equal(-1, exit_code) assert_includes output[0], "You forgot to set the min perf percentage to 100" - assert_includes output[0], "sudo sh -c 'echo 100 > /sys/devices/system/cpu/intel_pstate/min_perf_pct'" + assert_includes output[0], "sudo #{CPUConfig::MAXIMIZE_FREQUENCY_COMMAND}" end end end @@ -306,7 +306,7 @@ end assert_equal 1, cleanup_commands.length, "at_exit block should call check_call once" - assert_equal "sudo -n sh -c 'echo 1 > /sys/devices/system/cpu/cpufreq/boost'", cleanup_commands[0][:cmd] + assert_equal "sudo -n #{CPUConfig::TURBO_BOOST_COMMAND} on", cleanup_commands[0][:cmd] assert_equal({ quiet: true, raise_error: false }, cleanup_commands[0][:opts]) end @@ -337,7 +337,7 @@ assert_equal(-1, exit_code) assert_includes output[0], "You forgot to disable boost" - assert_includes output[0], "sudo sh -c 'echo 0 > /sys/devices/system/cpu/cpufreq/boost'" + assert_includes output[0], "sudo #{CPUConfig::TURBO_BOOST_COMMAND} off" end it 'exits when AMD performance governor is not set' do @@ -367,7 +367,7 @@ assert_equal(-1, exit_code) assert_includes output[0], "You forgot to set the performance governor" - assert_includes output[0], "sudo cpupower frequency-set -g performance" + assert_includes output[0], "sudo #{CPUConfig::MAXIMIZE_FREQUENCY_COMMAND}" end end end