Hi list,

I had a short conversation with Kimo about sysctlnametomib() versus 
sysctlbyname() [1] and I wanted to check if there is really a difference. At 
the same time I 'benchmarked' some other plugins. I think that a tool like 
this might aid in collectd plugin development.

I added a -b <N> flag to the collectd which runs "benchmark" by iterating the 
read loop N times and calculates total CPU time. To collect results I wrote a 
simple Makefile script. 

I ran benchmarks on a FreeBSD 8.2 system. I ran 5 instances of collectd 
simultaneously and every instance ran 100000 iterations. The collectd was 
configured to use only the plugin in question. I set the time interval as 
small as possible so that  there weren't too many 'Not sleeping because the 
next interval is 0.000 seconds in the past!' reports. 

Results (seconds of CPU time):
         Plugin:    Mean:    Variance:
         cpu.in   10.03281    0.00035
   cs-sysctl.in    3.49531    0.00312 (uses sysctlnametomib() and sysctl())
          cs.in    3.74687    0.00043 (uses sysctlbyname())
        disk.in    8.10156    0.00020
   interface.in   13.22031    0.00062
        load.in    3.31562    0.00556
      memory.in    8.67500    0.00041
        swap.in    1.82500    0.00761
    tcpconns.in   16.11094    0.00162
      uptime.in    3.16562    0.00160
       users.in   13.93281    0.00080
     zfs-arc.in   16.66094    0.00294 (modified to use sysctlbyname())
     zfs-arc.in   13.26250    0.00158 (mod. sysctlnametomib() and sysctl())

We can see that plugins use different amount of CPU. This is not real 
benchmark because values are not comparable to those produced on other 
systems. However we can easily calculate that if we use 30 seconds time 
interval 100000 iterations would mean 34 hours of real time, and we can see 
that some plugins are quite efficient.

I would like to point out two things about sysctl calls.

First, lets compare ContextSwitch plugin and my sysctl plugin [2]. It seems 
that when a plugin uses only one sysctl entry there is no significant 
difference between sysctlnametomib() and sysctlbyname(). Maybe collectd 
overhead is larger that the small performance gain. 

Second, we can see that zfs-arc plugin is faster when sysctlnametomib() is 
used. The plugin reads 16 different sysctl values on every read action and now 
the difference is noticeable.

There are two attachments:
- collectd-bench.patch (patch against version 5.0.0)
- time-suite.tgz
I didn't post zfs-arc plugin patches because they are poor quality. I will 
return to this later.

Thanks for your time.
Toni Ylenius

[1] http://mailman.verplant.org/pipermail/collectd/2011-June/004542.html
[2] https://collectd.org/gerrit/#change,10
commit 4d24d3d4a2a6cdd5d71afdabe0ac6cfb92b49c81
Author: Toni Ylenius <toni.ylen...@iki.fi>
Date:   Sat Sep 3 22:49:17 2011 +0300

    Added benchmark -b flag to collectd.

diff --git a/configure.in b/configure.in
index 8db24ca..d7d17ca 100644
--- a/configure.in
+++ b/configure.in
@@ -126,7 +126,7 @@ AC_HEADER_SYS_WAIT
 AC_HEADER_DIRENT
 AC_HEADER_STDBOOL
 
-AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h)
+AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h time.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h)
 
 # For ping library
 AC_CHECK_HEADERS(netinet/in_systm.h, [], [],
diff --git a/src/collectd.c b/src/collectd.c
index d33d1d6..94ac319 100644
--- a/src/collectd.c
+++ b/src/collectd.c
@@ -18,6 +18,7 @@
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
  *   Alvaro Barcellos <alvaro.barcellos at gmail.com>
+ *   Toni Ylenius <toni.ylenius at iki.fi>
  **/
 
 #include "collectd.h"
@@ -25,6 +26,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <time.h>
 #include <netdb.h>
 
 #include <pthread.h>
@@ -47,6 +49,7 @@ kstat_ctl_t *kc;
 #endif /* HAVE_LIBKSTAT */
 
 static int loop = 0;
+static int iter = -1;
 
 static void *do_flush (void __attribute__((unused)) *arg)
 {
@@ -277,6 +280,7 @@ static void exit_usage (int status)
 			"                    Default: "CONFIGFILE"\n"
 			"    -t              Test config and exit.\n"
 			"    -T              Test plugin read and exit.\n"
+			"    -b <n>          Benchmark with <n> iterations.\n"
 			"    -P <file>       PID-file.\n"
 			"                    Default: "PIDFILE"\n"
 #if COLLECT_DAEMON
@@ -332,6 +336,13 @@ static int do_loop (void)
 		struct timespec ts_wait = { 0, 0 };
 		cdtime_t now;
 
+
+		/* Stop looping when max iterations is reached */
+		if (iter >= 0 && iter == 0)
+			return(0);
+		else if (iter >= 0)
+			iter--;
+
 #if HAVE_LIBKSTAT
 		update_kstat ();
 #endif
@@ -412,6 +423,9 @@ int main (int argc, char **argv)
 	char *configfile = CONFIGFILE;
 	int test_config  = 0;
 	int test_readall = 0;
+	int iter_times = 0; /* 0 = do not benchmark */
+	clock_t start_clk = 0;
+	clock_t end_clk = 0;
 	const char *basedir;
 #if COLLECT_DAEMON
 	struct sigaction sig_chld_action;
@@ -425,7 +439,7 @@ int main (int argc, char **argv)
 	{
 		int c;
 
-		c = getopt (argc, argv, "htTC:"
+		c = getopt (argc, argv, "htb:TC:"
 #if COLLECT_DAEMON
 				"fP:"
 #endif
@@ -449,6 +463,12 @@ int main (int argc, char **argv)
 				daemonize = 0;
 #endif /* COLLECT_DAEMON */
 				break;
+			case 'b':
+				iter_times = atoi (optarg);
+#if COLLECT_DAEMON
+				daemonize = 0;
+#endif /* COLLECT_DAEMON */
+				break;
 #if COLLECT_DAEMON
 			case 'P':
 				global_option_set ("PIDFile", optarg);
@@ -508,6 +528,15 @@ int main (int argc, char **argv)
 	if (test_config)
 		return (0);
 
+	if (iter_times < 0)
+	{
+		fprintf (stderr, "Benchmark iteration number should be a "
+                      "positive number.\n");
+		return (1);
+	}
+	else if (iter_times > 0)
+		iter = iter_times;
+
 #if COLLECT_DAEMON
 	/*
 	 * fork off child
@@ -601,6 +630,10 @@ int main (int argc, char **argv)
 	/*
 	 * run the actual loops
 	 */
+	if (iter_times > 0)
+       	{
+		start_clk = clock();
+	}
 	do_init ();
 
 	if (test_readall)
@@ -618,6 +651,12 @@ int main (int argc, char **argv)
 	INFO ("Exiting normally.");
 
 	do_shutdown ();
+	if (iter_times > 0)
+       	{
+		end_clk = clock();
+		INFO ("CPU time used: %.6f",
+		      ((double) (end_clk - start_clk)) / CLOCKS_PER_SEC);
+	}
 
 #if COLLECT_DAEMON
 	if (daemonize)
diff --git a/src/collectd.pod b/src/collectd.pod
index e36dcdf..cf7f7ea 100644
--- a/src/collectd.pod
+++ b/src/collectd.pod
@@ -36,6 +36,11 @@ config file. A return code not equal to zero indicates an error.
 Test the plugin read callbacks only. The program immediately exits after invoking
 the read callbacks once. A return code not equal to zero indicates an error.
 
+=item B<-b> I<E<lt>nE<gt>>
+
+Runs benchmark with B<n> number of iterations and exists. Does not daemonize. 
+After the execution prints used CPU time.
+
 =item B<-P> I<E<lt>pid-fileE<gt>>
 
 Specify an alternative pid file. This overwrites any settings in the config 

Attachment: time-suite.tgz
Description: application/compressed-tar

_______________________________________________
collectd mailing list
collectd@verplant.org
http://mailman.verplant.org/listinfo/collectd

Reply via email to