Currently pg_test_timing utility measures its timing overhead in
microseconds, giving results like this

~$ /usr/lib/postgresql/15/bin/pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 18.97 ns
Histogram of timing durations:
  < us   % of total      count
     1     98.11132  155154419
     2      1.88756    2985010
     4      0.00040        630
     8      0.00012        184
    16      0.00058        919
    32      0.00003         40
    64      0.00000          6

I got curious and wanted to see how the 98.1% timings are distributed
(raw uncleaned patch attached)
And this is what I got when I increased the measuring resolution to nanoseconds

hannuk@hannuk1:~/work/postgres15_uni_dist_on/src/bin/pg_test_timing$
./pg_test_timing
Testing timing overhead for 3 seconds.
Per loop time including overhead: 17.34 ns, min: 15, same: 0
Histogram of timing durations:
   < ns   % of total      count
      1      0.00000          0
      2      0.00000          0
      4      0.00000          0
      8      0.00000          0
     16      1.14387    1979085
     32     98.47924  170385392
     64      0.21666     374859
    128      0.15654     270843
    256      0.00297       5139
    512      0.00016        272
   1024      0.00004         73
   2048      0.00018        306
   4096      0.00022        375
   8192      0.00006         99
  16384      0.00005         80
  32768      0.00001         20
  65536      0.00000          6
 131072      0.00000          2

As most of the samples seems to be in ranges 8..15 and 16..32
nanoseconds the current way of measuring at microsecond resolution is
clearly inadequate.

The attached patch is not meant to be applied as-is but is rather
there as a helper to easily verify the above numbers.


QUESTIONS

1. Do you think it is ok to just change pg_test_timing to return the
result in nanoseconds or should there be a flag that asks for
nanosecond resolution ?

2. Should the output be changed to give ranges instead of `<ns`
numbers for better clarity, and leave out the "too small numbers" from
the beginning as well ?

   So the first few lines would look like
    8 .. 15 ....
   16 .. 32 .....
....


---
Best Regards,
Hannu
*** pg_test_timing.c.orig	2023-01-16 01:09:51.839251695 +0100
--- pg_test_timing.c	2023-01-16 22:24:49.768037225 +0100
***************
*** 11,16 ****
--- 11,20 ----
  #include "getopt_long.h"
  #include "portability/instr_time.h"
  
+ #define INSTR_TIME_GET_NANOSEC(t) \
+ 	(((uint64) (t).tv_sec * (uint64) 1000000000) + (uint64) ((t).tv_nsec))
+ 
+ 
  static const char *progname;
  
  static unsigned int test_duration = 3;
***************
*** 19,26 ****
  static uint64 test_timing(unsigned int duration);
  static void output(uint64 loop_count);
  
! /* record duration in powers of 2 microseconds */
! long long int histogram[32];
  
  int
  main(int argc, char *argv[])
--- 23,30 ----
  static uint64 test_timing(unsigned int duration);
  static void output(uint64 loop_count);
  
! /* record duration in powers of 2 nanoseconds */
! long long int histogram[64];
  
  int
  main(int argc, char *argv[])
***************
*** 124,139 ****
  	uint64		total_time;
  	int64		time_elapsed = 0;
  	uint64		loop_count = 0;
  	uint64		prev,
  				cur;
  	instr_time	start_time,
  				end_time,
  				temp;
  
! 	total_time = duration > 0 ? duration * INT64CONST(1000000) : 0;
  
  	INSTR_TIME_SET_CURRENT(start_time);
