diff options
Diffstat (limited to 'gnuradio-examples/python/volk_benchmark')
-rw-r--r-- | gnuradio-examples/python/volk_benchmark/README | 252 | ||||
-rwxr-xr-x | gnuradio-examples/python/volk_benchmark/volk_math.py | 152 | ||||
-rwxr-xr-x | gnuradio-examples/python/volk_benchmark/volk_plot.py | 169 | ||||
-rw-r--r-- | gnuradio-examples/python/volk_benchmark/volk_test_funcs.py | 171 | ||||
-rwxr-xr-x | gnuradio-examples/python/volk_benchmark/volk_types.py | 182 |
5 files changed, 926 insertions, 0 deletions
diff --git a/gnuradio-examples/python/volk_benchmark/README b/gnuradio-examples/python/volk_benchmark/README new file mode 100644 index 000000000..516fc15bd --- /dev/null +++ b/gnuradio-examples/python/volk_benchmark/README @@ -0,0 +1,252 @@ +VOLK Benchmarking Scripts + +The Python programs in this directory are designed to help benchmark +and compare Volk enhancements to GNU Radio. There are two kinds of +scripts here: collecting data and displaying the data. + +Data collection is done by running a Volk testing script that will +populate a SQLite database file (volk_results.db by default). The +plotting utility provided here reads from the database files and plots +bar graphs to compare the different installations. + +These benchmarks can be used to compare previous versions of GNU +Radio to using Volk; they can be used to compare different Volk +proto-kernels, as well, by editing the volk_config file; or they could +be used to compare performance between different machines and/or +processors. + + +====================================================================== +Volk Profiling + +Before doing any kind of Volk benchmarking, it is important to run the +volk_profile program. The profiler will build a config file for the +best SIMD architecture for your processor. Run volk_profile that is +installed into $PREFIX/bin. This program tests all known Volk kernels +for each proto-kernel supported by the processor. When finished, it +will write to $HOME/.volk/volk_config the best architecture for the +VOLK function. This file is read when using a function to know the +best version of the function to execute. + +The volk_config file contains a line for each kernel, where each line +looks like: + + volk_<KERNEL_NAME> <ARCHITECTURE> + +The architecture will be something like (sse, sse2, sse3, avx, neon, +etc.), depending on your processor. + + +====================================================================== +Benchmark Tests + +There are currently two benchmark scripts defined for collecting +data. There is one that runs through the type conversions that have +been converted to Volk (volk_types.py) and the other runs through the +math operators converted to using Volk (volk_math.py). + +Script prototypes +Both have the same structure for use: + +---------------------------------------------------------------------- +./volk_<test>.py [-h] -L LABEL [-D DATABASE] [-N NITEMS] [-I ITERATIONS] + [--tests [{0,1,2,3} [{0,1,2,3} ...]]] [--list] + [--all] + +optional arguments: + -h, --help show this help message and exit + -L LABEL, --label LABEL + Label of database table [default: None] + -D DATABASE, --database DATABASE + Database file to store data in [default: + volk_results.db] + -N NITEMS, --nitems NITEMS + Number of items per iterations [default: 1000000000.0] + -I ITERATIONS, --iterations ITERATIONS + Number of iterations [default: 20] + --tests [{0,1,2,3} [{0,1,2,3} ...]] + A list of tests to run; can be a single test or a + space-separated list. + --list List the available tests + --all Run all tests +---------------------------------------------------------------------- + +To run, you specify the tests to run and a label to store along with +the results. To find out what the available tests are, use the +'--list' option. + +To specify a subset of tests, use the '--tests' with space-separated +list of tests numbers (e.g., --tests 0 2 4 9). + +Use the '--all' to run all tests. + +The label specified is used as an identifier for the benchmarking +currently being done. This is required as it is important in +organizing the data in the database (each label is its own +table). Usually, the label will specify the type of run being done, +such as "volk_aligned" or "v3_5_1". In these cases, the "volk_aligned" +label says that this is for a benchmarking using the GNU Radio version +that uses the aligned scheduler and Volk calls in the work +functions. The "v3_5_1" label is if you were benchmarking an installed +version 3.5.1 of GNU Radio, which is pre-Volk. These will then be +plotted against each other to see the timing differences. + +The 'database' option will output the results to a new database +file. This can be useful for separating the output of different runs +or of different benchmarks, such as the types versus the math scripts, +say, or to distinguish results from different computers. + +If rerun using the same database and label, the entries in the table +will simply be replaced by the new results. + +It is often useful to use the 'sqlitebrowser' program to interrogate +the database file farther, if you are interested in the structure or +the raw data. + +Other parameters of this script set the number of items to process and +number of iterations to use when computing the benchmarking +data. These default to 1 billion samples per iteration over 20 +iterations. Expect a default run to take a long time. Using the '-N' +and '-I' options can be used to change the runtime of the benchmarks +but are set high to remove problems of variance between iterations. + +====================================================================== +Plotting Results + +The volk_plot.py script reads a given database file and plots the +results. The default behavior is to read all of the labels stored in +the database and plot them as data sets on a bar graph. This shows the +average time taken to process the number of items given. + +The options for the plotting script are: + +usage: volk_plot.py [-h] [-D DATABASE] [-E] [-P {mean,min,max}] [-% table] + +Plot Volk performance results from a SQLite database. Run one of the volk +tests first (e.g, volk_math.py) + +---------------------------------------------------------------------- +optional arguments: + -h, --help show this help message and exit + -D DATABASE, --database DATABASE + Database file to read data from [default: + volk_results.db] + -E, --errorbars Show error bars (1 standard dev.) + -P {mean,min,max}, --plot {mean,min,max} + Set the type of plot to produce [default: mean] + -% table, --percent table + Show percent difference to the given type [default: + None] +---------------------------------------------------------------------- + +This script allows you to specify the database used (-D), but will +always read all rows from all tables from it and display them. You can +also turn on plotting error bars (1 standard deviation the mean). Be +careful, though, as some older versions of Matplotlib might have an +issue with this option. + +The mean time is only one possible statistic that we might be +interested in when looking at the data. It represents the average user +experience when running a given block. On the other hand, the minimum +runtime best represents the actual performance of a block given +minimal OS interruptions while running. Right now, the data collected +includes the mean, variance, min, and max over the number of +iterations given. Using the '-P' option, you can specify the type of +data to plot (mean, min, or max). + +Another useful way of looking at the data is to compare the percent +improvement of a benchmark compared to another. This is done using the +'-%' option with the provided table (or label) as the baseline. So if +we were interested in comparing how much the 'volk_aligned' was over +'v3_5_1', we would specify '-% v3_5_1' to see this. The plot would +then only show the percent speedup observed using Volk for each of the +blocks. + + +====================================================================== +Benchmarking Walkthrough + +This will walk through an example of benchmarking the new Volk +implementation versus the pre-Volk GNU Radio. It also shows how to +look at the SIMD optimized versions versus the generic +implementations. + +Since we introduced Volk in GNU Radio 3.5.2, we will use the following +labels for our data: + + 1.) volk_aligned: v3.5.2 with volk_profile results in .volk/volk_config + 2.) v3_5_2: v3.5.2 with the generic (non-SIMD) calls to Volk + 3.) v3_5_1: an installation of GNU Radio from version v3.5.1 + +We assume that we have installed two versions of GNU Radio. + + v3.5.2 installed into /opt/gr-3_5_2 + v3.5.1 installed into /opt/gr-3_5_1 + +To test cases 1 and 2 above, we have to run GNU Radio from the v3.5.2 +installation, so we set the following environmental variables. Note +that this is written for Ubuntu 11.10. These commands and directories +may have to be changed depending on your OS and versions. + + export LD_LIBRARY_PATH=/opt/gr-3_5_2/lib + export LD_LIBRARY_PATH=/opt/gr-3_5_2/lib/python2.7/dist-packages + +Now we can run the benchmark tests, so we will focus on the math +operators: + + ./volk_math.py -D volk_results_math.db --all -L volk_aligned + +When this finishes, the 'volk_results_math.db' will contain our +results for this run. + +We next want to run the generic, non-SIMD, calls. This can be done by +changing the Volk kernel settings in $HOME/.volk/volk_config. First, +make a backup of this file. Then edit it and change all architecture +calls (sse, sse2, etc.) to 'generic.' Now, Volk will only call the +generic versions of these functions. So we rerun the benchmark with: + + ./volk_math.py -D volk_results_math.db --all -L v3_5_2 + +Notice that the only thing changed here was the label to 'v3_5_2'. + +Next, we want to collect data for the non-Volk version of GNU +Radio. This is important because some internals to GNU Radio were made +when adding support for Volk, so it is nice to know what the +differences do to our performance. First, we set the environmental +variables to point to the v3.5.1 installation: + + export LD_LIBRARY_PATH=/opt/gr-3_5_1/lib + export LD_LIBRARY_PATH=/opt/gr-3_5_1/lib/python2.7/dist-packages + +And when we run the test, we use the same command line, but the GNU +Radio libraries and Python files used come from v3.5.1. We also change +the label to indicate the different version to store. + + ./volk_math.py -D volk_results_math.db --all -L v3_5_1 + +We now have a database populated with three tables for the three +different labels. We can plot them all together by simply running: + + ./volk_plot.py -D volk_results_math.db + +This will show the average run times for each of the three +configurations for all math functions tested. We might also be +interested to see the difference in performance from the v3.5.1 +version, so we can run: + + ./volk_plot.py -D volk_results_math.db -% v3_5_1 + +That will plot both the 'volk_aligned' and 'v3_5_2' as a percentage +improvement over v3_5_1. A positive value indicates that this version +runs faster than the v3.5.1 version. + + +---------------------------------------------------------------------- + +Another interesting test case could be to compare results on different +processors. So if you have different generation Intels, AMD, or +whatever, you can simply pass the .db file around and run the Volk +benchmark script to populate the database with different results. For +this, you would specify a label like '-L i7_2620M' that indicates the +processor type to uniquely ID the data. + diff --git a/gnuradio-examples/python/volk_benchmark/volk_math.py b/gnuradio-examples/python/volk_benchmark/volk_math.py new file mode 100755 index 000000000..8b0081387 --- /dev/null +++ b/gnuradio-examples/python/volk_benchmark/volk_math.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python + +from gnuradio import gr +import argparse +from volk_test_funcs import * + +def multiply_const_cc(N): + k = 3.3 + op = gr.multiply_const_cc(k) + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_gr_complex, 1, 1) + return tb + +###################################################################### + +def multiply_const_ff(N): + k = 3.3 + op = gr.multiply_const_ff(k) + tb = helper(N, op, gr.sizeof_float, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + +def multiply_cc(N): + op = gr.multiply_cc() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_gr_complex, 2, 1) + return tb + +###################################################################### + +def multiply_ff(N): + op = gr.multiply_ff() + tb = helper(N, op, gr.sizeof_float, gr.sizeof_float, 2, 1) + return tb + +###################################################################### + +def add_ff(N): + op = gr.add_ff() + tb = helper(N, op, gr.sizeof_float, gr.sizeof_float, 2, 1) + return tb + +###################################################################### + +def conjugate_cc(N): + op = gr.conjugate_cc() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_gr_complex, 1, 1) + return tb + +###################################################################### + +def multiply_conjugate_cc(N): + try: + op = gr.multiply_conjugate_cc() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_gr_complex, 2, 1) + return tb + + except AttributeError: + class s(gr.hier_block2): + def __init__(self): + gr.hier_block2.__init__(self, "s", + gr.io_signature(2, 2, gr.sizeof_gr_complex), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) + conj = gr.conjugate_cc() + mult = gr.multiply_cc() + self.connect((self,0), (mult,0)) + self.connect((self,1), conj, (mult,1)) + self.connect(mult, self) + + op = s() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_gr_complex, 2, 1) + return tb + + +###################################################################### + +def run_tests(func, N, iters): + print("Running Test: {0}".format(func.__name__)) + try: + tb = func(N) + t = timeit(tb, iters) + res = format_results(func.__name__, t) + return res + except AttributeError: + print "\tCould not run test. Skipping." + return None + +def main(): + avail_tests = [multiply_const_cc, + multiply_const_ff, + multiply_cc, + multiply_ff, + add_ff, + conjugate_cc, + multiply_conjugate_cc] + + desc='Time an operation to compare with other implementations. \ + This program runs a simple GNU Radio flowgraph to test a \ + particular math function, mostly to compare the \ + Volk-optimized implementation versus a regular \ + implementation. The results are stored to an SQLite database \ + that can then be read by volk_plot.py to plot the differences.' + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('-L', '--label', type=str, + required=True, default=None, + help='Label of database table [default: %(default)s]') + parser.add_argument('-D', '--database', type=str, + default="volk_results.db", + help='Database file to store data in [default: %(default)s]') + parser.add_argument('-N', '--nitems', type=float, + default=1e9, + help='Number of items per iterations [default: %(default)s]') + parser.add_argument('-I', '--iterations', type=int, + default=20, + help='Number of iterations [default: %(default)s]') + parser.add_argument('--tests', type=int, nargs='*', + choices=xrange(len(avail_tests)), + help='A list of tests to run; can be a single test or a \ + space-separated list.') + parser.add_argument('--list', action='store_true', + help='List the available tests') + parser.add_argument('--all', action='store_true', + help='Run all tests') + args = parser.parse_args() + + if(args.list): + print "Available Tests to Run:" + print "\n".join(["\t{0}: {1}".format(i,f.__name__) for i,f in enumerate(avail_tests)]) + sys.exit(0) + + N = int(args.nitems) + iters = args.iterations + label = args.label + + conn = create_connection(args.database) + new_table(conn, label) + + if not args.all: + func = avail_tests[args.test] + res = run_tests(func, N, iters) + if res is not None: + replace_results(conn, label, N, iters, res) + else: + for f in avail_tests: + res = run_tests(f, N, iters) + if res is not None: + replace_results(conn, label, N, iters, res) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass diff --git a/gnuradio-examples/python/volk_benchmark/volk_plot.py b/gnuradio-examples/python/volk_benchmark/volk_plot.py new file mode 100755 index 000000000..823dfbf64 --- /dev/null +++ b/gnuradio-examples/python/volk_benchmark/volk_plot.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python + +import sys, math +import argparse +from volk_test_funcs import * + +try: + import matplotlib + import matplotlib.pyplot as plt +except ImportError: + sys.stderr.write("Could not import Matplotlib (http://matplotlib.sourceforge.net/)\n") + sys.exit(1) + +def main(): + desc='Plot Volk performance results from a SQLite database. ' + \ + 'Run one of the volk tests first (e.g, volk_math.py)' + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('-D', '--database', type=str, + default='volk_results.db', + help='Database file to read data from [default: %(default)s]') + parser.add_argument('-E', '--errorbars', + action='store_true', default=False, + help='Show error bars (1 standard dev.)') + parser.add_argument('-P', '--plot', type=str, + choices=['mean', 'min', 'max'], + default='mean', + help='Set the type of plot to produce [default: %(default)s]') + parser.add_argument('-%', '--percent', type=str, + default=None, metavar="table", + help='Show percent difference to the given type [default: %(default)s]') + args = parser.parse_args() + + # Set up global plotting properties + matplotlib.rcParams['figure.subplot.bottom'] = 0.2 + matplotlib.rcParams['figure.subplot.top'] = 0.95 + matplotlib.rcParams['figure.subplot.right'] = 0.98 + matplotlib.rcParams['ytick.labelsize'] = 16 + matplotlib.rcParams['xtick.labelsize'] = 16 + matplotlib.rcParams['legend.fontsize'] = 18 + + # Get list of tables to compare + conn = create_connection(args.database) + tables = list_tables(conn) + M = len(tables) + + # Colors to distinguish each table in the bar graph + # More than 5 tables will wrap around to the start. + colors = ['b', 'r', 'g', 'm', 'k'] + + # Set up figure for plotting + f0 = plt.figure(0, facecolor='w', figsize=(14,10)) + s0 = f0.add_subplot(1,1,1) + + # Create a register of names that exist in all tables + tmp_regs = [] + for table in tables: + # Get results from the next table + res = get_results(conn, table[0]) + + tmp_regs.append(list()) + for r in res: + try: + tmp_regs[-1].index(r['kernel']) + except ValueError: + tmp_regs[-1].append(r['kernel']) + + # Get only those names that are common in all tables + name_reg = tmp_regs[0] + for t in tmp_regs[1:]: + name_reg = list(set(name_reg) & set(t)) + name_reg.sort() + + # Pull the data out for each table into a dictionary + # we can ref the table by it's name and the data associated + # with a given kernel in name_reg by it's name. + # This ensures there is no sorting issue with the data in the + # dictionary, so the kernels are plotted against each other. + table_data = dict() + for i,table in enumerate(tables): + # Get results from the next table + res = get_results(conn, table[0]) + + data = dict() + for r in res: + data[r['kernel']] = r + + table_data[table[0]] = data + + if args.percent is not None: + for i,t in enumerate(table_data): + if args.percent == t: + norm_data = [] + for name in name_reg: + if(args.plot == 'max'): + norm_data.append(table_data[t][name]['max']) + elif(args.plot == 'min'): + norm_data.append(table_data[t][name]['min']) + elif(args.plot == 'mean'): + norm_data.append(table_data[t][name]['avg']) + + + # Plot the results + x0 = xrange(len(name_reg)) + i = 0 + for t in (table_data): + ydata = [] + stds = [] + for name in name_reg: + stds.append(math.sqrt(table_data[t][name]['var'])) + if(args.plot == 'max'): + ydata.append(table_data[t][name]['max']) + elif(args.plot == 'min'): + ydata.append(table_data[t][name]['min']) + elif(args.plot == 'mean'): + ydata.append(table_data[t][name]['avg']) + + if args.percent is not None: + ydata = [-100*(y-n)/y for y,n in zip(ydata,norm_data)] + if(args.percent != t): + # makes x values for this data set placement + # width of bars depends on number of comparisons + wdth = 0.80/(M-1) + x1 = [x + i*wdth for x in x0] + i += 1 + + s0.bar(x1, ydata, width=wdth, + color=colors[(i-1)%M], label=t, + edgecolor='k', linewidth=2) + + else: + # makes x values for this data set placement + # width of bars depends on number of comparisons + wdth = 0.80/M + x1 = [x + i*wdth for x in x0] + i += 1 + + if(args.errorbars is False): + s0.bar(x1, ydata, width=wdth, + color=colors[(i-1)%M], label=t, + edgecolor='k', linewidth=2) + else: + s0.bar(x1, ydata, width=wdth, + yerr=stds, + color=colors[i%M], label=t, + edgecolor='k', linewidth=2, + error_kw={"ecolor": 'k', "capsize":5, + "linewidth":2}) + + nitems = res[0]['nitems'] + if args.percent is None: + s0.set_ylabel("Processing time (sec) [{0:G} items]".format(nitems), + fontsize=22, fontweight='bold', + horizontalalignment='center') + else: + s0.set_ylabel("% Improvement over {0} [{1:G} items]".format( + args.percent, nitems), + fontsize=22, fontweight='bold') + + s0.legend() + s0.set_xticks(x0) + s0.set_xticklabels(name_reg) + for label in s0.xaxis.get_ticklabels(): + label.set_rotation(45) + label.set_fontsize(16) + + plt.show() + +if __name__ == "__main__": + main() diff --git a/gnuradio-examples/python/volk_benchmark/volk_test_funcs.py b/gnuradio-examples/python/volk_benchmark/volk_test_funcs.py new file mode 100644 index 000000000..4f4e4afd3 --- /dev/null +++ b/gnuradio-examples/python/volk_benchmark/volk_test_funcs.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python + +from gnuradio import gr +import math, sys, os, time + +try: + import scipy +except ImportError: + sys.stderr.write("Unable to import Scipy (www.scipy.org)\n") + sys.exit(1) + +try: + import sqlite3 +except ImportError: + sys.stderr.write("Unable to import sqlite3: requires Python 2.5\n") + sys.exit(1) + +def execute(conn, cmd): + ''' + Executes the command cmd to the database opened in connection conn. + ''' + c = conn.cursor() + c.execute(cmd) + conn.commit() + c.close() + +def create_connection(database): + ''' + Returns a connection object to the SQLite database. + ''' + return sqlite3.connect(database) + +def new_table(conn, tablename): + ''' + Create a new table for results. + All results are in the form: [kernel | nitems | iters | avg. time | variance | max time | min time ] + Each table is meant as a different setting (e.g., volk_aligned, volk_unaligned, etc.) + ''' + cols = "kernel text, nitems int, iters int, avg real, var real, max real, min real" + cmd = "create table if not exists {0} ({1})".format( + tablename, cols) + execute(conn, cmd) + +def replace_results(conn, tablename, nitems, iters, res): + ''' + Inserts or replaces the results 'res' dictionary values into the table. + This deletes all old entries of the kernel in this table. + ''' + cmd = "DELETE FROM {0} where kernel='{1}'".format(tablename, res["kernel"]) + execute(conn, cmd) + insert_results(conn, tablename, nitems, iters, res) + +def insert_results(conn, tablename, nitems, iters, res): + ''' + Inserts the results dictionary values into the table. + ''' + cols = "kernel, nitems, iters, avg, var, max, min" + cmd = "INSERT INTO {0} ({1}) VALUES ('{2}', {3}, {4}, {5}, {6}, {7}, {8})".format( + tablename, cols, res["kernel"], nitems, iters, + res["avg"], res["var"], res["max"], res["min"]) + execute(conn, cmd) + +def list_tables(conn): + ''' + Returns a list of all tables in the database. + ''' + cmd = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" + c = conn.cursor() + c.execute(cmd) + t = c.fetchall() + c.close() + + return t + +def get_results(conn, tablename): + ''' + Gets all results in tablename. + ''' + cmd = "SELECT * FROM {0}".format(tablename) + c = conn.cursor() + c.execute(cmd) + fetched = c.fetchall() + c.close() + + res = list() + for f in fetched: + r = dict() + r['kernel'] = f[0] + r['nitems'] = f[1] + r['iters'] = f[2] + r['avg'] = f[3] + r['var'] = f[4] + r['min'] = f[5] + r['max'] = f[6] + res.append(r) + + return res + + +class helper(gr.top_block): + ''' + Helper function to run the tests. The parameters are: + N: number of items to process (int) + op: The GR block/hier_block to test + isizeof: the sizeof the input type + osizeof: the sizeof the output type + nsrcs: number of inputs to the op + nsnks: number of outputs of the op + + This function can only handle blocks where all inputs are the same + datatype and all outputs are the same data type + ''' + def __init__(self, N, op, + isizeof=gr.sizeof_gr_complex, + osizeof=gr.sizeof_gr_complex, + nsrcs=1, nsnks=1): + gr.top_block.__init__(self, "helper") + + self.op = op + self.srcs = [] + self.snks = [] + self.head = gr.head(isizeof, N) + + for n in xrange(nsrcs): + self.srcs.append(gr.null_source(isizeof)) + + for n in xrange(nsnks): + self.snks.append(gr.null_sink(osizeof)) + + self.connect(self.srcs[0], self.head, (self.op,0)) + + for n in xrange(1, nsrcs): + self.connect(self.srcs[n], (self.op,n)) + + for n in xrange(nsnks): + self.connect((self.op,n), self.snks[n]) + +def timeit(tb, iterations): + ''' + Given a top block, this function times it for a number of + iterations and stores the time in a list that is returned. + ''' + r = gr.enable_realtime_scheduling() + if r != gr.RT_OK: + print "Warning: failed to enable realtime scheduling" + + times = [] + for i in xrange(iterations): + start_time = time.time() + tb.run() + end_time = time.time() + tb.head.reset() + + times.append(end_time - start_time) + + return times + +def format_results(kernel, times): + ''' + Convinience function to convert the results of the timeit function + into a dictionary. + ''' + res = dict() + res["kernel"] = kernel + res["avg"] = scipy.mean(times) + res["var"] = scipy.var(times) + res["max"] = max(times) + res["min"] = min(times) + return res + + diff --git a/gnuradio-examples/python/volk_benchmark/volk_types.py b/gnuradio-examples/python/volk_benchmark/volk_types.py new file mode 100755 index 000000000..3bc5a22ae --- /dev/null +++ b/gnuradio-examples/python/volk_benchmark/volk_types.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python + +from gnuradio import gr +import argparse +from volk_test_funcs import * + +###################################################################### + +def float_to_char(N): + op = gr.float_to_char() + tb = helper(N, op, gr.sizeof_float, gr.sizeof_char, 1, 1) + return tb + +###################################################################### + +def float_to_int(N): + op = gr.float_to_int() + tb = helper(N, op, gr.sizeof_float, gr.sizeof_int, 1, 1) + return tb + +###################################################################### + +def float_to_short(N): + op = gr.float_to_short() + tb = helper(N, op, gr.sizeof_float, gr.sizeof_short, 1, 1) + return tb + +###################################################################### + +def short_to_float(N): + op = gr.short_to_float() + tb = helper(N, op, gr.sizeof_short, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + +def short_to_char(N): + op = gr.short_to_char() + tb = helper(N, op, gr.sizeof_short, gr.sizeof_char, 1, 1) + return tb + +###################################################################### + +def char_to_short(N): + op = gr.char_to_short() + tb = helper(N, op, gr.sizeof_char, gr.sizeof_short, 1, 1) + return tb + +###################################################################### + +def char_to_float(N): + op = gr.char_to_float() + tb = helper(N, op, gr.sizeof_char, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + +def int_to_float(N): + op = gr.int_to_float() + tb = helper(N, op, gr.sizeof_int, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + +def complex_to_float(N): + op = gr.complex_to_float() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_float, 1, 2) + return tb + +###################################################################### + +def complex_to_real(N): + op = gr.complex_to_real() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + +def complex_to_imag(N): + op = gr.complex_to_imag() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + +def complex_to_mag(N): + op = gr.complex_to_mag() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + +def complex_to_mag_squared(N): + op = gr.complex_to_mag_squared() + tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_float, 1, 1) + return tb + +###################################################################### + + +def run_tests(func, N, iters): + print("Running Test: {0}".format(func.__name__)) + try: + tb = func(N) + t = timeit(tb, iters) + res = format_results(func.__name__, t) + return res + except AttributeError: + print "\tCould not run test. Skipping." + return None + +def main(): + avail_tests = [float_to_char, + float_to_int, + float_to_short, + short_to_float, + short_to_char, + char_to_short, + char_to_float, + int_to_float, + complex_to_float, + complex_to_real, + complex_to_imag, + complex_to_mag, + complex_to_mag_squared] + + desc='Time an operation to compare with other implementations. \ + This program runs a simple GNU Radio flowgraph to test a \ + particular math function, mostly to compare the \ + Volk-optimized implementation versus a regular \ + implementation. The results are stored to an SQLite database \ + that can then be read by volk_plot.py to plot the differences.' + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('-L', '--label', type=str, + required=True, default=None, + help='Label of database table [default: %(default)s]') + parser.add_argument('-D', '--database', type=str, + default="volk_results.db", + help='Database file to store data in [default: %(default)s]') + parser.add_argument('-N', '--nitems', type=float, + default=1e9, + help='Number of items per iterations [default: %(default)s]') + parser.add_argument('-I', '--iterations', type=int, + default=20, + help='Number of iterations [default: %(default)s]') + parser.add_argument('--tests', type=int, nargs='*', + choices=xrange(len(avail_tests)), + help='A list of tests to run; can be a single test or a \ + space-separated list.') + parser.add_argument('--list', action='store_true', + help='List the available tests') + parser.add_argument('--all', action='store_true', + help='Run all tests') + args = parser.parse_args() + + if(args.list): + print "Available Tests to Run:" + print "\n".join(["\t{0}: {1}".format(i,f.__name__) for i,f in enumerate(avail_tests)]) + sys.exit(0) + + N = int(args.nitems) + iters = args.iterations + label = args.label + + conn = create_connection(args.database) + new_table(conn, label) + + if args.all: + tests = xrange(len(avail_tests)) + else: + tests = args.tests + + for test in tests: + res = run_tests(avail_tests[test], N, iters) + if res is not None: + replace_results(conn, label, N, iters, res) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass |