Earlier @simonhf asked for the program and such.
Disclaimer: this stuff was never intended to be seen by others and it is a total hack job.
I keep all c code and scripts and such in the same directory. The command to create the data for one of the graph lines is:
./ping-pong-many-parallel 0 30000000 40
And the CPU frequency scaling governor should be set to performance for this work.
The script source code is:
#! /bin/dash
#
# ping-pong-many-parallel Smythies 2024.01.23
# assume the ping pong program is local.
#
# ping-pong-many-parallel Smythies 2022.10.23
# update required to reflect changes to program
#
# ping-pong-many-parallel Smythies 2022.10.09
# Launch parrallel ping-pong pairs.
# because I always forget from last time
killall pingpong
# If it does not already exist, then create the first named pipe.
COUNTER=0
POINTER1=0
POINTER2=1
while [ $COUNTER -lt $3 ];
do
if [ -p /dev/shm/pong$POINTER1 ]
then
rm /dev/shm/pong$POINTER1
fi
mkfifo /dev/shm/pong$POINTER1
POINTER1=$(($POINTER1+1000))
POINTER2=$(($POINTER2+1000))
COUNTER=$(($COUNTER+1))
done
COUNTER=0
POINTER1=0
POINTER2=1
while [ $COUNTER -lt $3 ];
do
./pingpong /dev/shm/pong$POINTER1 /dev/shm/pong$POINTER2 $1 $2 &
./pingpong /dev/shm/pong$POINTER2 /dev/shm/pong$POINTER1 $1 $2 1 &
POINTER1=$(($POINTER1+1000))
POINTER2=$(($POINTER2+1000))
COUNTER=$(($COUNTER+1))
done
And the c program source code is:
/******************************************************
/*
/* pingpong.c Smythies 2022.10.21
/* Useing stdin and stdout redirection for this
/* program is a problem. The program doesn't start
/* execution until there is something in the
/* stdin redirected queue, so trying to start
/* things via the last flag doesn't work.
/* Try treating the incoming and outgoing named
/* as files opened herein. This will also allow
/* timeout management as a future edit.
/*
/* pingpong.c Smythies 2022.10.20
/* Use the new "last" flag to also start the
/* token passing.
/*
/* pingpong.c Smythies 2022.10.19
/* If the delay between the last read of the
/* first token and the write from the last place
/* in the chain of stuff is large enough then the
/* first intance of the program might have terminated
/* and shutdown the read pipe, resulting in a SIGPIPE
/* signal. With no handler it causes the program to
/* terminate.
/* Add an optional command line parameter to indicate if
/* this instance of the program is the last one and
/* therefore it should not attempt to pass along the
/* last token.
/*
/* pingpong.c Smythies 2021.10.26
/* Eveything works great as long as the number
/* of stops in the token passing ring is small
/* enough. However, synchronization issues
/* develop if the number of stops gets big enough.
/* Introduce a synchorizing step, after which
/* there should not be any EOF return codes.
/*
/* pingpong.c Smythies 2021.10.24
/* Print loop number and error code upon error
/* exit. Exit on 1st error. Was 3rd.
/*
/* pingpong.c Smythies 2021.10.23
/* Change to using CLOCK_MONOTONIC_RAW instead of
/* gettimeofday, as it doesn't have any
/* adjustments.
/* Change to nanoseconds.
/*
/* pingpong.c Smythies 2021.07.31
/* Add write error check.
/*
/* pingpong.c Smythies 2021.07.24
/* Exit after a few errors.
/*
/* pingpong.c Smythies 2021.07.23
/* Add execution time.
/*
/* pingpong.c Smythies 2020.12.07
/* Add an outter loop counter comnmand line option.
/* Make it optional, so as not to break my existing
/* scripts.
/*
/* pingpong.c Smythies 2020.06.21
/* The original code is from Alexander.
/* (See: https://marc.info/?l=linux-kernel&m=159137588213540&w=2)
/* But, it seems to get out of sync in my application.
/* Start this history header.
/* I can only think of some error return.
/* Add some error checking, I guess.
/*
/******************************************************/
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
//#include <signal.h>
//#include <sys/wait.h>
//#include <linux/unistd.h>
#define MAX_ERRORS 2
/* Aribitrary */
#define SYNC_LOOPS 3
unsigned long long stamp(void){
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC_RAW,&tv);
return (unsigned long long)tv.tv_sec * 1000000000 + tv.tv_nsec;
} /* endprocedure */
int main(int argc, char **argv){
unsigned long long tend, tstart;
long i, j, k, n, m;
long eof_count = 0;
int error_count = 0;
int err, inf, outf, errvalue;
int last = 0;
char c = '\n';
char *infile, *outfile;
// fprintf(stderr, "begin...\n");
switch(argc){
case 4:
infile = argv[1];
outfile = argv[2];
n = atol(argv[3]);
m = LONG_MAX;
break;
case 5:
infile = argv[1];
outfile = argv[2];
n = atol(argv[3]);
m = atol(argv[4]);
break;
case 6:
infile = argv[1];
outfile = argv[2];
n = atol(argv[3]);
m = atol(argv[4]);
last = atoi(argv[5]);
break;
default:
printf("%s : Useage: pingpong infifo outfifo inner_loop [optional outer_loop [optional last flag]]\n", argv[0]);
return -1;
} /* endcase */
// printf(" infile: %s ; outfile: %s ; %d\n", infile, outfile, last);
if(last != 1){ // for all but the last, create the named pipe outfile
err = mkfifo(outfile, 0666);
if ((err != 0) && (errno != EEXIST)){ // file already exists is OK
errvalue = errno;
printf("Cannot create output fifo file: %s ; %d ; %s\n", outfile, err, strerror(errvalue));
return -1;
} /* endif */
} else { // for the last we open the write first, read should already be open.
if ((outf = open(outfile, O_WRONLY)) == -1){
errvalue = errno;
printf("Cannot open last output fifo file: %s ; %d ; %s\n", outfile, outf, strerror(errvalue));
return -1;
} /* endif */
} /* endif */
if ((inf = open(infile, O_RDONLY)) == -1){
errvalue = errno;
printf("Cannot open input fifo file: %s ; %d ; %s\n", outfile, inf, strerror(errvalue));
return -1;
} /* endif */
if(last != 1){ // for all but the last, now we open the write
// if ((outf = open(outfile, O_WRONLY | O_NONBLOCK)) == -1){
if ((outf = open(outfile, O_WRONLY)) == -1){
errvalue = errno;
printf("Cannot open not last output fifo file: %s ; %d ; %s\n", outfile, outf, strerror(errvalue));
return -1;
} /* endif */
} /* endif */
if(last == 1){ // the last chain initiates the token passing
// usleep(999999);
err = write(outf, &c, 1);
if(err != 1){
fprintf(stderr, "pingpong write error on startup, aborting. %d %d %d\n", last, err, outf);
return -1;
} /* endif */
} /* endif */
// printf("flag 4: inf: %d ; outf: %d ; %d \n", inf, outf, last);
/* make sure we are synchronized. EOF (0 return code) can occur until we are */
j = SYNC_LOOPS;
while(j > 0) { // for SYNC_LOOP successful loops do:
err = read(inf, &c, 1);
if(err == 1){
j--; // don't decrement for EOF.
for (i = n; i; i--){ // we also attempt to sync in time for later T start
k = i;
k = k++;
} /* endfor */
err = write(outf, &c, 1);
if(err != 1){ // and then pass along the token along to the next pipeline step.
fprintf(stderr, "pingpong sync step: write error or timeout to named pipe. (error code: %d ; loops left: %ld ; last: %d)\n", err, j, last);
return -1;
} /* endif */
} else {
if(err < 0){
fprintf(stderr, "pingpong sync step: read error or timeout from named pipe. (error code: %d ; loops left: %ld ; last: %d)\n", err, j, last);
return -1;
} else {
eof_count++; // does the loop counter need to be reset??
} /* endif */
} /* endif */
} /* endwhile */
// printf(" infile: %s ; outfile: %s ; last: %d; eof_count %ld\n", infile, outfile, last, eof_count);
/* now we are synchronized, or so I claim. Get on with the real work. EOF is an error now.*/
j = m;
tstart = stamp(); /* only start the timer once synchronized */
while(j > 0) { // for outer_loop times do:
err = read(inf, &c, 1);
if(err == 1){
for (i = n; i; i--){ // for each token, do a packet of work.
k = i;
k = k++;
} /* endfor */
err = write(outf, &c, 1);
if(err != 1){ // and then pass along the token along to the next pipeline step.
fprintf(stderr, "pingpong write error or timeout to named pipe. (error code: %d ; loops left: %ld ; EOFs: %ld ; last: %d)\n", err, j, eof_count, last);
error_count++;
if(error_count >= MAX_ERRORS) return -1;
} /* endif */
} else {
error_count++;
fprintf(stderr, "pingpong read error or timeout from named pipe. (error code: %d ; loops left: %ld ; EOFs: %ld ; last: %d)\n", err, j, eof_count, last);
if(error_count >= MAX_ERRORS) return -1;
} /* endif */
// if(j <= 3) fprintf(stderr, "Loop: %ld ; EOFs: %ld\n", j, eof_count);
j--;
} /* endwhile */
tend = stamp(); // the timed portion is done
/* Now we do one token pass to flush. The previous write pipe may have already been terminated, so EOF read response is O.K. */
err = read(inf, &c, 1);
if(err == 1){
if(last != 1){ // last in the chain does not pass along the last token
err = write(outf, &c, 1);
if(err != 1){ // and then pass along the token along to the next pipeline step.
fprintf(stderr, "pingpong flush loop: write error or timeout to named pipe. (error code: %d ; EOFs: %ld ; last: %d)\n", err, eof_count, last);
} /* endif */
} /* endif */
} else {
fprintf(stderr, "pingpong flush loop: read error or timeout from named pipe. (error code: %d ; EOFs: %ld ; last: %d)\n", err, eof_count, last);
} /* endif */
fprintf(stderr,"%.4f usecs/loop. EOFs: %ld\n",(double)(tend-tstart)/((double) m * 1000.0), eof_count);
close(outf);
close(inf);
return -1;
// return 0;
} /* endprogram */
EDIT: The script and program are also posted over on Ubuntu forums, along with more thorough how to instructions.