summaryrefslogtreecommitdiff
path: root/gnuradio-examples
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-examples')
-rwxr-xr-xgnuradio-examples/python/volk_benchmark/volk_math.py146
-rwxr-xr-xgnuradio-examples/python/volk_benchmark/volk_plot.py81
-rw-r--r--gnuradio-examples/python/volk_benchmark/volk_test_funcs.py171
3 files changed, 398 insertions, 0 deletions
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..ec85ce0ad
--- /dev/null
+++ b/gnuradio-examples/python/volk_benchmark/volk_math.py
@@ -0,0 +1,146 @@
+#!/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_volk(N):
+ op = gr.multiply_conjugate_cc()
+ tb = helper(N, op, gr.sizeof_gr_complex, gr.sizeof_gr_complex, 2, 1)
+ return tb
+
+def multiply_conjugate_cc_nonvolk(N):
+ 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
+
+#multiply_conjugate_cc = multiply_conjugate_cc_volk
+multiply_conjugate_cc = multiply_conjugate_cc_nonvolk
+
+######################################################################
+
+def run_tests(func, N, iters):
+ print("Running Test: {0}".format(func.__name__))
+ tb = func(N)
+ t = timeit(tb, iters)
+ res = format_results(func.__name__, t)
+ return res
+
+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('label', type=str,
+ 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('--test', type=int,
+ choices=xrange(len(avail_tests)),
+ help='Test to run')
+ 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)
+ replace_results(conn, label, N, iters, res)
+ else:
+ for f in avail_tests:
+ res = run_tests(f, N, iters)
+ 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..665df5e14
--- /dev/null
+++ b/gnuradio-examples/python/volk_benchmark/volk_plot.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+
+import sys
+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]')
+ 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['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)
+
+ # width of bars depends on number of comparisons
+ wdth = 0.80/M
+
+ 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)
+
+ for i,table in enumerate(tables):
+ # Get results from the next table
+ res = get_results(conn, table[0])
+
+ xlabels = []
+ averages = []
+ variances = []
+ maxes = []
+ mins = []
+ for r in res:
+ xlabels.append(r['kernel'])
+ averages.append(r['avg'])
+ variances.append(r['var'])
+ maxes.append(r['max'])
+ mins.append(r['min'])
+
+ # makes x values for this data set placement
+ x0 = xrange(len(res))
+ x1 = [x + i*wdth for x in x0]
+
+ s0.bar(x1, averages, width=wdth,
+ #yerr=variances,
+ color=colors[i%M], label=table[0],
+ edgecolor='k', linewidth=2)
+
+ s0.legend()
+ s0.set_ylabel("Processing time (sec) [{0:G} items]".format(res[0]['nitems']),
+ fontsize=22, fontweight='bold')
+ s0.set_xticks(x0)
+ s0.set_xticklabels(xlabels)
+ 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
+
+