Skip to contentSkip to conversation

rt-tests: cyclictest: Add support to report standard deviation

RFC NAK Punit Agrawal 4 participants 23 messages Oct 14, 2021 – Nov 11, 2021 Kbuild

lore
Share & tools
lore
#0 lore raw

cover rt-tests: cyclictest: Add support to report standard deviation

Hi,

Standard deviation is an important measure to evaluate the latency response of a real time system. It is calculated by downstream tools such as rteval but the value cannot be correctly calculated when the latency value exceeds the range of histogram.

This series adds support to report the streaming standard deviation of latencies to cyclictest. This approach avoids having to track all latency values and adds minimal overhead (two additional doubles and compute steps) to each iteration.

In terms of patch organization, the first 5 patches are clean-ups that were noticed in the course of developing the support for standard deviation. It should be possible to apply them indepndently.

Patch 6 converts the existing average calculation to a streaming version. This running average value is needed for the standard deviation.

Patch 7 finally adds support for calculating standard deviation.

The changes were verified by capturing the latency samples and verifying the average and standard deviation with manual calculation.

All feedback welcome.

Thanks,
Punit

Punit Agrawal (7):
rt-tests: cyclictest: Drop unused defines
rt-tests: cyclictest: Simplify duplicate initialization of "stop"
rt-tests: cyclictest: Drop unnecessary variable "stopped"
rt-tests: cyclictest: Drop unnecessary variable "bufsize"
rt-tests: cyclictest: Move signal handler to avoid function
declaration
rt-tests: cyclictest: Use streaming algorithm to calculate averages
rt-tests: cyclictest: Add support to report standard deviation

 Makefile                    |   2 +-
 src/cyclictest/cyclictest.c | 149 +++++++++++++++++++++---------------
 2 files changed, 87 insertions(+), 64 deletions(-)
Signature · 1 line
2.32.0
#1 lore raw

RFC 1/7 rt-tests: cyclictest: Drop unused defines

From: Punit Agrawal <[email protected]>

KVARS, KVARNAMELEN and KVALUELEN defines are not used in cyclictest. Drop them.

---
 src/cyclictest/cyclictest.c | 4 ----
 1 file changed, 4 deletions(-)
