| #!/usr/bin/python | |
| import string | |
| import sys | |
| import re | |
| from Numeric import * | |
| from pychart import * | |
| from xml.dom import minidom | |
| class exception: | |
| pass | |
| class res: | |
| """ | |
| A 'structure' representing the results of a test. | |
| """ | |
| def __init__(self, x_label, y_label, cntnr_list, cntnr_descs, res_sets): | |
| self.x_label = x_label | |
| self.y_label = y_label | |
| self.cntnr_list = cntnr_list | |
| self.cntnr_descs = cntnr_descs | |
| self.res_sets = res_sets | |
| class res_getter: | |
| """ | |
| This class returns a res object for some test. | |
| """ | |
| class __sorter: | |
| def __accum(self, results): | |
| total = 0 | |
| for result in results: | |
| total = total + result[1] | |
| return total | |
| def sort(self, cntnr_list, res_sets): | |
| cntnrs_and_totals = [] | |
| for cntnr in cntnr_list: | |
| results = res_sets[cntnr] | |
| total = self.__accum(results) | |
| cntnrs_and_totals.append((cntnr, total)) | |
| by_total = lambda x,y: x[1] > y[1] and -1 or 1 | |
| cntnrs_and_totals.sort(by_total) | |
| ret = [] | |
| for cntnr_and_total in cntnrs_and_totals: | |
| cntnr = cntnr_and_total[0] | |
| ret.append(cntnr) | |
| return ret | |
| def __init__(self, test_infos_f_name): | |
| self.__test_to_container_res_sets = {} | |
| self.__test_to_f_names = {} | |
| tests_dat = minidom.parse(test_infos_f_name) | |
| for test in tests_dat.getElementsByTagName('test'): | |
| test_name = test.attributes['name'].value | |
| self.__test_to_f_names[test_name] = test.getElementsByTagName('file')[0].attributes['name'].value | |
| cntnr_list = [] | |
| for cntnr in test.getElementsByTagName('cntnr'): | |
| cntnr_list.append(cntnr.attributes['name'].value) | |
| self.__test_to_container_res_sets[test_name] = cntnr_list | |
| def __get_label(self, tst_dat, label_name): | |
| label = tst_dat.getElementsByTagName(label_name)[0].firstChild.data | |
| label = string.strip(label, '\n') | |
| label = string.strip(label) | |
| return label | |
| def __parse_result_sets(self, f_name, cntnr_list): | |
| tst_dat = minidom.parse(f_name) | |
| x_label = self.__get_label(tst_dat, 'x_name') | |
| y_label = self.__get_label(tst_dat, 'y_name') | |
| parsed_container_list = tst_dat.getElementsByTagName('cntnr') | |
| res_sets = {} | |
| cntnr_descs = {} | |
| for cntnr in parsed_container_list: | |
| cntnr_name = cntnr.attributes["name"].value | |
| res_sets[cntnr_name] = [] | |
| for cntnr in parsed_container_list: | |
| cntnr_name = cntnr.attributes["name"].value | |
| cntnr_desc = cntnr.getElementsByTagName('desc') | |
| if res_sets.has_key(cntnr_name): | |
| res_set = [] | |
| result_list = cntnr.getElementsByTagName('result') | |
| for result in result_list: | |
| x = string.atol(result.attributes["x"].value) | |
| y = string.atof(result.attributes["y"].value) | |
| res_set.append((x, y)) | |
| res_sets[cntnr_name] = res_set | |
| cntnr_descs[cntnr_name] = cntnr_desc[0] | |
| return (x_label, y_label, cntnr_descs, res_sets) | |
| def get(self, res_dir, test_name): | |
| cntnr_list = self.__test_to_container_res_sets[test_name] | |
| f_name = res_dir + '/' + self.__test_to_f_names[test_name] | |
| parsed = self.__parse_result_sets(f_name, cntnr_list) | |
| x_label = parsed[0] | |
| y_label = parsed[1] | |
| cntnr_descs = parsed[2] | |
| res_sets = parsed[3] | |
| cntnr_list = self.__sorter().sort(cntnr_list, res_sets) | |
| return res(x_label, y_label, cntnr_list, cntnr_descs, res_sets) | |
| class image_maker: | |
| """ | |
| This class creates a svg file from a result set. | |
| """ | |
| class __style_chooser: | |
| def __init__(self): | |
| self.native_re = re.compile(r'n_(?:.*?)') | |
| self.native_tick_mark_0 = tick_mark.blackdtri | |
| self.native_tick_mark_1 = tick_mark.blackdia | |
| self.native_line_style_0 = line_style.gray50_dash1 | |
| self.native_line_style_1 = line_style.gray50_dash2 | |
| self.mask_re = re.compile(r'mask(?:.*?)') | |
| self.mod_re = re.compile(r'mod(?:.*?)') | |
| self.rb_tree_mmap_rb_tree_set_re = re.compile(r'rb_tree_mmap_rb_tree_set(?:.*?)') | |
| self.rb_tree_mmap_lu_mtf_set_re = re.compile(r'rb_tree_mmap_lu_mtf_set(?:.*?)') | |
| self.splay_re = re.compile(r'splay(?:.*?)') | |
| self.rb_tree_re = re.compile(r'rb_tree(?:.*?)') | |
| self.ov_tree_re = re.compile(r'ov_tree(?:.*?)') | |
| self.splay_tree_re = re.compile(r'splay_tree(?:.*?)') | |
| self.pat_trie_re = re.compile(r'pat_trie(?:.*?)') | |
| self.lc_1div8_1div2_re = re.compile(r'lc_1div8_1div2(?:.*?)') | |
| self.lc_1div8_1div1_re = re.compile(r'lc_1div8_1div1(?:.*?)') | |
| self.mcolc_1div2_re = re.compile(r'mcolc_1div2(?:.*?)') | |
| def choose(self, cntnr): | |
| if self.native_re.search(cntnr): | |
| if cntnr == 'n_pq_vector': | |
| return (self.native_tick_mark_1, self.native_line_style_1) | |
| return (self.native_tick_mark_0, self.native_line_style_0) | |
| # tick_mark predefined | |
| # square, circle3, dia, tri, dtri, star, plus5, x5, gray70dia, blackdtri, blackdia | |
| if self.mask_re.search(cntnr): | |
| clr = color.navy | |
| elif self.mod_re.search(cntnr): | |
| clr = color.green4 | |
| elif self.rb_tree_mmap_rb_tree_set_re.search(cntnr): | |
| clr = color.mediumblue | |
| tm = tick_mark.square | |
| elif self.rb_tree_mmap_lu_mtf_set_re.search(cntnr) or cntnr == 'rc_binomial_heap': | |
| clr = color.gray50 | |
| tm = tick_mark.dia | |
| elif self.splay_tree_re.search(cntnr) or cntnr == 'binomial_heap': | |
| clr = color.gray58 | |
| tm = tick_mark.tri | |
| elif self.rb_tree_re.search(cntnr) or cntnr == 'binary_heap': | |
| clr = color.red3 | |
| tm = tick_mark.dtri | |
| elif self.ov_tree_re.search(cntnr) or cntnr == 'thin_heap': | |
| clr = color.orangered1 | |
| tm = tick_mark.star | |
| elif self.pat_trie_re.search(cntnr) or cntnr == 'pairing_heap': | |
| clr = color.blueviolet | |
| tm = tick_mark.plus5 | |
| else: | |
| sys.stderr.write(cntnr + '\n') | |
| raise exception | |
| # mask / mod | |
| if cntnr.find('lc_1div8_1div') <> -1: | |
| if cntnr.find('mask') <> -1: | |
| # mask | |
| if self.lc_1div8_1div2_re.search(cntnr): | |
| if cntnr.find('nsth') <> -1: | |
| tm = tick_mark.x5 | |
| else: | |
| tm = tick_mark.gray70dia | |
| if self.lc_1div8_1div1_re.search(cntnr): | |
| if cntnr.find('nsth') <> -1: | |
| tm = tick_mark.dia | |
| else: | |
| tm = tick_mark.circle3 | |
| else: | |
| # mod | |
| if self.lc_1div8_1div2_re.search(cntnr): | |
| if cntnr.find('nsth') <> -1: | |
| tm = tick_mark.tri | |
| else: | |
| tm = tick_mark.square | |
| if self.lc_1div8_1div1_re.search(cntnr): | |
| if cntnr.find('nsth') <> -1: | |
| tm = tick_mark.dtri | |
| else: | |
| tm = tick_mark.star | |
| if self.mcolc_1div2_re.search(cntnr): | |
| tm = tick_mark.circle3 | |
| return (tm, line_style.T(color = clr, width = 2)) | |
| def __init__(self): | |
| self.__sc = self.__style_chooser() | |
| self.__mmap_re = re.compile('mmap_') | |
| def __container_label_name(self, cntnr): | |
| return self.__mmap_re.sub('\nmmap_\n', cntnr) | |
| def make(self, res, of_name): | |
| print of_name | |
| # theme settings | |
| theme.debug_level = 3 | |
| theme.output_format = 'svg' | |
| theme.scale_factor = 2 | |
| theme.default_line_width = 0.5 | |
| theme.default_font_size = 8 | |
| theme.use_color = 1 | |
| theme.reinitialize() | |
| # canvas settings | |
| f = file(of_name, "w") | |
| can = canvas.init(f, "svg") | |
| # axes | |
| y_tick_interval = self.__get_y_tics(res) | |
| xaxis = axis.X(format = "/6/i/a-90{}%d", | |
| tic_interval = 200, | |
| label = res.x_label, label_offset = (0, -20)) | |
| yaxis = axis.Y(format = "/6/i/a0{}%.2e", | |
| tic_interval = y_tick_interval, tic_label_offset = (-25, 0), | |
| label = res.y_label, label_offset = (-15, 0)) | |
| # legend | |
| legend_lines = len(res.cntnr_list) | |
| legend_vloc = 80 + (legend_lines * 10) | |
| legend_hloc = -0 | |
| lg = legend.T(loc=(legend_hloc,-legend_vloc), | |
| frame_line_style = None, inter_row_sep = 2) | |
| # plot datasets | |
| ar = area.T(x_axis = xaxis, y_axis = yaxis, legend = lg, size = (240,110), x_range = (0, 2200)) | |
| plot_list = [] | |
| for cntnr in res.cntnr_list: | |
| style = self.__sc.choose(cntnr) | |
| pl = line_plot.T(label = self.__container_label_name(cntnr), | |
| data = res.res_sets[cntnr], | |
| tick_mark = style[0], | |
| line_style = style[1]) | |
| plot_list.append(pl) | |
| for plot in plot_list: | |
| ar.add_plot(plot) | |
| # render image | |
| ar.draw(can) | |
| can.close() | |
| def __get_y_max_min(self, res): | |
| mx = 0 | |
| nx = 0 | |
| for cntnr in res.cntnr_list: | |
| m = max(d[1] for d in res.res_sets[cntnr]) | |
| mx = max(m, mx) | |
| n = min(d[1] for d in res.res_sets[cntnr]) | |
| nx = min(n, nx) | |
| return (mx, nx) | |
| def __get_x_max_min(self, res): | |
| mx = 0 | |
| nx = 0 | |
| for cntnr in res.cntnr_list: | |
| m = max(d[0] for d in res.res_sets[cntnr]) | |
| mx = max(m, mx) | |
| n = min(d[0] for d in res.res_sets[cntnr]) | |
| nx = min(n, nx) | |
| return (mx, nx) | |
| def __get_y_tics(self, res): | |
| mx = 0 | |
| for cntnr in res.cntnr_list: | |
| m = max(d[1] for d in res.res_sets[cntnr]) | |
| mx = max(m, mx) | |
| return mx / 5 | |
| def main(test_infos_f_name, res_dir, doc_dir): | |
| xmls_dat = minidom.parse(test_infos_f_name) | |
| for test in xmls_dat.getElementsByTagName('test'): | |
| # parse results | |
| test_name = test.attributes['name'].value | |
| res_gtr = res_getter(test_infos_f_name) | |
| res = res_gtr.get(res_dir, test_name) | |
| # generate image | |
| image_mkr = image_maker() | |
| svg_of_name = doc_dir + '/pbds_' + test_name + '.svg' | |
| image_mkr.make(res, svg_of_name) | |
| if __name__ == "__main__": | |
| """ | |
| This module takes 3 parameters from the command line: | |
| Tests info XML file name | |
| Test results directory | |
| Image output directory | |
| """ | |
| usg = "make_graph.py <test_info_file> <res_dir> <image_dir>\n" | |
| if len(sys.argv) != 4: | |
| sys.stderr.write(usg) | |
| raise exception | |
| main(sys.argv[1], sys.argv[2], sys.argv[3]) |