This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFC [testsuite] Obey --load-average


On 08/03/2017 05:07 PM, Mike Stump wrote:
> On Aug 2, 2017, at 10:34 PM, Daniel Santos <daniel.santos@pobox.com> wrote:
>> I'm working on a patch to modify the testsuite to obey the
>> --load-average value if one is passed to make.
> The code seems like a reasonable approach.  Love to see numbers and test scenarios so that others can tell if you've covered their use case.  -j 100 is different from -j 4.

Well I've re-tweaked it a little to ensure more even performance with
higher numbers of CPUs, but the only way that I can think of to assure
this given the scheme is to increase the maximum *tolerance* to
something like 1 per 10 CPUs:

    set tolerance [expr {(0.0 + $num_jobs - $jobno - 1) / 10}]

Short of adding some type of IPC mechanism, I don't see another way to
do it,  but this doesn't mean that with -j 100 -l 100 we will run a load
average of 110 because only the lowest numbered jobs will use a max load
average of 110 to decide rather or not to run.  Job zero never sleeps,
job #1 will sleep until the load average drops to 109.8 or lower, job #2
until 109.7... job #98 until 100.1, and job #99 until 100.  So if one is
going for a fastest run (with 100 CPUs), something like make -j 100 -l
105 may be a good target, depending upon the statistics for when the
cost of task switching becomes greater than the loss of CPU cycles that
are unutilized due to I/O or whatever.

> People can help chip in numbers, if they have senarios that are less represented.

Yes, that is my hope.

> I don't usually share or use -l, so I don't think I can help test it.  I do wonder if it might be better to use a higher -j (I use -j somewhere between 24 and 50) and use a load limit, even in my situation.

Well that is the theory.  It would be even better if there was a way to
ask the kernel's task accounting for a load average of the last x
seconds (maybe there is and I'm just not aware of it).  Of course, when
you have 24 cores and your load average is 50, then a massive number of
cpu cycles are lost to task switching, data cache (and possibly also
instruction cache) misses, tlb misses, etc.  (Hmm, this actually makes
me wonder if a cgroup can be configured to have a much longer time slice
before being preempted by other processes in its same cgroup.)


> The only concern would be that of portability.  Seems reasonable to let it in and fix up any issues found after the fact.  I like how you ensure low impact when -l isn't used.

Yes and I've already modified the patch to disable on cygwin and mingw
because they implement getloadavg by always returning zero.  I'm sure
you've probably seen the size of Windows' PEB and TEB that it must
initialize for *each* new process -- so the fewer forks or execs on that
OS the better.  I haven't tried to test on Windows in 4-5 months, but I
have this silly hope that a recently fixed race issue will make it
better.  Last time I was only able to run a single test thread at a time. 

Other than that, I hope that it will simply be disabled where it isn't
supported.  I suspect that getloadavg will be more portable than posix
semaphores, which I would eventually like to experiment with as an
alternative to both this patch and the 10 jobs per lock file mechanism.

> Minor nit, tollerance -> tolerance.

Thank you.  Until spell checker, I used to write "paralell" and I blame
dyslexia.  I don't have dyslexia, but it's still dyslexia's fault!

I'm attaching my latest patch, which happens to be wildly broken.  I've
completed a full test run and I'm missing .sum files for libatomic,
libgom, and libitm in addition to a vast number of failed tests, but I
think I'm getting closer!

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..bdfd2f0ad65 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
 
+load_lib parallelize.exp
+
 #
 # ${tool}_check_compile -- Reports and returns pass/fail for a compilation
 #
@@ -148,99 +150,6 @@ proc ${tool}_exit { } {
     }
 }
 
