# BST-TIMEOUT: 300 # measured 30 s on a fresh 1 GiB guest (==BST-SUITE line); # 10x headroom because benchl is soft-float fp128 and three # 6-8 MB binaries are exec'd many times over . "$BST_LIB" # fftw 3.3.10 (meta-oe recipes-support/fftw), ported under BRINTOS-399. # # The ticket's acceptance gate is upstream's OWN suite -- tests/check.pl driving # bench, which meta-oe packages as a ptest -- and that suite passes here outright: # # ptest-runner -t 3000 fftw -> PASS: fftw / PASS: fftwf / PASS: fftwl # DURATION: 1395 TOTAL: 1 FAIL: 0 # # So this suite is NOT a restatement of the ptest. It is the cheap, deterministic # guard that runs at every gate; the full 23-minute ptest stays one environment # variable away at the bottom (BST_FFTW_PTEST=1). # # The difference that matters: check.pl is RANDOM (-r -c=30), so it cannot be a # fixture -- a red in it may be a new problem size rather than new behaviour. Every # problem below is a fixed string, validated against a native x86_64 control build # of the identical tarball, patch set and configure flags. P=/usr/lib/fftw/ptest # --- payload ------------------------------------------------------------------ # do_install runs three separate builds into one ${D}, so every way this recipe # can be wrong quietly looks like a missing or duplicated file. miss= for f in "$P/run-ptest" \ "$P/fftw/bench" "$P/fftw/check.pl" \ "$P/fftwf/benchf" "$P/fftwf/check.pl" \ "$P/fftwl/benchl" "$P/fftwl/check.pl"; do [ -x "$f" ] || miss="$miss $f" done if [ -z "$miss" ]; then bst_ok fftw/ptest-payload else bst_notok fftw/ptest-payload "not executable:$(_bst_1line "$miss")"; fi if [ ! -x "$P/fftw/bench" ]; then bst_skip fftw/present "no $P/fftw/bench on this image" exit 0 fi # --- the three precisions really are three precisions ------------------------- # The likeliest silent failure of this recipe: one library shipped three times. check_out fftw/precision-double "double" "$P/fftw/bench" --print-precision check_out fftw/precision-single "single" "$P/fftwf/benchf" --print-precision check_out fftw/precision-longdouble "long-double" "$P/fftwl/benchl" --print-precision # --- the verification battery ------------------------------------------------- # bench --verify exits 0 silently on success and 1 with "Found relative # error ..." on failure, so one invocation carrying the whole list is both the # cheapest form and an all-or-nothing assertion. The grammar is upstream's: # {i,o} in-place/out-of-place, {f,b} forward/backward, {c,r,k,h} complex / real # (r2c,c2r) / r2r / halfcomplex, // split arrays, x separates dimensions, v vector # rank, * stride. No problem string contains whitespace, which is what lets the # list expand unquoted below. C1D="ofc8 ifc8 obc8 ibc8 //ofc8 //ifc8 //obc8 //ibc8 ofc13 ibc13 obc31 ifc64" R1D="ofr8 ifr8 obr8 ibr8 ofr13 ibr13 obr32 ifr32" ND="ofc4x4 ibc4x4 ofr4x4 ibr4x4 ofc2x3x4 ibc2x3x4 ofc16x16 obc5x7" R2R="ok16 ik16 okd8 ok8e01 ok8e10 ok8o01 ok8o10 ok12e00 ok12e11 ok9h" VEC="obr8v2 ofc8v3 obc6*4 ofc7x3v2 ofc8v2 ibc9v3" verify_set() { # ... _vid=$1; _vprog=$2; shift 2 _vargs= for _vp in "$@"; do _vargs="$_vargs --verify $_vp"; done check "$_vid" "$_vprog" $_vargs } for arm in "double:$P/fftw/bench" "single:$P/fftwf/benchf" "longdouble:$P/fftwl/benchl"; do name=${arm%%:*}; prog=${arm#*:} verify_set "fftw/verify-1d-$name" "$prog" $C1D $R1D verify_set "fftw/verify-nd-$name" "$prog" $ND verify_set "fftw/verify-r2r-$name" "$prog" $R2R verify_set "fftw/verify-vector-$name" "$prog" $VEC done # --- accuracy is a number, so assert the number ------------------------------- # A transform that has quietly collapsed to a narrower precision still passes # --verify, because --verify's tolerance scales with the type it was compiled for. # bench -a prints six relative-error figures against an exact reference; # the bounds below sit ~3 orders of magnitude above each type's epsilon, so they # detect a collapse and not numerical noise. acc_worst() { # -> worst of the six figures on stdout "$1" -a "$2" 2>&1 | tr ' ' '\n' | awk 'NF' | sort -g | tail -1 } acc_max() { # _aw=$(acc_worst "$2" "$3") if [ -z "$_aw" ]; then bst_notok "$1" "no error figures from $2 -a $3"; return; fi if awk -v w="$_aw" -v b="$4" 'BEGIN{exit !(w+0 < b+0)}'; then bst_ok "$1" else bst_notok "$1" "worst relative error $_aw >= $4"; fi } acc_max fftw/accuracy-double "$P/fftw/bench" 32 1e-13 acc_max fftw/accuracy-single "$P/fftwf/benchf" 32 1e-4 acc_max fftw/accuracy-longdouble "$P/fftwl/benchl" 32 1e-30 # On wasm32 `long double` is IEEE quad (fp128), which is soft-float and the one # type the double and single arms never exercise. If it had been lowered to fp64 # the library would still build, still verify, and still call itself # "long-double" -- only the error magnitude gives it away (~1e-34 vs ~1e-16). lw=$(acc_worst "$P/fftwl/benchl" 32) if awk -v w="$lw" 'BEGIN{exit !(w+0 < 1e-25)}'; then bst_ok fftw/longdouble-is-wider else bst_notok fftw/longdouble-is-wider "long double error $lw is fp64-grade, not fp128"; fi # --- threads: ASSERTED AS A RED, DELIBERATELY NOT RUN ------------------------- # BRINTOS-WORKAROUND: hardwarejs#12 remove -- when the futex BITSET ops reach the # kernel this case runs and passes on its own; delete the guard then. # # libfftw3{,f,l}_threads.a ship, so the threaded planner is part of the port, and # it does not work here. bench -onthreads= goes through init_threads / # plan_with_nthreads / make_planner_thread_safe for ANY n including 1, and that # path has been measured to end in one of two ways on this arm: # # 1. glibc's futex_fatal_error -- "The futex facility returned an unexpected # error code.", rc=127. FUTEX_WAIT_BITSET returns ENOSYS in the Worker # fast-path before the kernel is consulted. -> hardwarejs#12 # 2. a leaked coroutine park slot that takes the WHOLE GUEST down: # "hwjs-rt VIOLATION[lane 2]: orphan at clear: parked slot not empty at # bracket entry", then "end-of-process residue -10 frames", then # RuntimeError: unreachable at bench.__wasm32_user_entry. -> hardwarejs#17 # # Which of the two happens is not stable: one run took three -onthreads=4 calls # and got three clean aborts; the next died on its FIRST -onthreads=1 call, before # a single BST line was emitted -- i.e. it cost the ==BST-END== sentinel and turned # the whole run into an INCOMPLETE. A case that sometimes kills the guest is not a # case, so this one is asserted rather than executed, exactly as 78-libevent.test # does for its two wedging families. # # The real thing is one environment variable away for whoever works #12/#17. if [ -n "${BST_FFTW_THREADS:-}" ]; then for arm in "double:$P/fftw/bench" "single:$P/fftwf/benchf" "longdouble:$P/fftwl/benchl"; do name=${arm%%:*}; prog=${arm#*:} check "fftw/threads-$name" "$prog" -onthreads=4 --verify 1024 --verify ofc4x4 --verify ofr64 done else bst_notok fftw/threads-blocked "NOT RUN: -onthreads aborts on futex BITSET ENOSYS (hardwarejs#12) or leaks a park slot and kills the guest (hardwarejs#17); BST_FFTW_THREADS=1 to run it" fi # --- what shipped ------------------------------------------------------------- miss= for a in libfftw3.a libfftw3f.a libfftw3l.a \ libfftw3_threads.a libfftw3f_threads.a libfftw3l_threads.a; do [ -s "/usr/lib/$a" ] || miss="$miss $a" done if [ -z "$miss" ]; then bst_ok fftw/staticdev else bst_notok fftw/staticdev "missing or empty:$(_bst_1line "$miss")"; fi check fftw/headers test -s /usr/include/fftw3.h check fftw/pkgconfig test -s /usr/lib/pkgconfig/fftw3.pc # The distro is static-only; a shared object here means --disable-shared stopped # reaching this recipe. so=$(find /usr/lib -name 'libfftw3*.so*' 2>/dev/null | head -3) if [ -z "$so" ]; then bst_ok fftw/no-shared else bst_notok fftw/no-shared "shared objects present: $(_bst_1line "$so")"; fi # --- the wisdom tools --------------------------------------------------------- # Four programs in the image that the ptest does not touch at all: the ptest runs # bench, never these. w=${TMPDIR:-/tmp}/bst-fftw.$$ mkdir -p "$w" || w=/tmp for arm in "double:fftw-wisdom" "single:fftwf-wisdom" "longdouble:fftwl-wisdom"; do name=${arm%%:*}; tool=${arm#*:} if command -v "$tool" >/dev/null 2>&1; then out=$("$tool" -e -n -o "$w/$name.wis" cof32 2>&1); rc=$? if [ "$rc" -eq 0 ] && head -1 "$w/$name.wis" 2>/dev/null | grep -q 'fftw.*wisdom' then bst_ok "fftw/wisdom-$name" else bst_notok "fftw/wisdom-$name" "rc=$rc $(_bst_1line "$out")"; fi else bst_skip "fftw/wisdom-$name" "$tool not on this image"; fi done if command -v fftw-wisdom-to-conf >/dev/null 2>&1 && [ -s "$w/double.wis" ]; then check_out fftw/wisdom-to-conf "fftw_configure_planner" \ sh -c "fftw-wisdom-to-conf < '$w/double.wis'" else bst_skip fftw/wisdom-to-conf "fftw-wisdom-to-conf absent, or no wisdom to convert" fi # Writing a wisdom file proves nothing on its own -- upstream's own check.pl # --validate-wisdom idiom is write-then-replan-from-wisdom-only, and the negative # control is real: with wis.dat removed, the same command exits 1 with # "No can_do for ofc32". So this pair asserts the wisdom was genuinely read back. if (cd "$w" && rm -f wis.dat && "$P/fftw/bench" -owisdom --verify ofc32) >/dev/null 2>&1; then check fftw/wisdom-roundtrip \ sh -c "cd '$w' && '$P/fftw/bench' -owisdom -owisdom-only --verify ofc32" else bst_notok fftw/wisdom-roundtrip "bench -owisdom did not write $w/wis.dat" fi rm -rf "$w" # --- upstream's own suite, in full, on request -------------------------------- # 1395 s measured, so it does not run at a ticket gate; the battery above is the # fixture, and this is the thing it is a fixture FOR. if [ -n "${BST_FFTW_PTEST:-}" ]; then out=$(cd "$P" && ./run-ptest 2>&1); rc=$? n=$(printf '%s\n' "$out" | grep -c '^PASS: ') if [ "$n" -eq 3 ]; then bst_ok fftw/upstream-ptest else bst_notok fftw/upstream-ptest "rc=$rc PASS=$n $(printf '%s\n' "$out" | grep '^\(PASS\|FAIL\): ' | tr '\n' ' ')"; fi fi