! 	cur = INSTR_TIME_GET_MICROSEC(start_time);
  
  	while (time_elapsed < total_time)
  	{
--- 128,145 ----
  	uint64		total_time;
  	int64		time_elapsed = 0;
  	uint64		loop_count = 0;
+ 	uint64		same_count = 0;
+ 	uint64		min_diff = UINT64_MAX;
  	uint64		prev,
  				cur;
  	instr_time	start_time,
  				end_time,
  				temp;
  
! 	total_time = duration > 0 ? duration * INT64CONST(1000000000) : 0;
  
  	INSTR_TIME_SET_CURRENT(start_time);
! 	cur = INSTR_TIME_GET_NANOSEC(start_time);
  
  	while (time_elapsed < total_time)
  	{
***************
*** 141,157 ****
  					bits = 0;
  
  		prev = cur;
  		INSTR_TIME_SET_CURRENT(temp);
! 		cur = INSTR_TIME_GET_MICROSEC(temp);
  		diff = cur - prev;
  
  		/* Did time go backwards? */
! 		if (diff < 0)
  		{
  			fprintf(stderr, _("Detected clock going backwards in time.\n"));
  			fprintf(stderr, _("Time warp: %d ms\n"), diff);
  			exit(1);
  		}
  
  		/* What is the highest bit in the time diff? */
  		while (diff)
--- 147,172 ----
  					bits = 0;
  
  		prev = cur;
+ //		INSTR_TIME_SET_CURRENT(temp);
+ //		prev = INSTR_TIME_GET_NANOSEC(temp);
  		INSTR_TIME_SET_CURRENT(temp);
! 		cur = INSTR_TIME_GET_NANOSEC(temp);
  		diff = cur - prev;
  
  		/* Did time go backwards? */
! 		if (unlikely(diff <= 0))
  		{
+ 			if (diff == 0 )
+ 				same_count ++;
+ 			else
+ 			{
  			fprintf(stderr, _("Detected clock going backwards in time.\n"));
  			fprintf(stderr, _("Time warp: %d ms\n"), diff);
  			exit(1);
+ 			}
  		}
+ 		if (min_diff > diff)
+ 			min_diff = diff;
  
  		/* What is the highest bit in the time diff? */
  		while (diff)
***************
*** 165,179 ****
  
  		loop_count++;
  		INSTR_TIME_SUBTRACT(temp, start_time);
! 		time_elapsed = INSTR_TIME_GET_MICROSEC(temp);
  	}
  
  	INSTR_TIME_SET_CURRENT(end_time);
  
  	INSTR_TIME_SUBTRACT(end_time, start_time);
  
! 	printf(_("Per loop time including overhead: %0.2f ns\n"),
! 		   INSTR_TIME_GET_DOUBLE(end_time) * 1e9 / loop_count);
  
  	return loop_count;
  }
--- 180,194 ----
  
  		loop_count++;
  		INSTR_TIME_SUBTRACT(temp, start_time);
! 		time_elapsed = INSTR_TIME_GET_NANOSEC(temp);
  	}
  
  	INSTR_TIME_SET_CURRENT(end_time);
  
  	INSTR_TIME_SUBTRACT(end_time, start_time);
  
! 	printf(_("Per loop time including overhead: %0.2f ns, min: %ld, same: %ld\n"),
! 		   INSTR_TIME_GET_DOUBLE(end_time) * 1e9 / loop_count, min_diff, same_count);
  
  	return loop_count;
  }
***************
*** 181,189 ****
  static void
  output(uint64 loop_count)
  {
! 	int64		max_bit = 31,
  				i;
! 	char	   *header1 = _("< us");
  	char	   *header2 = /* xgettext:no-c-format */ _("% of total");
  	char	   *header3 = _("count");
  	int			len1 = strlen(header1);
--- 196,204 ----
  static void
  output(uint64 loop_count)
  {
! 	int64		max_bit = 63,
  				i;
! 	char	   *header1 = _("< ns");
  	char	   *header2 = /* xgettext:no-c-format */ _("% of total");
  	char	   *header3 = _("count");
  	int			len1 = strlen(header1);
***************
*** 196,208 ****
  
  	printf(_("Histogram of timing durations:\n"));
  	printf("%*s   %*s %*s\n",
! 		   Max(6, len1), header1,
  		   Max(10, len2), header2,
  		   Max(10, len3), header3);
  
  	for (i = 0; i <= max_bit; i++)
  		printf("%*ld    %*.5f %*lld\n",
! 			   Max(6, len1), 1l << i,
  			   Max(10, len2) - 1, (double) histogram[i] * 100 / loop_count,
  			   Max(10, len3), histogram[i]);
  }
--- 211,223 ----
  
  	printf(_("Histogram of timing durations:\n"));
  	printf("%*s   %*s %*s\n",
! 		   Max(7, len1), header1,
  		   Max(10, len2), header2,
  		   Max(10, len3), header3);
  
  	for (i = 0; i <= max_bit; i++)
  		printf("%*ld    %*.5f %*lld\n",
! 			   Max(7, len1), 1l << i,
  			   Max(10, len2) - 1, (double) histogram[i] * 100 / loop_count,
  			   Max(10, len3), histogram[i]);
  }

Reply via email to