-#
-# runtest_file_p -- Provide a definition for older dejagnu releases
-# 		    and assume the old syntax: foo1.exp bar1.c foo2.exp bar2.c.
-# 		    (delete after next dejagnu release).
-#
-
-if { [info procs runtest_file_p] == "" } then {
-    proc runtest_file_p { runtests testcase } {
-	if { $runtests != "" && [regexp "\[.\]\[cC\]" $runtests] } then {
-	    if { [lsearch $runtests [file tail $testcase]] >= 0 } then {
-		return 1
-	    } else {
-		return 0
-	    }
-	}
-	return 1
-    }
-}
-
-if { [info exists env(GCC_RUNTEST_PARALLELIZE_DIR)] \
-     && [info procs runtest_file_p] != [list] \
-     && [info procs gcc_parallelize_saved_runtest_file_p] == [list] } then {
-    global gcc_runtest_parallelize_counter
-    global gcc_runtest_parallelize_counter_minor
-    global gcc_runtest_parallelize_enable
-    global gcc_runtest_parallelize_dir
-    global gcc_runtest_parallelize_last
-
-    set gcc_runtest_parallelize_counter 0
-    set gcc_runtest_parallelize_counter_minor 0
-    set gcc_runtest_parallelize_enable 1
-    set gcc_runtest_parallelize_dir [getenv GCC_RUNTEST_PARALLELIZE_DIR]
-    set gcc_runtest_parallelize_last 0
-
-    proc gcc_parallel_test_run_p { testcase } {
-	global gcc_runtest_parallelize_counter
-	global gcc_runtest_parallelize_counter_minor
-	global gcc_runtest_parallelize_enable
-	global gcc_runtest_parallelize_dir
-	global gcc_runtest_parallelize_last
-
-	if { $gcc_runtest_parallelize_enable == 0 } {
-	    return 1
-	}
-
-	# Only test the filesystem every 10th iteration
-	incr gcc_runtest_parallelize_counter_minor
-	if { $gcc_runtest_parallelize_counter_minor == 10 } {
-	    set gcc_runtest_parallelize_counter_minor 0
-	}
-	if { $gcc_runtest_parallelize_counter_minor != 1 } {
-	    #verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter $gcc_runtest_parallelize_last"
-	    return $gcc_runtest_parallelize_last
-	}
-
-	set path $gcc_runtest_parallelize_dir/$gcc_runtest_parallelize_counter
-
-	if {![catch {open $path {RDWR CREAT EXCL} 0600} fd]} {
-	    close $fd
-	    set gcc_runtest_parallelize_last 1
-	    #verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter 1"
-	    incr gcc_runtest_parallelize_counter
-	    return 1
-	}
-	set gcc_runtest_parallelize_last 0
-	#verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter 0"
-	incr gcc_runtest_parallelize_counter
-	return 0
-    }
-
-    proc gcc_parallel_test_enable { val } {
-	global gcc_runtest_parallelize_enable
-	set gcc_runtest_parallelize_enable $val
-    }
-
-    rename runtest_file_p gcc_parallelize_saved_runtest_file_p
-    proc runtest_file_p { runtests testcase } {
-	if ![gcc_parallelize_saved_runtest_file_p $runtests $testcase] {
-	    return 0
-	}
-	return [gcc_parallel_test_run_p $testcase]
-    }
-
-} else {
-
-    proc gcc_parallel_test_run_p { testcase } {
-	return 1
-    }
-
-    proc gcc_parallel_test_enable { val } {
-    }
-
-}
 
 # Like dg-options, but adds to the default options rather than replacing them.
 
