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 Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users