src/cyclictest/cyclictest.c +0 -4
index 2187d98de725..0c1e6617e0e1 100644
@@ -99,10 +99,6 @@ extern int clock_nanosleep(clockid_t __clock_id, int __flags,
 /* Must be power of 2 ! */
 #define VALBUF_SIZE		16384
 
-#define KVARS			32
-#define KVARNAMELEN		32
-#define KVALUELEN		32
-
 #if (defined(__i386__) || defined(__x86_64__))
 #define ARCH_HAS_SMI_COUNTER
 #endif
Signature · 1 line
2.32.0
#2 lore raw

Re: [RFC 1/7] rt-tests: cyclictest: Drop unused defines

Quoted 29 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

KVARS, KVARNAMELEN and KVALUELEN defines are not used in cyclictest. Drop them.

---
 src/cyclictest/cyclictest.c | 4 ----
 1 file changed, 4 deletions(-)
src/cyclictest/cyclictest.c +0 -4
index 2187d98de725..0c1e6617e0e1 100644
@@ -99,10 +99,6 @@ extern int clock_nanosleep(clockid_t __clock_id, int __flags,
 /* Must be power of 2 ! */
 #define VALBUF_SIZE		16384
 
-#define KVARS			32
-#define KVARNAMELEN		32
-#define KVALUELEN		32
-
 #if (defined(__i386__) || defined(__x86_64__))
 #define ARCH_HAS_SMI_COUNTER
 #endif
Signature · 1 line
2.32.0

This looks like more left over code from before we handed tracing functionality to tracecmd. Just remove the 'RFC' and I'll sign-off on that.

#3 lore raw

Re: [RFC 1/7] rt-tests: cyclictest: Drop unused defines

In reply to Punit Agrawal
Quoted 29 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

KVARS, KVARNAMELEN and KVALUELEN defines are not used in cyclictest. Drop them.

---
 src/cyclictest/cyclictest.c | 4 ----
 1 file changed, 4 deletions(-)
src/cyclictest/cyclictest.c +0 -4
index 2187d98de725..0c1e6617e0e1 100644
@@ -99,10 +99,6 @@ extern int clock_nanosleep(clockid_t __clock_id, int __flags,
 /* Must be power of 2 ! */
 #define VALBUF_SIZE		16384
 
-#define KVARS			32
-#define KVARNAMELEN		32
-#define KVALUELEN		32
-
 #if (defined(__i386__) || defined(__x86_64__))
 #define ARCH_HAS_SMI_COUNTER
 #endif
Signature · 1 line
2.32.0
#4 lore raw

RFC 2/7 rt-tests: cyclictest: Simplify duplicate initialization of "stop"

In reply to Punit Agrawal

From: Punit Agrawal <[email protected]>

The timespec structure "stop" is initialised whether it is used or not as the compiler is not smart enough to figure out that it's use is always guarded by the "duration" variable. As a result, "stop" needs to be initialised whether it's used or not to avoid a compiler warning.

Replace the duplicate memset statements by initializing "stop" using structure initialiser.

---
 src/cyclictest/cyclictest.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
src/cyclictest/cyclictest.c +1 -4
index 0c1e6617e0e1..d06ed01c58f4 100644
@@ -507,7 +507,7 @@ static void *timerthread(void *param)
 	struct sigevent sigev;
 	sigset_t sigset;
 	timer_t timer;
-	struct timespec now, next, interval, stop;
+	struct timespec now, next, interval, stop = { 0 };
 	struct itimerval itimer;
 	struct itimerspec tspec;
 	struct thread_stat *stat = par->stats;
@@ -516,8 +516,6 @@ static void *timerthread(void *param)
 	pthread_t thread;
 	unsigned long smi_now, smi_old = 0;
 
-	memset(&stop, 0, sizeof(stop));
-
 	/* if we're running in numa mode, set our memory node */
 	if (par->node != -1)
 		rt_numa_set_numa_run_on_node(par->node, par->cpu);
@@ -598,7 +596,6 @@ static void *timerthread(void *param)
 	tsnorm(&next);
 
 	if (duration) {
-		memset(&stop, 0, sizeof(stop)); /* grrr */
 		stop = now;
 		stop.tv_sec += duration;
 	}
Signature · 1 line
2.32.0
#5 lore raw

Re: [RFC 2/7] rt-tests: cyclictest: Simplify duplicate initialization of "stop"

Quoted 50 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

The timespec structure "stop" is initialised whether it is used or not as the compiler is not smart enough to figure out that it's use is always guarded by the "duration" variable. As a result, "stop" needs to be initialised whether it's used or not to avoid a compiler warning.

Replace the duplicate memset statements by initializing "stop" using structure initialiser.

---
 src/cyclictest/cyclictest.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
src/cyclictest/cyclictest.c +1 -4
index 0c1e6617e0e1..d06ed01c58f4 100644
@@ -507,7 +507,7 @@ static void *timerthread(void *param)
 	struct sigevent sigev;
 	sigset_t sigset;
 	timer_t timer;
-	struct timespec now, next, interval, stop;
+	struct timespec now, next, interval, stop = { 0 };
 	struct itimerval itimer;
 	struct itimerspec tspec;
 	struct thread_stat *stat = par->stats;
@@ -516,8 +516,6 @@ static void *timerthread(void *param)
 	pthread_t thread;
 	unsigned long smi_now, smi_old = 0;
 
-	memset(&stop, 0, sizeof(stop));
-
 	/* if we're running in numa mode, set our memory node */
 	if (par->node != -1)
 		rt_numa_set_numa_run_on_node(par->node, par->cpu);
@@ -598,7 +596,6 @@ static void *timerthread(void *param)
 	tsnorm(&next);
 
 	if (duration) {
-		memset(&stop, 0, sizeof(stop)); /* grrr */
 		stop = now;
 		stop.tv_sec += duration;
 	}
Signature · 1 line
2.32.0

Looks good, I'll sign off on that, just remove the RFC

#6 lore raw

Re: [RFC 2/7] rt-tests: cyclictest: Simplify duplicate initialization of "stop"

In reply to Punit Agrawal
Quoted 50 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

The timespec structure "stop" is initialised whether it is used or not as the compiler is not smart enough to figure out that it's use is always guarded by the "duration" variable. As a result, "stop" needs to be initialised whether it's used or not to avoid a compiler warning.

Replace the duplicate memset statements by initializing "stop" using structure initialiser.

---
 src/cyclictest/cyclictest.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
src/cyclictest/cyclictest.c +1 -4
index 0c1e6617e0e1..d06ed01c58f4 100644
@@ -507,7 +507,7 @@ static void *timerthread(void *param)
 	struct sigevent sigev;
 	sigset_t sigset;
 	timer_t timer;
-	struct timespec now, next, interval, stop;
+	struct timespec now, next, interval, stop = { 0 };
 	struct itimerval itimer;
 	struct itimerspec tspec;
 	struct thread_stat *stat = par->stats;
@@ -516,8 +516,6 @@ static void *timerthread(void *param)
 	pthread_t thread;
 	unsigned long smi_now, smi_old = 0;
 
-	memset(&stop, 0, sizeof(stop));
-
 	/* if we're running in numa mode, set our memory node */
 	if (par->node != -1)
 		rt_numa_set_numa_run_on_node(par->node, par->cpu);
@@ -598,7 +596,6 @@ static void *timerthread(void *param)
 	tsnorm(&next);
 
 	if (duration) {
-		memset(&stop, 0, sizeof(stop)); /* grrr */
 		stop = now;
 		stop.tv_sec += duration;
 	}
Signature · 1 line
2.32.0
#7 lore raw

RFC 3/7 rt-tests: cyclictest: Drop unnecessary variable "stopped"

In reply to Punit Agrawal

From: Punit Agrawal <[email protected]>

In timerthread(), "stopped" is used to track when the tracelimit has been breached. It prevents additional updates of break_thread_id.

But the timerthread functionality is already guarded by "shutdown" which is also updated then latency crosses tracelimit. Remove the redundant variable "stopped" to simplify the logic.

---
 src/cyclictest/cyclictest.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
src/cyclictest/cyclictest.c +1 -3
index d06ed01c58f4..721d242a1da0 100644
@@ -511,7 +511,6 @@ static void *timerthread(void *param)
 	struct itimerval itimer;
 	struct itimerspec tspec;
 	struct thread_stat *stat = par->stats;
-	int stopped = 0;
 	cpu_set_t mask;
 	pthread_t thread;
 	unsigned long smi_now, smi_old = 0;
@@ -715,8 +714,7 @@ static void *timerthread(void *param)
 		if (duration && (calcdiff(now, stop) >= 0))
 			shutdown++;
 
-		if (!stopped && tracelimit && (diff > tracelimit)) {
-			stopped++;
+		if (tracelimit && (diff > tracelimit)) {
 			shutdown++;
 			pthread_mutex_lock(&break_thread_id_lock);
 			if (break_thread_id == 0) {
Signature · 1 line
2.32.0
#8 lore raw

RFC 4/7 rt-tests: cyclictest: Drop unnecessary variable "bufsize"

In reply to Punit Agrawal

From: Punit Agrawal <[email protected]>

Two copies of "bufsize", initialised with the same value are declared in enclosed blocks. Remove the redundant declaration.

---
 src/cyclictest/cyclictest.c | 1 -
 1 file changed, 1 deletion(-)
src/cyclictest/cyclictest.c +0 -1
index 721d242a1da0..9c67a3ce3034 100644
@@ -2054,7 +2054,6 @@ int main(int argc, char **argv)
 			memset(stat->values, 0, bufsize);
 			par->bufmsk = VALBUF_SIZE - 1;
 			if (smi) {
-				int bufsize = VALBUF_SIZE * sizeof(long);
 				stat->smis = threadalloc(bufsize, node);
 				if (!stat->smis)
 					goto outall;
Signature · 1 line
2.32.0
#9 lore raw

Re: [RFC 4/7] rt-tests: cyclictest: Drop unnecessary variable "bufsize"

Quoted 26 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

Two copies of "bufsize", initialised with the same value are declared in enclosed blocks. Remove the redundant declaration.

---
 src/cyclictest/cyclictest.c | 1 -
 1 file changed, 1 deletion(-)
src/cyclictest/cyclictest.c +0 -1
index 721d242a1da0..9c67a3ce3034 100644
@@ -2054,7 +2054,6 @@ int main(int argc, char **argv)
 			memset(stat->values, 0, bufsize);
 			par->bufmsk = VALBUF_SIZE - 1;
 			if (smi) {
-				int bufsize = VALBUF_SIZE * sizeof(long);
 				stat->smis = threadalloc(bufsize, node);
 				if (!stat->smis)
 					goto outall;
Signature · 1 line
2.32.0

NACK: two different scopes, two different variables.

#10 lore raw

Re: [RFC 4/7] rt-tests: cyclictest: Drop unnecessary variable "bufsize"

Quoted 21 lines · John Kacur
Quoted 19 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

Two copies of "bufsize", initialised with the same value are declared in enclosed blocks. Remove the redundant declaration.

---
 src/cyclictest/cyclictest.c | 1 -
 1 file changed, 1 deletion(-)
src/cyclictest/cyclictest.c +0 -1
index 721d242a1da0..9c67a3ce3034 100644
@@ -2054,7 +2054,6 @@ int main(int argc, char **argv)
 			memset(stat->values, 0, bufsize);
 			par->bufmsk = VALBUF_SIZE - 1;
 			if (smi) {
-				int bufsize = VALBUF_SIZE * sizeof(long);
Quoted 8 lines
Quoted 7 lines
src/cyclictest/cyclictest.c +0 -0
 				stat->smis = threadalloc(bufsize, node);
 				if (!stat->smis)
 					goto outall;
Signature · 1 line
2.32.0

NACK: two different scopes, two different variables.

If they had different values or if the inner scope was not dependent (nested) I would agree.

But no strong opinion - I'll drop this one.

#11 lore raw

Re: [RFC 4/7] rt-tests: cyclictest: Drop unnecessary variable "bufsize"

Quoted 32 lines · Punit Agrawal
Quoted 21 lines · John Kacur
Quoted 19 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

Two copies of "bufsize", initialised with the same value are declared in enclosed blocks. Remove the redundant declaration.

---
 src/cyclictest/cyclictest.c | 1 -
 1 file changed, 1 deletion(-)
src/cyclictest/cyclictest.c +0 -1
index 721d242a1da0..9c67a3ce3034 100644
@@ -2054,7 +2054,6 @@ int main(int argc, char **argv)
 			memset(stat->values, 0, bufsize);
 			par->bufmsk = VALBUF_SIZE - 1;
 			if (smi) {
-				int bufsize = VALBUF_SIZE * sizeof(long);
Quoted 8 lines
Quoted 7 lines
src/cyclictest/cyclictest.c +0 -0
 				stat->smis = threadalloc(bufsize, node);
 				if (!stat->smis)
 					goto outall;
Signature · 1 line
2.32.0

NACK: two different scopes, two different variables.

Quoted 5 lines

If they had different values or if the inner scope was not dependent (nested) I would agree.

But no strong opinion - I'll drop this one.

I had another look at this and realize that I am wrong. bufsize is just a buffer size passed to threadalloc which does the malloc which gives the two different addresses we are concerned with. Go ahead and submit this one and I'll sign-off on it.

Thanks

John

#12 lore raw

Re: [RFC 4/7] rt-tests: cyclictest: Drop unnecessary variable "bufsize"

In reply to Punit Agrawal
Quoted 26 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

Two copies of "bufsize", initialised with the same value are declared in enclosed blocks. Remove the redundant declaration.

---
 src/cyclictest/cyclictest.c | 1 -
 1 file changed, 1 deletion(-)
src/cyclictest/cyclictest.c +0 -1
index 721d242a1da0..9c67a3ce3034 100644
@@ -2054,7 +2054,6 @@ int main(int argc, char **argv)
 			memset(stat->values, 0, bufsize);
 			par->bufmsk = VALBUF_SIZE - 1;
 			if (smi) {
-				int bufsize = VALBUF_SIZE * sizeof(long);
 				stat->smis = threadalloc(bufsize, node);
 				if (!stat->smis)
 					goto outall;
Signature · 1 line
2.32.0
#13 lore raw

RFC 5/7 rt-tests: cyclictest: Move signal handler to avoid function declaration

In reply to Punit Agrawal

From: Punit Agrawal <[email protected]>

---
 src/cyclictest/cyclictest.c | 78 ++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 41 deletions(-)
src/cyclictest/cyclictest.c +37 -41
index 9c67a3ce3034..48a782fe167d 100644
@@ -220,10 +220,6 @@ static char jsonfile[MAX_PATH];
 static struct thread_param **parameters;
 static struct thread_stat **statistics;
 
-static void print_stat(FILE *fp, struct thread_param *par, int index, int verbose, int quiet);
-static void rstat_print_stat(struct thread_param *par, int index, int verbose, int quiet);
-static void rstat_setup(void);
-
 static int latency_target_fd = -1;
 static int32_t latency_target_value = 0;
 
@@ -1311,43 +1307,6 @@ static int check_timer(void)
 	return (ts.tv_sec != 0 || ts.tv_nsec != 1);
 }
 
-static void sighand(int sig)
-{
-	if (sig == SIGUSR1) {
-		int i;
-		int oldquiet = quiet;
-
-		quiet = 0;
-		fprintf(stderr, "#---------------------------\n");
-		fprintf(stderr, "# cyclictest current status:\n");
-		for (i = 0; i < num_threads; i++)
-			print_stat(stderr, parameters[i], i, 0, 0);
-		fprintf(stderr, "#---------------------------\n");
-		quiet = oldquiet;
-		return;
-	} else if (sig == SIGUSR2) {
-		int i;
-		int oldquiet = quiet;
-
-		if (rstat_fd == -1) {
-			fprintf(stderr, "ERROR: rstat_fd not valid\n");
-			return;
-		}
-		rstat_ftruncate(rstat_fd, 0);
-		quiet = 0;
-		dprintf(rstat_fd, "#---------------------------\n");
-		dprintf(rstat_fd, "# cyclictest current status:\n");
-		for (i = 0; i < num_threads; i++)
-			rstat_print_stat(parameters[i], i, 0, 0);
-		dprintf(rstat_fd, "#---------------------------\n");
-		quiet = oldquiet;
-		return;
-	}
-	shutdown = 1;
-	if (refresh_on_max)
-		pthread_cond_signal(&refresh_on_max_cond);
-}
-
 static void print_tids(struct thread_param *par[], int nthreads)
 {
 	int i;
@@ -1566,6 +1525,43 @@ static void rstat_print_stat(struct thread_param *par, int index, int verbose, i
 }
 
 
+static void sighand(int sig)
+{
+	if (sig == SIGUSR1) {
+		int i;
+		int oldquiet = quiet;
+
+		quiet = 0;
+		fprintf(stderr, "#---------------------------\n");
+		fprintf(stderr, "# cyclictest current status:\n");
+		for (i = 0; i < num_threads; i++)
+			print_stat(stderr, parameters[i], i, 0, 0);
+		fprintf(stderr, "#---------------------------\n");
+		quiet = oldquiet;
+		return;
+	} else if (sig == SIGUSR2) {
+		int i;
+		int oldquiet = quiet;
+
+		if (rstat_fd == -1) {
+			fprintf(stderr, "ERROR: rstat_fd not valid\n");
+			return;
+		}
+		rstat_ftruncate(rstat_fd, 0);
+		quiet = 0;
+		dprintf(rstat_fd, "#---------------------------\n");
+		dprintf(rstat_fd, "# cyclictest current status:\n");
+		for (i = 0; i < num_threads; i++)
+			rstat_print_stat(parameters[i], i, 0, 0);
+		dprintf(rstat_fd, "#---------------------------\n");
+		quiet = oldquiet;
+		return;
+	}
+	shutdown = 1;
+	if (refresh_on_max)
+		pthread_cond_signal(&refresh_on_max_cond);
+}
+
 /*
  * thread that creates a named fifo and hands out run stats when someone
  * reads from the fifo.
Signature · 1 line
2.32.0
#14 lore raw

Re: [RFC 5/7] rt-tests: cyclictest: Move signal handler to avoid function declaration

Quoted 114 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

---
 src/cyclictest/cyclictest.c | 78 ++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 41 deletions(-)
src/cyclictest/cyclictest.c +37 -41
index 9c67a3ce3034..48a782fe167d 100644
@@ -220,10 +220,6 @@ static char jsonfile[MAX_PATH];
 static struct thread_param **parameters;
 static struct thread_stat **statistics;
 
-static void print_stat(FILE *fp, struct thread_param *par, int index, int verbose, int quiet);
-static void rstat_print_stat(struct thread_param *par, int index, int verbose, int quiet);
-static void rstat_setup(void);
-
 static int latency_target_fd = -1;
 static int32_t latency_target_value = 0;
 
@@ -1311,43 +1307,6 @@ static int check_timer(void)
 	return (ts.tv_sec != 0 || ts.tv_nsec != 1);
 }
 
-static void sighand(int sig)
-{
-	if (sig == SIGUSR1) {
-		int i;
-		int oldquiet = quiet;
-
-		quiet = 0;
-		fprintf(stderr, "#---------------------------\n");
-		fprintf(stderr, "# cyclictest current status:\n");
-		for (i = 0; i < num_threads; i++)
-			print_stat(stderr, parameters[i], i, 0, 0);
-		fprintf(stderr, "#---------------------------\n");
-		quiet = oldquiet;
-		return;
-	} else if (sig == SIGUSR2) {
-		int i;
-		int oldquiet = quiet;
-
-		if (rstat_fd == -1) {
-			fprintf(stderr, "ERROR: rstat_fd not valid\n");
-			return;
-		}
-		rstat_ftruncate(rstat_fd, 0);
-		quiet = 0;
-		dprintf(rstat_fd, "#---------------------------\n");
-		dprintf(rstat_fd, "# cyclictest current status:\n");
-		for (i = 0; i < num_threads; i++)
-			rstat_print_stat(parameters[i], i, 0, 0);
-		dprintf(rstat_fd, "#---------------------------\n");
-		quiet = oldquiet;
-		return;
-	}
-	shutdown = 1;
-	if (refresh_on_max)
-		pthread_cond_signal(&refresh_on_max_cond);
-}
-
 static void print_tids(struct thread_param *par[], int nthreads)
 {
 	int i;
@@ -1566,6 +1525,43 @@ static void rstat_print_stat(struct thread_param *par, int index, int verbose, i
 }
 
 
+static void sighand(int sig)
+{
+	if (sig == SIGUSR1) {
+		int i;
+		int oldquiet = quiet;
+
+		quiet = 0;
+		fprintf(stderr, "#---------------------------\n");
+		fprintf(stderr, "# cyclictest current status:\n");
+		for (i = 0; i < num_threads; i++)
+			print_stat(stderr, parameters[i], i, 0, 0);
+		fprintf(stderr, "#---------------------------\n");
+		quiet = oldquiet;
+		return;
+	} else if (sig == SIGUSR2) {
+		int i;
+		int oldquiet = quiet;
+
+		if (rstat_fd == -1) {
+			fprintf(stderr, "ERROR: rstat_fd not valid\n");
+			return;
+		}
+		rstat_ftruncate(rstat_fd, 0);
+		quiet = 0;
+		dprintf(rstat_fd, "#---------------------------\n");
+		dprintf(rstat_fd, "# cyclictest current status:\n");
+		for (i = 0; i < num_threads; i++)
+			rstat_print_stat(parameters[i], i, 0, 0);
+		dprintf(rstat_fd, "#---------------------------\n");
+		quiet = oldquiet;
+		return;
+	}
+	shutdown = 1;
+	if (refresh_on_max)
+		pthread_cond_signal(&refresh_on_max_cond);
+}
+
 /*
  * thread that creates a named fifo and hands out run stats when someone
  * reads from the fifo.
Signature · 1 line
2.32.0

NACK: What's wrong with function declarations? They allow the freedeom to put your function where you wish. This patch is unnecessary churn, sorry.

#15 lore raw

Re: [RFC 5/7] rt-tests: cyclictest: Move signal handler to avoid function declaration

Hi John,

Quoted 111 lines · John Kacur
Quoted 109 lines · Punit Agrawal

From: Punit Agrawal <[email protected]>

---
 src/cyclictest/cyclictest.c | 78 ++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 41 deletions(-)
src/cyclictest/cyclictest.c +37 -41
index 9c67a3ce3034..48a782fe167d 100644
@@ -220,10 +220,6 @@ static char jsonfile[MAX_PATH];
 static struct thread_param **parameters;
 static struct thread_stat **statistics;
 
-static void print_stat(FILE *fp, struct thread_param *par, int index, int verbose, int quiet);
-static void rstat_print_stat(struct thread_param *par, int index, int verbose, int quiet);
-static void rstat_setup(void);
-
 static int latency_target_fd = -1;
 static int32_t latency_target_value = 0;
 
@@ -1311,43 +1307,6 @@ static int check_timer(void)
 	return (ts.tv_sec != 0 || ts.tv_nsec != 1);
 }
 
-static void sighand(int sig)
-{
-	if (sig == SIGUSR1) {
-		int i;
-		int oldquiet = quiet;
-
-		quiet = 0;
-		fprintf(stderr, "#---------------------------\n");
-		fprintf(stderr, "# cyclictest current status:\n");
-		for (i = 0; i < num_threads; i++)
-			print_stat(stderr, parameters[i], i, 0, 0);
-		fprintf(stderr, "#---------------------------\n");
-		quiet = oldquiet;
-		return;
-	} else if (sig == SIGUSR2) {
-		int i;
-		int oldquiet = quiet;
-
-		if (rstat_fd == -1) {
-			fprintf(stderr, "ERROR: rstat_fd not valid\n");
-			return;
-		}
-		rstat_ftruncate(rstat_fd, 0);
-		quiet = 0;
-		dprintf(rstat_fd, "#---------------------------\n");
-		dprintf(rstat_fd, "# cyclictest current status:\n");
-		for (i = 0; i < num_threads; i++)
-			rstat_print_stat(parameters[i], i, 0, 0);
-		dprintf(rstat_fd, "#---------------------------\n");
-		quiet = oldquiet;
-		return;
-	}
-	shutdown = 1;
-	if (refresh_on_max)
-		pthread_cond_signal(&refresh_on_max_cond);
-}
-
 static void print_tids(struct thread_param *par[], int nthreads)
 {
 	int i;
@@ -1566,6 +1525,43 @@ static void rstat_print_stat(struct thread_param *par, int index, int verbose, i
 }
 
 
+static void sighand(int sig)
+{
+	if (sig == SIGUSR1) {
+		int i;
+		int oldquiet = quiet;
+
+		quiet = 0;
+		fprintf(stderr, "#---------------------------\n");
+		fprintf(stderr, "# cyclictest current status:\n");
+		for (i = 0; i < num_threads; i++)
+			print_stat(stderr, parameters[i], i, 0, 0);
+		fprintf(stderr, "#---------------------------\n");
+		quiet = oldquiet;
+		return;
+	} else if (sig == SIGUSR2) {
+		int i;
+		int oldquiet = quiet;
+
+		if (rstat_fd == -1) {
+			fprintf(stderr, "ERROR: rstat_fd not valid\n");
+			return;
+		}
+		rstat_ftruncate(rstat_fd, 0);
+		quiet = 0;
+		dprintf(rstat_fd, "#---------------------------\n");
+		dprintf(rstat_fd, "# cyclictest current status:\n");
+		for (i = 0; i < num_threads; i++)
+			rstat_print_stat(parameters[i], i, 0, 0);
+		dprintf(rstat_fd, "#---------------------------\n");
+		quiet = oldquiet;
+		return;
+	}
+	shutdown = 1;
+	if (refresh_on_max)
+		pthread_cond_signal(&refresh_on_max_cond);
+}
+
 /*
  * thread that creates a named fifo and hands out run stats when someone
Quoted 8 lines
Quoted 5 lines
src/cyclictest/cyclictest.c +0 -0
  * reads from the fifo.
Signature · 1 line
2.32.0

NACK: What's wrong with function declarations? They allow the freedeom to put your function where you wish. This patch is unnecessary churn, sorry.

I wasn't implying there's anything wrong with a function declaration. They seemed unnecessary and can be easily avoided. But if there's a reason to prefer the current placement of the signal handler (I couldn't see any obvious pattern), I'll drop the patch.

Thanks for comments on the other patches as well. I'll update the set once you've had a chance to go through the rest.

Punit

#16 lore raw

RFC 6/7 rt-tests: cyclictest: Use streaming algorithm to calculate averages

In reply to Punit Agrawal

From: Punit Agrawal <[email protected]>

As part of latency evaluation, cyclictest reports the average latencies observed for the threads. To calculate the average, the aggregate latency of a thread across all samples is tracked and the value calcualted at the end when it is reported.

In preparation for adding support to report standard deviation, update the average calculation to track the streaming average, i.e., averages of the samples observed so far.

Streaming average can be calculated using -

avg = prev_avg + (sample - prev_avg) / #samples

The patch implements this formula and updates the usage of average in the rest of the code.

---
 src/cyclictest/cyclictest.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)
src/cyclictest/cyclictest.c +12 -6
index 48a782fe167d..dde8b1625c62 100644
@@ -616,6 +616,7 @@ static void *timerthread(void *param)
 		uint64_t diff;
 		unsigned long diff_smi = 0;
 		int sigs, ret;
+		double prev_avg;
 
 		/* Wait for next period */
 		switch (par->mode) {
@@ -702,7 +703,6 @@ static void *timerthread(void *param)
 			if (refresh_on_max)
 				pthread_cond_signal(&refresh_on_max_cond);
 		}
-		stat->avg += (double) diff;
 
 		if (trigger && (diff > trigger))
 			trigger_update(par, diff, calctime(now));
@@ -742,6 +742,14 @@ static void *timerthread(void *param)
 
 		stat->cycles++;
 
+		/*
+		 * Calculate the streaming average
+		 *
+		 * avg = avg' + (x - avg') / n
+		 */
+		prev_avg = stat->avg;
+		stat->avg = prev_avg + ((diff - prev_avg) / stat->cycles);
+
 		next.tv_sec += interval.tv_sec;
 		next.tv_nsec += interval.tv_nsec;
 		if (par->mode == MODE_CYCLIC) {
@@ -1429,8 +1437,7 @@ static void print_stat(FILE *fp, struct thread_param *par, int index, int verbos
 
 			fprintf(fp, fmt, index, stat->tid, par->prio,
 				par->interval, stat->cycles, stat->min,
-				stat->act, stat->cycles ?
-				(long)(stat->avg/stat->cycles) : 0, stat->max);
+				stat->act, (long)stat->avg, stat->max);
 
 			if (smi)
 				fprintf(fp, " SMI:%8ld", stat->smi_count);
@@ -1485,8 +1492,7 @@ static void rstat_print_stat(struct thread_param *par, int index, int verbose, i
 
 			dprintf(fd, fmt, index, stat->tid, par->prio,
 				par->interval, stat->cycles, stat->min,
-				stat->act, stat->cycles ?
-				(long)(stat->avg/stat->cycles) : 0, stat->max);
+				stat->act, (long)stat->avg, stat->max);
 
 			if (smi)
 				dprintf(fd, " SMI:%8ld", stat->smi_count);
@@ -1774,7 +1780,7 @@ static void write_stats(FILE *f, void *data)
 		fprintf(f, "      \"cycles\": %ld,\n", s->cycles);
 		fprintf(f, "      \"min\": %ld,\n", s->min);
 		fprintf(f, "      \"max\": %ld,\n", s->max);
-		fprintf(f, "      \"avg\": %.2f,\n", s->avg/s->cycles);
+		fprintf(f, "      \"avg\": %.2f,\n", s->avg);
 		fprintf(f, "      \"cpu\": %d,\n", par[i]->cpu);
 		fprintf(f, "      \"node\": %d\n", par[i]->node);
 		fprintf(f, "    }%s\n", i == num_threads - 1 ? "" : ",");
Signature · 1 line
2.32.0
#17 lore raw

RFC 7/7 rt-tests: cyclictest: Add support to report standard deviation

In reply to Punit Agrawal

From: Punit Agrawal <[email protected]>

For real time systems, the standard deviation is an important metric to help qualify the suitability for low-latency workloads. Downstream tools such as rteval calculate the value of the standard deviation based on the latency histogram but this approach doesn't work in situations when the max latency exceeds the range of the histogram.

The common formula to calculating standard deviation requires tracking all samples which is not feasible. But an alternative approach is to track the streaming standard deviation of the latency samples. With this it is possible to update the standard deviation using -

         d = d' + (x - avg)(x - avg')

    stddev = sqrt (d / (n - 1))

where 'd' is similar to the variance, 'x' is the current latency sample, 'n' is the number of samples and prime terms (') refer to the previous values.

Implement the above algorithm to calculate the standard deviation and update the reporting to also include standard deviation.

---
 Makefile                    |  2 +-
 src/cyclictest/cyclictest.c | 43 +++++++++++++++++++++++++++++++------
 2 files changed, 38 insertions(+), 7 deletions(-)
Makefile +1 -1
index ec5d693436d0..3f08788c0208 100644
@@ -21,7 +21,7 @@ sources = cyclictest.c \
 	  oslat.c
 
 TARGETS = $(sources:.c=)
-LIBS	= -lrt -lpthread
+LIBS	= -lrt -lpthread -lm
 RTTESTLIB = -lrttest -L$(OBJDIR)
 EXTRA_LIBS ?= -ldl	# for get_cpu
 RTTESTNUMA = -lrttestnuma -lnuma
src/cyclictest/cyclictest.c +37 -6

107 lines not on this page · raw message

Signature · 1 line
2.32.0
#18 lore raw

Hi Punit,

src/cyclictest/cyclictest.c static void write_stats(FILE *f, void *data) · line 1816

Quoted 8 lines · Punit Agrawal
src/cyclictest/cyclictest.c +1 -0
@@ -1781,6 +1810,7 @@ static void write_stats(FILE *f, void *data)
 		fprintf(f, "      \"min\": %ld,\n", s->min);
 		fprintf(f, "      \"max\": %ld,\n", s->max);
 		fprintf(f, "      \"avg\": %.2f,\n", s->avg);
+		fprintf(f, "      \"stddev\": %.2f,\n", calc_stddev(s));
 		fprintf(f, "      \"cpu\": %d,\n", par[i]->cpu);
 		fprintf(f, "      \"node\": %d\n", par[i]->node);
 		fprintf(f, "    }%s\n", i == num_threads - 1 ? "" : ",");

I think in this case you should also increase the version number of the JSON file. And while at it, I'd love to see the same stats fixes/extension for the other tools, not just cyclictest.

Daniel

#19 lore raw
Quoted 14 lines · Daniel Wagner

Hi Punit,

src/cyclictest/cyclictest.c static void write_stats(FILE *f, void *data) · line 1816

Quoted 8 lines · Punit Agrawal
src/cyclictest/cyclictest.c +1 -0
@@ -1781,6 +1810,7 @@ static void write_stats(FILE *f, void *data)
 		fprintf(f, "      \"min\": %ld,\n", s->min);
 		fprintf(f, "      \"max\": %ld,\n", s->max);
 		fprintf(f, "      \"avg\": %.2f,\n", s->avg);
+		fprintf(f, "      \"stddev\": %.2f,\n", calc_stddev(s));
 		fprintf(f, "      \"cpu\": %d,\n", par[i]->cpu);
 		fprintf(f, "      \"node\": %d\n", par[i]->node);
 		fprintf(f, "    }%s\n", i == num_threads - 1 ? "" : ",");

I think in this case you should also increase the version number of the JSON file.

Sure, I can bump up the version to 1.1 - minor update as it's adding a new field rather than changing existing ones. Or "2" if that is preferred.

Taking a closer look, the rt_write_json() helper, that outputs the version, is shared with other tools. Bumping the version will bump it up for all of them - would that be OK?

Quoted 2 lines

And while at it, I'd love to see the same stats fixes/extension for the other tools, not just cyclictest.

I can look at adding support for standard deviation to the other tools if there's interest once the cyclictest changes have been merged.

Thanks for taking a look.

Punit

#20 lore raw
Quoted 3 lines · Punit Agrawal

Sure, I can bump up the version to 1.1 - minor update as it's adding a new field rather than changing existing ones. Or "2" if that is preferred.

I'd say we just increment the number to 2 and don't any semantic meaning to the version number. Instead push the compatible checks to the user side. I suspect many don't care anyway.

BTW, I've noticed we print the version number wrong. Currently, 'cyclictest --version' will print 'V 2.20' instead 'V 2.2'. The printf formatting string is wrong. Same in the JSON output. If you don't mind you could update this well when you are updating file version string. Thanks!

Quoted 2 lines

I can look at adding support for standard deviation to the other tools if there's interest once the cyclictest changes have been merged.

Yeah, there a couple of tests which quite similar. So you might just have to move the newly added code to util.h(?) and add the calls to the tests.. It shouldn't be too difficult just a bit of repetitive work. Probably a good starting point for an intern :)

Quoted 1 line

Thanks for taking a look.

Thanks for contributing!

Daniel

#21 lore raw
In reply to Punit Agrawal
Quoted 41 lines · Punit Agrawal

Hi,

Standard deviation is an important measure to evaluate the latency response of a real time system. It is calculated by downstream tools such as rteval but the value cannot be correctly calculated when the latency value exceeds the range of histogram.

This series adds support to report the streaming standard deviation of latencies to cyclictest. This approach avoids having to track all latency values and adds minimal overhead (two additional doubles and compute steps) to each iteration.

In terms of patch organization, the first 5 patches are clean-ups that were noticed in the course of developing the support for standard deviation. It should be possible to apply them indepndently.

Patch 6 converts the existing average calculation to a streaming version. This running average value is needed for the standard deviation.

Patch 7 finally adds support for calculating standard deviation.

The changes were verified by capturing the latency samples and verifying the average and standard deviation with manual calculation.

All feedback welcome.

Thanks,
Punit

Punit Agrawal (7):
rt-tests: cyclictest: Drop unused defines
rt-tests: cyclictest: Simplify duplicate initialization of "stop"
rt-tests: cyclictest: Drop unnecessary variable "stopped"
rt-tests: cyclictest: Drop unnecessary variable "bufsize"
rt-tests: cyclictest: Move signal handler to avoid function
declaration
rt-tests: cyclictest: Use streaming algorithm to calculate averages
rt-tests: cyclictest: Add support to report standard deviation

Quoted 7 lines
 Makefile                    |   2 +-
 src/cyclictest/cyclictest.c | 149 +++++++++++++++++++++---------------
 2 files changed, 87 insertions(+), 64 deletions(-)

--
2.32.0

Hi Punit,

Thanks for your contributions!

I noticed there were no commit messages for patches 1 and 5.

Would it be possible to add commit messages to all patches in the future? Commit messages help those trying to learn (Me :-) ) and understand the purpose of the patch.

Thanks,

Joe

#22 lore raw

Hi Joseph,

Quoted 50 lines · Joseph Salisbury
Quoted 41 lines · Punit Agrawal

Hi,

Standard deviation is an important measure to evaluate the latency response of a real time system. It is calculated by downstream tools such as rteval but the value cannot be correctly calculated when the latency value exceeds the range of histogram.

This series adds support to report the streaming standard deviation of latencies to cyclictest. This approach avoids having to track all latency values and adds minimal overhead (two additional doubles and compute steps) to each iteration.

In terms of patch organization, the first 5 patches are clean-ups that were noticed in the course of developing the support for standard deviation. It should be possible to apply them indepndently.

Patch 6 converts the existing average calculation to a streaming version. This running average value is needed for the standard deviation.

Patch 7 finally adds support for calculating standard deviation.

The changes were verified by capturing the latency samples and verifying the average and standard deviation with manual calculation.

All feedback welcome.

Thanks,
Punit

Punit Agrawal (7):
rt-tests: cyclictest: Drop unused defines
rt-tests: cyclictest: Simplify duplicate initialization of "stop"
rt-tests: cyclictest: Drop unnecessary variable "stopped"
rt-tests: cyclictest: Drop unnecessary variable "bufsize"
rt-tests: cyclictest: Move signal handler to avoid function
declaration
rt-tests: cyclictest: Use streaming algorithm to calculate averages
rt-tests: cyclictest: Add support to report standard deviation

Quoted 7 lines
 Makefile                    |   2 +-
 src/cyclictest/cyclictest.c | 149 +++++++++++++++++++++---------------
 2 files changed, 87 insertions(+), 64 deletions(-)

--
2.32.0

Quoted 5 lines

Hi Punit,

Thanks for your contributions!

I noticed there were no commit messages for patches 1 and 5.

If you look closely, patch 1 does have a commit log.

I missed adding a commit log for patch 5 - but the gist of it is to drop function declarations when they are not required. In this case, this is achieved by having the caller defined after all the callees have been defined.

I'll add a log if the patch gets reposted.

Thanks for taking a look.

Punit

[...]