diff --git a/gcc/testsuite/lib/parallelize.exp b/gcc/testsuite/lib/parallelize.exp
new file mode 100644
index 00000000000..e0e8f52f365
--- /dev/null
+++ b/gcc/testsuite/lib/parallelize.exp
@@ -0,0 +1,241 @@
+# Copyright (C) 2017 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+load_lib site.exp
+global num_jobs load_max getloadavg_exe tmpdir objdir HOSTCC HOSTCCFLAGS TEST_ALWAYS_FLAGS
+
+# Hack-aroundS for libstdc++-v3
+if ![info exists tmpdir] then {
+    set tmpdir "$objdir"
+}
+
+if ![info exists TEST_ALWAYS_FLAGS] then {
+    # FIXME: Need sysroot here?
+    set TEST_ALWAYS_FLAGS "-fno-diagnostics-show-caret -fdiagnostics-color=never"
+}
+
+
+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
+    }
+}
+
+# No getloadavg support on Windows (it always returns 0.0)
+if { [istarget *-*-cygwin*] || [istarget *-*-mingw*] } {
+    unset load_max
+}
+
+# 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
+    }
+}
+
+#
+# check_cpu_load -- If make was passed --load-average and and libc supports
+#		    getloadavg, then check CPU load and regulate job execution.
+#
+
+if [info exists getloadavg_exe] {
+    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 [regsub "\[a-z\]*(\\d)" [getenv GCC_RUNTEST_JOBNO] "\\1"]
+
+	# First job always runs
+	if {$jobno == 0 || $jobno == ""} then {
+	    return
+	}
+
+	#set sleep_time [expr {rand() * 4 * $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 tolerance of over loading.
+	    # This avoids a feast and famine cycle of bouncing between all jobs
+	    # running and all jobs sleeping.
+	    set tolerance [expr {(0.0 + $num_jobs - $jobno - 1) / 10}]
+
+	    if { $load <= [expr {$load_max + $tolerance}] } then {
+		return
+	    }
+
+	    #sleep $sleep_time
+	    sleep 1
+	}
+    }
+} else {
+    proc check_cpu_load {} {
+    }
+}
+
+#
+# runtest_file_p -- Provide a definition for older dejagnu releases
+# 		    and assume the old syntax: foo1.exp bar1.c foo2.exp bar2.c.
+# 		    (delete after next dejagnu release).
+#
+
+if { [info procs runtest_file_p] == "" } then {
+    proc runtest_file_p { runtests testcase } {
+	if { $runtests != "" && [regexp "\[.\]\[cC\]" $runtests] } then {
+	    if { [lsearch $runtests [file tail $testcase]] >= 0 } then {
+		return 1
+	    } else {
+		return 0
+	    }
+	}
+	return 1
+    }
+}
+
+if { [info exists env(GCC_RUNTEST_PARALLELIZE_DIR)] \
+     && [info procs runtest_file_p] != [list] \
+     && [info procs gcc_parallelize_saved_runtest_file_p] == [list] } then {
+    global gcc_runtest_parallelize_counter
+    global gcc_runtest_parallelize_counter_minor
+    global gcc_runtest_parallelize_enable
+    global gcc_runtest_parallelize_dir
+    global gcc_runtest_parallelize_last
+    global gcc_runtest_parallelize_jobs_per_lock
+
+    set gcc_runtest_parallelize_counter 0
+    set gcc_runtest_parallelize_counter_minor 0
+    set gcc_runtest_parallelize_enable 1
+    set gcc_runtest_parallelize_dir [getenv GCC_RUNTEST_PARALLELIZE_DIR]
+    set gcc_runtest_parallelize_last 0
+    
+    if ![info exists gcc_runtest_parallelize_jobs_per_lock] then {
+	set gcc_runtest_parallelize_jobs_per_lock 10
+    }
+
+    proc gcc_parallel_test_run_p { testcase } {
+	global gcc_runtest_parallelize_counter
+	global gcc_runtest_parallelize_counter_minor
+	global gcc_runtest_parallelize_enable
+	global gcc_runtest_parallelize_dir
+	global gcc_runtest_parallelize_last
+	global gcc_runtest_parallelize_jobs_per_lock
+
+	if { $gcc_runtest_parallelize_enable == 0 } {
+	    return 1
+	}
+
+	# Only test the filesystem every 10th iteration
+	incr gcc_runtest_parallelize_counter_minor
+	if { $gcc_runtest_parallelize_counter_minor == $gcc_runtest_parallelize_jobs_per_lock } {
+	    set gcc_runtest_parallelize_counter_minor 0
+	}
+	if { $gcc_runtest_parallelize_counter_minor != 1 } {
+	    #verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter $gcc_runtest_parallelize_last"
+	    return $gcc_runtest_parallelize_last
+	}
+
+	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
+	    #verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter 1"
+	    incr gcc_runtest_parallelize_counter
+	    return 1
+	}
+	set gcc_runtest_parallelize_last 0
+	#verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter 0"
+	incr gcc_runtest_parallelize_counter
+	return 0
+    }
+
+    proc gcc_parallel_test_enable { val } {
+	global gcc_runtest_parallelize_enable
+	set gcc_runtest_parallelize_enable $val
+    }
+
+    rename runtest_file_p gcc_parallelize_saved_runtest_file_p
+    proc runtest_file_p { runtests testcase } {
+	if ![gcc_parallelize_saved_runtest_file_p $runtests $testcase] {
+	    return 0
+	}
+	return [gcc_parallel_test_run_p $testcase]
+    }
+
+} else {
+
+    proc gcc_parallel_test_run_p { testcase } {
+	return 1
+    }
+
+    proc gcc_parallel_test_enable { val } {
+    }
+
+}
diff --git a/libstdc++-v3/testsuite/Makefile.in b/libstdc++-v3/testsuite/Makefile.in
index da51c0fc3c7..7ae6ce695ca 100644
--- a/libstdc++-v3/testsuite/Makefile.in
+++ b/libstdc++-v3/testsuite/Makefile.in
@@ -591,6 +591,8 @@ new-abi-baseline:
 	sed -e "s|^set srcdir .*$$|set srcdir $$srcdir|" \
 	    -e "s|^set objdir .*$$|set objdir $$objdir|" \
 	    site.exp > $*/site.exp.tmp
+	@echo "set HOSTCC \"$(CC)\"" >> $*/site.exp.tmp
+	@echo "set HOSTCFLAGS \"$(CFLAGS)\"" >> $*/site.exp.tmp
 	@-rm -f $*/site.bak
 	@test ! -f $*/site.exp || mv $*/site.exp $*/site.bak
 	@mv $*/site.exp.tmp $*/site.exp
