Repository navigation
Faster test runs: a 2 s wait at every shutdown, and two tests that waited out a timeout - #4770
Conversation
Since the IO handling moved into task, milltask owns the iocontrol.0 component but never called hal_exit() on it, so the component stayed registered after milltask was gone. The Task destructor now releases it and the shutdown deletes the Task object.
Cleanup waits for every component but halcmd to go before it stops realtime. A thread is listed as a __<name> pseudo component, which unload all leaves in place, so together with the stale iocontrol.0 the wait always ran its full 2 seconds. Each linuxcnc -r in the tests paid it, and mdi-while-queuebuster-waitflag starts linuxcnc 20 times.
7e0c2c4 to
ded8906
Compare
|
Nice, down to 48s at -j32.
Total 46.4 / Run at once 45.8s This could be optimized by splitting them but it does not help that much. If I run just 1/5 of these tests, I am down to 41s. The order helps a bit, due to the tests are sorted alphabetically, this does the trick: -> still 48s But now when I run 1/5 of the tests in For my machine, -j32 is the sweet spot. More jobs make it slower, less to. From my side, I would call it good enough with this PR as it is, not worth investing hours to squeeze a few second more out of it. However, from 13min to 1m30 and now 48s where quite nice improvements. |
Both called wait_complete() right after AUTO_RUN. For a program run that waits for the end of the program, which these never reach before the abort, so every case sat out the 5 second timeout. Wait for the point to abort at instead: the Y position in abort/g64, which then also waits for halsampler to log it, and in workplane-retain the slow move with the read ahead stopped at the queue buster or M2, in place of the fixed sleeps. The other command waits get 30 seconds, so a loaded machine is not cut off at the 5 second default.
The test ran five mode settings back to back in one linuxcnc session, about 45 seconds, which made it the floor of a parallel run. Each setting with its redo is now a test of its own in a directory named after it; the one-line test.ini includes the shared settings, now in g64.inc, and test-ui.py picks the case by the directory. The checks are the same. Measured by hdiethelm in LinuxCNC#4770: with this and the slow tests started first, -j 32 goes from 48 to 32 seconds.
ded8906 to
a8fe217
Compare
Sorry, that was sloppy of me: I swapped a 5 s timeout for fixed sleeps and did not check the tests under load before pushing. Both sleeps were stand-ins. The 0.2 s gave the read ahead time to get past the slow move; the abort now waits for
Thanks for measuring; split in the last commit: abort/g64 is now five tests, one per mode setting, about 10 s each. A one-line test.ini per case includes the shared settings in g64.inc. With the slow tests started first that should give your 32 s at -j 32; I can send the ordering after #4765 is in. Checked with 6 copies of each test at once plus 4 busy loops: all pass, twice, 42 s (master 73 s under the same load). |
|
Just ran all tests 5 times and took an average. Some cherry picked parts of this PR were applied (linuxcnc script and component cleanup). There are only 26/350 tests that take 10 seconds or more. If we can get those that run 20+ seconds down (8/350) then we'd be running much better. Note: abort/g64 is already stripped down in this PR but not tested. The worst offenders are: Getting all tests to run less than 20 seconds would help a lot. |
|
Thanks for the additional improvements. Now I measure ~37s. A quick hack to measure the runtime of each test: diff --git a/scripts/runtests.in b/scripts/runtests.in
index e0eeb30246..fb70ed7336 100755
--- a/scripts/runtests.in
+++ b/scripts/runtests.in
@@ -346,6 +346,8 @@ run_tests () {
parallel -j "$PARALLEL" "$CMD" < "$TMPDIR/alltests_realpath" 4> "$TMPDIR/testlog"
RET_PARALLEL=$? #Will be nonzero if any command failed
+ cp "$TMPDIR/testlog" .
+
#Create sumary based on testlog
ALL=$(wc -l < "$TMPDIR/alltests")
SHMERR=$(grep -c SHMERR < "$TMPDIR/testlog")
@@ -527,20 +529,21 @@ elif [ $WORKER -eq 1 ]; then
echo ERROR: worker mode does not take -j argument
exit 1;
fi
+ SECONDS=0
run_one_test "$1"
#FD 4 is used in parallel to log test results, so stdout / stderr / fd 3 used above don't interfere
echo -n "$1" >&4
if [ "$SHMERR" -ne 0 ]; then
- echo " SHMERR" >&4
+ echo " SHMERR ${SECONDS}" >&4
fi
if [ "$FAIL" -ne 0 ]; then
- echo " FAILED" >&4
+ echo " FAILED ${SECONDS}" >&4
exit 1;
elif [ "$SKIP" -ne 0 ]; then
- echo " SKIPPED" >&4
+ echo " SKIPPED ${SECONDS}" >&4
exit 0;
else
- echo " SUCCESS" >&4
+ echo " SUCCESS ${SECONDS}" >&4
exit 0;
fi
elseResult, sorted with: |
|
And a quick hack that does the tests in descending runtime order: diff --git a/scripts/runtests.in b/scripts/runtests.in
index f0c7a44d44..bb77354d32 100755
--- a/scripts/runtests.in
+++ b/scripts/runtests.in
@@ -203,6 +203,7 @@ test_and_remove_shmem() {
}
run_one_test() {
+ SECONDS=0
if [ "$#" -ne 1 ]; then
echo "ERROR: run_one_test testfile"
return 1
@@ -287,6 +288,9 @@ run_one_test() {
rm -f "$testdir/stderr" "$testdir/result" \
"$testdir"/*.var "$testdir"/*.var.bak
fi
+ if [ $RUNTIME -ne 0 ]; then
+ echo $SECONDS > "$testdir/runtime"
+ fi
fi
if ! test_and_remove_shmem; then
@@ -307,10 +311,21 @@ run_tests () {
| sort > "$TMPDIR/alltests"
if [ "$PARALLEL" -gt 0 ]; then
- #convert relative to full path
+ #convert relative to full path and sort by runtime
while read -r testname; do
- realpath -s "$testname" >> "$TMPDIR/alltests_realpath"
- done < "$TMPDIR/alltests"
+ realpath=$(realpath -s "$testname")
+ runtime="$(dirname "$realpath")/runtime"
+ if [[ -f "$runtime" ]]; then
+ printf '%s\t%s\n' "$(cat "$runtime")" "$realpath"
+ else
+ printf '0\t%s\n' "$realpath"
+ fi
+ done < "$TMPDIR/alltests" > "$TMPDIR/alltests_realpath_tmp"
+
+ cat "$TMPDIR/alltests_realpath_tmp" | sort -nr -k1,1 | cut -f2- > "$TMPDIR/alltests_realpath"
+
+ cp "$TMPDIR/alltests_realpath_tmp" .
+ cp "$TMPDIR/alltests_realpath" .
#ro-bind: / (root) so if any files are written at an unknown location, an error is generated
#dev: add device files
@@ -472,6 +487,7 @@ STOP=0
PRINT=0
PARALLEL=0
PARALLEL_OUTDIR=""
+RUNTIME=1
WORKER=0
WORKER_OPT=()
while getopts cnvsphdj:o:w opt; doThis would probably be the nicer solution than checking for ini. The only downside is, that here are runtime files that need to be updated if a new test is created or a test is changed generating noise in the diffs. An easy solution would be to rename the tests to start with 37s -> 29s. |
|
The trick is that the running time distribution must be truly even random for long runs to cancel out the variability. We have several long running processes at the end of the alphabetically sorted queue, which will additionally skew the total running time. The second thing is that we do not want outliers. Any slow outlier will become part of a long tail. Therefore, we need to reduce each test to run some max time and if it is too slow, try to split the test so that parallel runs make it better (like abort/g64). The max time for a single test must always be less than the total running time of all the other tests in parallel. That is why the ui-smoke tests need speeding up. They are the next bottleneck. |
A viable solution is to record the runtime once. After this, you can run the tests in descending runtime order to reduce the influence of long running tests. Worst case is starting the longest running test at the end which is nearly the case right now,
Right now, Just checked, by removing the slowest 10 tests (with ordering) it goes from 29s to 24s, diminishing returns. |
Following up on the slow tests found in #4765.
Every linuxcnc shutdown waited 2 s for nothing. Cleanup in scripts/linuxcnc waits until halcmd is the last component before it stops realtime, but that never happened: a thread is listed as a
__<name>pseudo component, whichunload allleaves in place, and milltask never called hal_exit() on iocontrol.0 since the IO handling moved into task, so the component outlived the process. Both are fixed: the Task destructor releases iocontrol.0, and the wait ignores the pseudo components. Onelinuxcnc -rin a test goes from 3.3 s to 1.2 s here, and every real shutdown is 2 s faster.tests/abort/g64 and tests/workplane-retain called wait_complete() right after AUTO_RUN. For a program run that waits for the end of the program, which these never reach before the abort, so every case sat out the 5 s timeout. They now wait for the point they abort at.
abort/g64 also ran its five mode settings back to back in one session, which made it the floor of a parallel run. Each setting is now a test of its own directory; a one-line test.ini includes the shared settings in g64.inc.
Times on my machine, before => after:
Under load (6 copies of each test at once plus 4 busy loops) all pass, 42 s against 73 s on master. Full suite passes, 354/354 with
-j 4.