Best regards, Scott
-- Scott Lamb <http://www.slamb.org/>
<<inline: one-active.png>>
Add a chart generator.
Also clean up what appears to be a totally extraneous error output from
bench.c.
Index: test/chartbench.py
===================================================================
--- test/chartbench.py (revision 0)
+++ test/chartbench.py (revision 0)
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+"""
+%prog [options]
+
+Creates libevent benchmark charts.
+
+Requirements:
+* Python >= 2.4 for the subprocess module
+* test-init and bench in current directory
+* gnuplot with png output format (and optionally freetype support)
+"""
+
+from __future__ import division
+
+import optparse
+import os
+import subprocess
+import sys
+import traceback
+
+ALL_METHODS = 'kqueue devpoll poll select epoll evport'.split(' ')
+
+STYLE_MAP = {
+ 'candlesticks': 'using 1:3:2:6:5 with candlesticks linewidth 2',
+ 'lines': 'using 1:4 with lines linewidth 2 smooth csplines',
+}
+
+def get_environment(method):
+ """Returns an environment that makes libevent use the specified method."""
+ env = {}
+ for i in ALL_METHODS:
+ if i != method:
+ env['EVENT_NO%s' % i.upper()] = 'yes'
+ return env
+
+def median(data):
+ """Returns the median of a sorted list of numbers."""
+ n = len(data)
+ if n % 2 == 0:
+ return (data[n//2] + data[n//2+1]) / 2
+ else:
+ return data[n//2]
+
+def run_bench(method, pipes, active, writes):
+ """Gets candestick bounds for a given test.
+
+ Returns (minimum, first quartile, median, third quartile, maximum).
+ """
+ print 'run_bench(%r, %d, %d, %d)' % (method, pipes, active, writes)
+
+ # Get the data.
+ bench = subprocess.Popen(('./bench -n %d -a %d -w %d'
+ % (pipes, active, writes)).split(' '),
+ bufsize=1, stdout=subprocess.PIPE,
+ env=get_environment(method))
+ data = [float(line) for line in bench.stdout]
+ retval = bench.wait()
+ if retval != 0:
+ raise Exception('bench returned %d' % retval)
+ if not data:
+ raise Exception('bench returned insufficient data')
+
+ # Now get our statistics.
+ data.sort()
+ return (data[0],
+ median(data[0:len(data)//2]),
+ median(data),
+ median(data[len(data)//2:]),
+ data[-1])
+
+def escape(literal):
+ """Escapes the given literal for use in gnuplot control channel.
+
+ See gnuplot's "help syntax" for details."""
+ return ('"%s"' % literal.replace('\\', '\\\\')
+ .replace('"', '\\"')
+ .replace('\n', '\\n'))
+
+def plot(filename, title, options, methods, active, writes):
+ """Creates the plot described by the arguments."""
+
+ # Launch gnuplot. It wants to poke around at my environment, so I won't
+ # give it one. (At least if it gets an unusable $DISPLAY, it's unhappy.)
+ if options.store_commands:
+ gnuplot_out = open('%s.plot' % filename, 'w')
+ else:
+ gnuplot = subprocess.Popen(['gnuplot'], stdin=subprocess.PIPE,
+ env={'PATH': os.environ['PATH']})
+ gnuplot_out = gnuplot.stdin
+
+ if options.font is None:
+ gnuplot_out.write('set terminal png\n')
+ else:
+ gnuplot_out.write('set terminal png font %s %d\n'
+ % (escape(options.font), options.fontsize))
+ gnuplot_out.write("""
+ set output %(filename)s
+ set title %(title)s
+ set xlabel "Inactive file descriptors"
+ set ylabel "Time (\xC2\xB5s)"
+ set palette color
+ set style fill solid
+ set key top left
+ set logscale y 10
+ set style line 1 lc rgb "#888888"
+ set border 3 linestyle 1
+ set xtics out nomirror textcolor rgb "#000000"
+ set ytics out nomirror textcolor rgb "#000000"
+ set grid
+ """ % {
+ 'title': escape(title),
+ 'filename': escape(filename),
+ })
+ gnuplot_out.write('plot [0:] [] ' +
+ ', '.join(['"-" %s title %s'
+ % (STYLE_MAP[options.style], escape(method))
+ for method in methods]) +
+ '\n')
+
+ # Write data for each method.
+ for method in methods:
+ for i in range(0, options.max+options.step, options.step):
+ stats = run_bench(method=method,
+ pipes=i+active,
+ active=active,
+ writes=writes)
+ gnuplot_out.write('%d %g %g %g %g %g\n' % tuple((i,) + stats))
+ gnuplot_out.write('e\n')
+
+ # Finish up.
+ if not options.store_commands:
+ gnuplot_out.close()
+ retval = gnuplot.wait()
+ if retval != 0:
+ raise Exception('gnuplot failure')
+
+def main(args):
+ # Parse arguments.
+ parser = optparse.OptionParser(usage=__doc__)
+ parser.add_option('-s', '--step', dest='step', default=250, type='int',
+ help='plot a point every STEP file descriptors')
+ parser.add_option('-m', '--max', dest='max', default=25000, type='int',
+ help='maximum number of file descriptors')
+ parser.add_option('--style', dest='style', default='candlesticks',
+ help='plot style', choices=STYLE_MAP.keys())
+ parser.add_option('--font', dest='font',
+ help='path to TTF file (requires gnuplot support)')
+ parser.add_option('--font-size', dest='fontsize', default=10, type='int',
+ help='in points; only used if FONT is specified')
+ parser.add_option('--store-commands', dest='store_commands', default=False,
+ help='store commands rather than executing gnuplot',
+ action='store_true')
+ options, args = parser.parse_args()
+ if len(args) != 0:
+ parser.error('No positional arguments expected')
+
+ # Determine what methods are supported.
+ supported_methods = []
+ for method in ALL_METHODS:
+ if subprocess.call('./test-init', env=get_environment(method)) == 0:
+ supported_methods.append(method)
+ if not supported_methods:
+ raise Exception('No supported methods')
+ print 'supported methods: %s' % ' '.join(supported_methods)
+
+ # Plot.
+ plot(filename='one-active.png',
+ title='1 active connection and 1 write',
+ options=options,
+ methods=supported_methods,
+ active=1,
+ writes=1)
+ plot(filename='many-active.png',
+ title='100 active connections and 1000 writes',
+ options=options,
+ methods=supported_methods,
+ active=100,
+ writes=1000)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
Property changes on: test/chartbench.py
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Index: test/bench.c
===================================================================
--- test/bench.c (revision 621)
+++ test/bench.c (working copy)
@@ -102,17 +102,12 @@
count = 0;
writes = num_writes;
- { int xcount = 0;
gettimeofday(&ts, NULL);
do {
event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
- xcount++;
} while (count != fired);
gettimeofday(&te, NULL);
- if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n",
xcount, count);
- }
-
evutil_timersub(&te, &ts, &te);
return (&te);
_______________________________________________ Libevent-users mailing list [email protected] http://monkeymail.org/mailman/listinfo/libevent-users