@@ -623,6 +625,7 @@ check-DEJAGNU $(check_DEJAGNU_normal_targets): check-DEJAGNU%: site.exp
 	if [ -z "$$runtest" ]; then runtest=runtest; fi; \
 	tool=libstdc++; \
 	if [ -n "$*" ]; then \
+	  GCC_RUNTEST_JOBNO="$*" ; export GCC_RUNTEST_JOBNO ; \
 	  if [ -f normal-parallel/finished ]; then rm -rf "$*"; exit 0; fi; \
 	  GCC_RUNTEST_PARALLELIZE_DIR=`${PWD_COMMAND}`/normal-parallel; \
 	  export GCC_RUNTEST_PARALLELIZE_DIR; \
diff --git a/libstdc++-v3/testsuite/lib/libstdc++.exp b/libstdc++-v3/testsuite/lib/libstdc++.exp
index b081d8a5b90..d9461eb7ef7 100644
--- a/libstdc++-v3/testsuite/lib/libstdc++.exp
+++ b/libstdc++-v3/testsuite/lib/libstdc++.exp
@@ -59,6 +59,10 @@ load_gcc_lib timeout-dg.exp
 load_gcc_lib wrapper.exp
 load_gcc_lib target-utils.exp
 
+global gcc_runtest_parallelize_jobs_per_lock
+set gcc_runtest_parallelize_jobs_per_lock 2
+load_gcc_lib parallelize.exp
+
 # Useful for debugging.  Pass the name of a variable and the verbosity
 # threshold (number of -v's on the command line).
 proc v3track { var n } {
@@ -2012,78 +2016,3 @@ proc check_effective_target_cxx11-abi { } {
 }
 
 set additional_prunes ""
-
-if { [info exists env(GCC_RUNTEST_PARALLELIZE_DIR)] \
-     && [info procs runtest_file_p] != [list] \
-     && [info procs gcc_parallelize_saved_runtest_file_p] == [list] } then {
-    global gcc_runtest_parallelize_counter
-    global gcc_runtest_parallelize_counter_minor
-    global gcc_runtest_parallelize_enable
-    global gcc_runtest_parallelize_dir
-    global gcc_runtest_parallelize_last
-
-    set gcc_runtest_parallelize_counter 0
-    set gcc_runtest_parallelize_counter_minor 0
-    set gcc_runtest_parallelize_enable 1
-    set gcc_runtest_parallelize_dir [getenv GCC_RUNTEST_PARALLELIZE_DIR]
-    set gcc_runtest_parallelize_last 0
-
-    proc gcc_parallel_test_run_p { testcase } {
-	global gcc_runtest_parallelize_counter
-	global gcc_runtest_parallelize_counter_minor
-	global gcc_runtest_parallelize_enable
-	global gcc_runtest_parallelize_dir
-	global gcc_runtest_parallelize_last
-
-	if { $gcc_runtest_parallelize_enable == 0 } {
-	    return 1
-	}
-
-	# Only test the filesystem every 10th iteration
-	incr gcc_runtest_parallelize_counter_minor
-	if { $gcc_runtest_parallelize_counter_minor == 10 } {
-	    set gcc_runtest_parallelize_counter_minor 0
-	}
-	if { $gcc_runtest_parallelize_counter_minor != 1 } {
-	    #verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter $gcc_runtest_parallelize_last"
-	    return $gcc_runtest_parallelize_last
-	}
-
-	set path $gcc_runtest_parallelize_dir/$gcc_runtest_parallelize_counter
-
-	if {![catch {open $path {RDWR CREAT EXCL} 0600} fd]} {
-	    close $fd
-	    set gcc_runtest_parallelize_last 1
-	    #verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter 1"
-	    incr gcc_runtest_parallelize_counter
-	    return 1
-	}
-	set gcc_runtest_parallelize_last 0
-	#verbose -log "gcc_parallel_test_run_p $testcase $gcc_runtest_parallelize_counter 0"
-	incr gcc_runtest_parallelize_counter
-	return 0
-    }
-
-    proc gcc_parallel_test_enable { val } {
-	global gcc_runtest_parallelize_enable
-	set gcc_runtest_parallelize_enable $val
-    }
-
-    rename runtest_file_p gcc_parallelize_saved_runtest_file_p
-    proc runtest_file_p { runtests testcase } {
-	if ![gcc_parallelize_saved_runtest_file_p $runtests $testcase] {
-	    return 0
-	}
-	return [gcc_parallel_test_run_p $testcase]
-    }
-
-} else {
-
-    proc gcc_parallel_test_run_p { testcase } {
-	return 1
-    }
-
-    proc gcc_parallel_test_enable { val } {
-    }
-
-}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]