I'm working on a patch to modify the testsuite to obey the --load-average value if one is passed to make. It seems to work pretty well, except for libstdc++ which doesn't load gcc/libs/gcc-defs.exp since it defines it's own ${tool}_functions. I haven't dug too deeply into libstdc++'s testsuite yet, but how does it manage parallelization if it isn't using the routines in gcc-defs.exp? I'm thinking I will need a separate load-limit.exp file or some such.
This feature would be very helpful since you cannot interrupt a test run and restart from where you left off. Also, if you suspend the job, then you will get timeouts. So it would be helpful to have a way to have it yield when you need to do something else on your machine, or if you're using a shared test machine and you want to use all available CPU, but not crowd out other users. Due to not having actual interprocess communication, the check_cpu_load procedure uses an algo that gives lower numbered jobs slightly more priority than higher numbered jobs. When the load average is exceeded, the job sleeps an amount of time based upon the "priority" (lower numbered jobs sleep less) and a random number - this helps prevent feast or famine cycles where all jobs stop when the load is too high and then all jobs start again and saturate the CPUs, bouncing back and forth. gcc/testsuite/ChangeLog * gcc/Makefile.in (check-parallel-%): Export job number to environment. * gcc/testsuite/lib/gcc-defs.exp (num_jobs, load_max, getloadavg_exe): New global variables. (check_cpu_load): New proc to check speed limit. (gcc_parallel_test_run_p): Modify to call check_cpu_load before acquiring a new lock file. Thanks, Daniel
diff --git a/gcc/Makefile.in b/gcc/Makefile.in index efca9169671..f26ff3840b8 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -4039,6 +4039,7 @@ check-parallel-% : site.exp @test -d $(TESTSUITEDIR)/$(check_p_subdir) || mkdir $(TESTSUITEDIR)/$(check_p_subdir) -$(if $(check_p_subno),@)(rootme=`${PWD_COMMAND}`; export rootme; \ srcdir=`cd ${srcdir}; ${PWD_COMMAND}` ; export srcdir ; \ + GCC_RUNTEST_JOBNO=$(check_p_subno) ; export GCC_RUNTEST_JOBNO ; \ if [ -n "$(check_p_subno)" ] \ && [ -n "$$GCC_RUNTEST_PARALLELIZE_DIR" ] \ && [ -f $(TESTSUITEDIR)/$(check_p_tool)-parallel/finished ]; then \ diff --git a/gcc/testsuite/lib/gcc-defs.exp b/gcc/testsuite/lib/gcc-defs.exp index d5fde7ce5e3..e18025018d2 100644 --- a/gcc/testsuite/lib/gcc-defs.exp +++ b/gcc/testsuite/lib/gcc-defs.exp @@ -20,6 +20,8 @@ load_lib wrapper.exp load_lib target-utils.exp +global num_jobs load_max getloadavg_exe + # # ${tool}_check_compile -- Reports and returns pass/fail for a compilation # @@ -148,6 +150,107 @@ proc ${tool}_exit { } { } } +if { [info exists env(MAKEFLAGS)] } then { + # Attempt to get the --load-average from make + set load_max [regsub "^(?:|.*? -)l(\\d+(\\.\\d+)?).*?$" \ + $env(MAKEFLAGS) "\\1" ] + if [regexp "^\\d+(\\.\\d+)?$" $load_max match] then { + verbose "load_max = $load_max" 0 + } else { + unset load_max + } + + # Attempt to get the number of make -j<jobs> + set num_jobs [regsub "^(?:|.*? -)?j(\\d+).*?$" $env(MAKEFLAGS) "\\1" ] + if [regexp "^\\d+$" $num_jobs match] then { + verbose "num_jobs = $num_jobs" 0 + } else { + set num_jobs 1 + } +} + +# If a --load-average was specified, try to build getloadavg_exe. +if [info exists load_max] then { + set src "$tmpdir/getloadavg.[pid].c" + set getloadavg_exe "$tmpdir/getloadavg.exe" + set f [open $src "w"] + puts $f { + #include <stdlib.h> + #include <stdio.h> + + int main (int argc, char *argv[]) + { + double load; + if (getloadavg (&load, 1) == -1) + return -1; + + printf ("%f", load); + return 0; + } + } + close $f + + # Temporarily switch to the environment for the host compiler. + #restore_ld_library_path_env_vars + set cc "$HOSTCC $HOSTCFLAGS $TEST_ALWAYS_FLAGS -O2" + set status [remote_exec host "$cc -O2 -o $getloadavg_exe $src"] + #set_ld_library_path_env_vars + file delete $src + if [lindex $status 0] { + verbose "Failed to build $src, will not attempt to enforce CPU load limit." 0 + unset getloadavg_exe + } + verbose "$getloadavg_exe and appears to be working..." 0 +} + +# +# check_cpu_load -- If make was passed --load-average and and libc supports +# getloadavg, then check CPU load and regulate job execution. +# + +proc check_cpu_load {} { + global num_jobs load_max getloadavg_exe load_last_checked + + # Only recheck CPU load every 4 seconds. + set now [clock seconds] + if {[info exists load_last_checked] && \ + $now < [expr {$load_last_checked + 4}]} then { + return; + } + set load_last_checked $now + set jobno [getenv GCC_RUNTEST_JOBNO] + + # First job always runs + if {$jobno == 0 || $jobno == ""} then { + return + } + + set sleep_time [expr {rand() * 8 * $jobno / $num_jobs + 1}] + while true { + set status [remote_exec host "$getloadavg_exe"] + if [lindex $status 0] { + warning "$getloadavg_exe failed, disabling CPU load limit." + unset getloadavg_exe + unset check_cpu_load + proc check_cpu_load {} { + } + return + } + set load [lindex $status 1] + + # Allow lower job numbers slightly more tollerance of over loading. + # This avoids a feast and famine cycle of bouncing between all jobs + # running and all jobs sleeping. + set tollerance [expr {0.75 - 0.75 * $jobno / $num_jobs}] + + if { $load <= [expr {$load_max + $tollerance}] } then { + return + } + + sleep $sleep_time + } +} + # # runtest_file_p -- Provide a definition for older dejagnu releases # and assume the old syntax: foo1.exp bar1.c foo2.exp bar2.c. @@ -205,6 +308,7 @@ if { [info exists env(GCC_RUNTEST_PARALLELIZE_DIR)] \ set path $gcc_runtest_parallelize_dir/$gcc_runtest_parallelize_counter + check_cpu_load if {![catch {open $path {RDWR CREAT EXCL} 0600} fd]} { close $fd set gcc_runtest_parallelize_last 1