[including Rui Nuno Capela in cc: list] >> Con Kolivas <[EMAIL PROTECTED]> writes: >> >>>This patch for 2.6.11-rc1 provides a method of providing real time >>>scheduling to unprivileged users which increasingly is desired for >>>multimedia workloads.
> Jack O'Quin wrote: >> I ran some jack_test3.2 runs with this, using all the default >> settings. The results of three runs differ quite significantly for no >> obvious reason. I can't figure out why the DSP load should vary so >> much. Con Kolivas <[EMAIL PROTECTED]> writes: > I installed a fresh jack installation and got the test suite. I tried > running the test suite and found it only ran to completion if I > changed the run time right down to 30 seconds from 300. Otherwise it > bombed out almost instantly at the default of 300. I don't know if > that helps you debug the problem or not but it might be worth > mentioning. Here are a couple of bug fixes for the test package. I still had not gotten them to Rui (the test author). He has created several newer versions, but I'm still using this modified jack_test3.2, so the number comparisons will continue to make sense.
--- jack_test3.2/jack_test3_client.cpp Thu Dec 9 10:26:12 2004 +++ jack_test/jack_test3_client.cpp Sat Jan 15 20:48:20 2005 @@ -13,20 +13,25 @@ unsigned int seconds_to_run = 60; unsigned int num_of_ports = 4; +/* mix all the input ports to each output port */ int process(jack_nframes_t frames, void *arg) { // std::cout << "process callback" << std::endl; jack_default_audio_sample_t *ibuf; jack_default_audio_sample_t *obuf; for (int i = 0; i < num_of_ports; i++) { - obuf = (jack_default_audio_sample_t*) jack_port_get_buffer(oports[i], frames); + obuf = (jack_default_audio_sample_t*) + jack_port_get_buffer(oports[i], frames); for (int j = 0; j < num_of_ports; j++) { - ibuf = (jack_default_audio_sample_t*) jack_port_get_buffer(iports[j], frames); - for (jack_nframes_t frame = 0; frame < frames; frame++) { + ibuf = (jack_default_audio_sample_t*) + jack_port_get_buffer(iports[j], frames); + for (jack_nframes_t frame = 0; + frame < frames; frame++) { if (j == 0) obuf[frame] = 0.0; if (ibuf[frame] < -1E-6 || ibuf[frame] > +1E-6) - obuf[frame] += ibuf[frame] / (float) num_of_ports; + obuf[frame] += ibuf[frame] + / (float) num_of_ports; } } } @@ -68,10 +73,23 @@ for (int i = 0; i < num_of_ports; i++) { std::stringstream iport_name; iport_name << "in_" << i; - iports[i] = jack_port_register(client, iport_name.str().c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0); + iports[i] = + jack_port_register(client, + iport_name.str().c_str(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput|JackPortIsTerminal, + 0); std::stringstream oport_name; oport_name << "out_" << i; - oports[i] = jack_port_register(client, oport_name.str().c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsTerminal|JackPortIsOutput, 0); + oports[i] = jack_port_register(client, + oport_name.str().c_str(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsTerminal|JackPortIsOutput, + 0); + if ((iports[i] == NULL) || (oports[i] == NULL)) { + std::cout << "port registration failed" << std::endl; + goto exit; + } } std::cout << "set_process_callback" << std::endl; jack_set_process_callback(client, process, 0); @@ -85,6 +103,8 @@ sleep(seconds_to_run); jack_deactivate(client); + +exit: jack_client_close(client); delete [] iports; --- jack_test3.2/jack_test3_nmeter.c Sat Nov 27 09:08:01 2004 +++ jack_test/jack_test3_nmeter.c Thu Dec 30 07:50:38 2004 @@ -21,7 +21,9 @@ //============== #define NL "\n" typedef unsigned long long ullong; +#ifndef __USE_MISC typedef unsigned long ulong; +#endif typedef ulong sample_t; --- jack_test3.2/jack_test3_run.sh Fri Dec 10 06:21:21 2004 +++ jack_test/jack_test3_run.sh Sat Jan 15 22:23:06 2005 @@ -6,12 +6,14 @@ CLIENTS=$2 PORTS=$3 PERIOD=$4 +PLYBK_PORTS=$5 # Parameter defaults. [ -z "${SECS}" ] && SECS=300 [ -z "${CLIENTS}" ] && CLIENTS=20 [ -z "${PORTS}" ] && PORTS=4 [ -z "${PERIOD}" ] && PERIOD=64 +[ -z "${PLYBK_PORTS}" ] && PLYBK_PORTS=32 # Local tools must be on same directory... BASEDIR=`dirname $0` @@ -24,13 +26,13 @@ NMETER_ARGS="t c i x b m" # jackd configuration. -JACKD="/usr/bin/jackd" +JACKD="jackd" JACKD_DRIVER="alsa" JACKD_DEVICE="hw:0" JACKD_PRIO=60 JACKD_RATE=44100 -JACKD_PORTS=$(((${CLIENTS} + 1) * ${PORTS} * 2)) -JACKD_ARGS="-v -R -P${JACKD_PRIO} -p${JACKD_PORTS} -d${JACKD_DRIVER} -d${JACKD_DEVICE} -r${JACKD_RATE} -p${PERIOD} -n2 -S -P" +JACKD_PORTS=$((${CLIENTS} * ${PORTS} * 2 + ${PLYBK_PORTS})) +JACKD_ARGS="-Rv -p${JACKD_PORTS} -d${JACKD_DRIVER} -d${JACKD_DEVICE} -r${JACKD_RATE} -p${PERIOD} -n2 -P" # client test configuration. CLIENT="${BASEDIR}/jack_test3_client" @@ -85,6 +87,7 @@ echo "Number of clients (CLIENTS) = ${CLIENTS}" >> ${LOG} 2>&1 echo "Ports per client (PORTS) = ${PORTS}" >> ${LOG} 2>&1 echo "Frames per buffer (PERIOD) = ${PERIOD}" >> ${LOG} 2>&1 +echo "Playback ports (PLYBK_PORTS) = ${PLYBK_PORTS}" >> ${LOG} 2>&1 echo >> ${LOG} 2>&1 exec_log "uname -a" @@ -102,7 +105,7 @@ ilist_log # -# Lauch nmeter in the background... +# Launch nmeter in the background... # echo -n "Launching `basename ${NMETER}`..." (${NMETER} ${NMETER_ARGS} >> ${LOG} 2>&1) & @@ -110,7 +113,7 @@ echo "done." sleep 1 -# Lauch the test client suite... +# Launch the test client suite... SLEEP=6 echo -n "Launching ${CLIENTS} ${CLIENT}(s) instance(s)..." for NUM in `seq 1 ${CLIENTS}`; do @@ -129,7 +132,7 @@ (sleep ${SLEEP}; killall `basename ${JACKD}`) & # -# Lauch jackd and wait for it... +# Launch jackd and wait for it... # echo -n "Running `basename ${JACKD}`..." exec_log ${JACKD} ${JACKD_ARGS} @@ -149,7 +152,7 @@ sleep 1 sync -# Summary log analynis... +# Summary log analysis... cat ${LOG} | awk -f ${BASEDIR}/jack_test3_summary.awk | tee -a ${LOG} # Finally, plot log output... --- jack_test3.2/Makefile Thu Nov 25 05:28:47 2004 +++ jack_test/Makefile Sat Jan 15 20:50:04 2005 @@ -1,10 +1,10 @@ all: jack_test3_nmeter jack_test3_client jack_test3_nmeter: jack_test3_nmeter.c - gcc -o jack_test3_nmeter jack_test3_nmeter.c + $(CC) -g -o jack_test3_nmeter jack_test3_nmeter.c jack_test3_client: jack_test3_client.cpp - g++ -o jack_test3_client jack_test3_client.cpp -ljack + $(CXX) -g -o jack_test3_client jack_test3_client.cpp -ljack clean: rm -vf jack_test3_nmeter jack_test3_client
For completeness, here's Rui's unmodified test package against which these diff's were generated.
jack_test3.2.tar.gz
Description: JACK test version 3.2 (from rncbc)
There's also a modified script for running with nice --20, called jack_test3_nice.sh. I hacked it for Ingo's tests. It really should be integrated into the main jack_test3_run.sh as an option, but I have not bothered. I think we're beyond that at this point, anyway. Mainly, it proves that per-thread granularity is essential for making this stuff work.
#!/bin/sh # # Command line arguments. SECS=$1 CLIENTS=$2 PORTS=$3 PERIOD=$4 PLYBK_PORTS=$5 # Parameter defaults. [ -z "${SECS}" ] && SECS=300 [ -z "${CLIENTS}" ] && CLIENTS=20 [ -z "${PORTS}" ] && PORTS=4 [ -z "${PERIOD}" ] && PERIOD=64 [ -z "${PLYBK_PORTS}" ] && PLYBK_PORTS=32 # Local tools must be on same directory... BASEDIR=`dirname $0` # Make sure our local tools are in place. (cd ${BASEDIR}; make all > /dev/null) || exit 1 # nmeter configuration. NMETER="${BASEDIR}/jack_test3_nmeter" NMETER_ARGS="t c i x b m" # jackd configuration. JACKD="jackd" JACKD_DRIVER="alsa" JACKD_DEVICE="hw:0" JACKD_PRIO=60 JACKD_RATE=44100 JACKD_PORTS=$((${CLIENTS} * ${PORTS} * 2 + ${PLYBK_PORTS})) JACKD_ARGS="-v -p${JACKD_PORTS} -d${JACKD_DRIVER} -d${JACKD_DEVICE} -r${JACKD_RATE} -p${PERIOD} -n2 -P" # client test configuration. CLIENT="${BASEDIR}/jack_test3_client" CLIENT_ARGS="${SECS} ${PORTS}" # process/thread list configuration. PIDOF="/sbin/pidof" PLIST="ps -o pid,tid,class,rtprio,ni,pri,pcpu,stat,comm" # Log filename. LOG="jack_test3-`uname -r`-`date +'%Y%m%d%H%M'`.log" # Command executive and logger. exec_log () { CMD="$*" echo "-----------------------" >> ${LOG} 2>&1 echo "# ${CMD}" >> ${LOG} 2>&1 ${CMD} >> ${LOG} 2>&1 echo >> ${LOG} 2>&1 } # Process/thread status loggers. plist_log () { PIDS="$*" if [ -n "${PIDS}" ]; then echo "- - - - - - - - - - - -" >> ${LOG} 2>&1 ${PLIST} -m ${PIDS} >> ${LOG} 2>&1 echo >> ${LOG} 2>&1 fi } # IRQ thread status loggers. ilist_log () { echo "- - - - - - - - - - - -" >> ${LOG} 2>&1 ${PLIST} --sort -rtprio -e \ | egrep '(^[ ]+PID|IRQ|jack)' >> ${LOG} 2>&1 echo >> ${LOG} 2>&1 } # # Log headings -- show some relevant info... # echo "*** Started `date` ***" >> ${LOG} 2>&1 echo >> ${LOG} 2>&1 echo "Seconds to run (SECS) = ${SECS}" >> ${LOG} 2>&1 echo "Number of clients (CLIENTS) = ${CLIENTS}" >> ${LOG} 2>&1 echo "Ports per client (PORTS) = ${PORTS}" >> ${LOG} 2>&1 echo "Frames per buffer (PERIOD) = ${PERIOD}" >> ${LOG} 2>&1 echo "Playback ports (PLYBK_PORTS) = ${PLYBK_PORTS}" >> ${LOG} 2>&1 echo >> ${LOG} 2>&1 exec_log "uname -a" exec_log "cat /proc/asound/version" exec_log "cat /proc/asound/cards" #exec_log "grep . /proc/sys/kernel/*_preemption" #exec_log "grep . /proc/irq/*/*/threaded" #exec_log "grep . /sys/block/hd*/queue/max_sectors_kb" exec_log "cat /proc/interrupts" # This is just about to be sure... ilist_log # # Launch nmeter in the background... # echo -n "Launching `basename ${NMETER}`..." (nice -15 ${NMETER} ${NMETER_ARGS} >> ${LOG} 2>&1) & sleep 2 echo "done." sleep 1 # Launch the test client suite... SLEEP=6 echo -n "Launching ${CLIENTS} ${CLIENT}(s) instance(s)..." for NUM in `seq 1 ${CLIENTS}`; do CLIENT_CMDLINE="${CLIENT} ${CLIENT_ARGS}" SLEEP=$((${SLEEP} + 3)) (sleep ${SLEEP}; echo -n "$NUM..."; exec_log ${CLIENT_CMDLINE}) & done echo "done." # Let there be some evidence of process status... SLEEP=$((${SLEEP} + 6)) (sleep ${SLEEP}; plist_log -C `basename ${JACKD}`; plist_log -C `basename ${CLIENT}`) & # Let jackd live for extended but limited time... SLEEP=$((${SLEEP} + ${SECS})) (sleep ${SLEEP}; killall `basename ${JACKD}`) & # # Launch jackd and wait for it... # echo -n "Running `basename ${JACKD}`..." exec_log ${JACKD} ${JACKD_ARGS} echo "done." sleep 1 #echo -n "Killing `basename ${CLIENT}`..." #killall `basename ${CLIENT}` && echo "OK." || echo "FAILED." #sleep 1 echo -n "Killing `basename ${NMETER}`..." killall `basename ${NMETER}` && echo "OK." || echo "FAILED." echo "*** Terminated `date` ***" >> ${LOG} 2>&1 sync sleep 1 sync # Summary log analynis... cat ${LOG} | awk -f ${BASEDIR}/jack_test3_summary.awk | tee -a ${LOG} # Finally, plot log output... ${BASEDIR}/jack_test3_plot.sh ${LOG}
What version of JACK do you have? (jackd --version) You need a recent CVS version to get all the data collection Rui and Lee have added. Looks like you're missing that. Delay Max is an important statistic. You need a *very* recent version (about 10 minutes ago) to fix that jack_deactivate() segfault. (see URL below) > It occasionally would segfault on client exit as well (as you've > already mentioned). I think we're still in the dark here to be honest. Try again with JACK 0.99.48. It's in CVS now, but you probably need this tarball to get around the dreaded SourceForge anon CVS lag... http://www.joq.us/jack/tarballs/jack-audio-connection-kit-0.99.48.tar.gz The results I get with these versions are a lot more stable. But, there are still some puzzles about the scheduling. Do you distinguish different SCHED_ISO priorities? JACK runs with three different SCHED_FIFO priorities: (1) The main jackd audio thread uses the -P parameter. The JACK default is 10, but Rui's script sets it with -P60. I don't think the absolute value matters, but the value relative to the other JACK realtime threads probably does. (2) The clients' process threads run at a priority one less (59). (3) The watchdog timer thread runs at a priority 10 greater (70). (4) LinuxThreads creates a manager thread in each process running one level higher than the highest user realtime thread priority. -- joq