Spaces:
Runtime error
Runtime error
#! /usr/bin/env python | |
# -*- coding: utf-8 -*- | |
############################################################################### | |
# Copyright (c) 2012-7 Bryce Adelstein Lelbach aka wash <[email protected]> | |
# | |
# Distributed under the Boost Software License, Version 1.0. (See accompanying | |
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
############################################################################### | |
############################################################################### | |
# Copyright (c) 2018 NVIDIA Corporation | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
############################################################################### | |
# XXX Put code shared with `compare_benchmark_results.py` in a common place. | |
# XXX Relative uncertainty. | |
from sys import exit, stdout | |
from os.path import splitext | |
from itertools import imap # Lazy map. | |
from math import sqrt, log10, floor | |
from collections import deque | |
from argparse import ArgumentParser as argument_parser | |
from csv import DictReader as csv_dict_reader | |
from csv import DictWriter as csv_dict_writer | |
from re import compile as regex_compile | |
############################################################################### | |
def unpack_tuple(f): | |
"""Return a unary function that calls `f` with its argument unpacked.""" | |
return lambda args: f(*iter(args)) | |
def strip_dict(d): | |
"""Strip leading and trailing whitespace from all keys and values in `d`.""" | |
d.update({key: value.strip() for (key, value) in d.items()}) | |
def merge_dicts(d0, d1): | |
"""Create a new `dict` that is the union of `dict`s `d0` and `d1`.""" | |
d = d0.copy() | |
d.update(d1) | |
return d | |
def strip_list(l): | |
"""Strip leading and trailing whitespace from all values in `l`.""" | |
for i, value in enumerate(l): l[i] = value.strip() | |
############################################################################### | |
def int_or_float(x): | |
"""Convert `x` to either `int` or `float`, preferring `int`. | |
Raises: | |
ValueError : If `x` is not convertible to either `int` or `float` | |
""" | |
try: | |
return int(x) | |
except ValueError: | |
return float(x) | |
def try_int_or_float(x): | |
"""Try to convert `x` to either `int` or `float`, preferring `int`. `x` is | |
returned unmodified if conversion fails. | |
""" | |
try: | |
return int_or_float(x) | |
except ValueError: | |
return x | |
############################################################################### | |
def find_significant_digit(x): | |
"""Return the significant digit of the number x. The result is the number of | |
digits after the decimal place to round to (negative numbers indicate rounding | |
before the decimal place).""" | |
if x == 0: return 0 | |
return -int(floor(log10(abs(x)))) | |
def round_with_int_conversion(x, ndigits = None): | |
"""Rounds `x` to `ndigits` after the the decimal place. If `ndigits` is less | |
than 1, convert the result to `int`. If `ndigits` is `None`, the significant | |
digit of `x` is used.""" | |
if ndigits is None: ndigits = find_significant_digit(x) | |
x_rounded = round(x, ndigits) | |
return int(x_rounded) if ndigits < 1 else x_rounded | |
############################################################################### | |
class measured_variable(object): | |
"""A meta-variable representing measured data. It is composed of three raw | |
variables plus units meta-data. | |
Attributes: | |
quantity (`str`) : | |
Name of the quantity variable of this object. | |
uncertainty (`str`) : | |
Name of the uncertainty variable of this object. | |
sample_size (`str`) : | |
Name of the sample size variable of this object. | |
units (units class or `None`) : | |
The units the value is measured in. | |
""" | |
def __init__(self, quantity, uncertainty, sample_size, units = None): | |
self.quantity = quantity | |
self.uncertainty = uncertainty | |
self.sample_size = sample_size | |
self.units = units | |
def as_tuple(self): | |
return (self.quantity, self.uncertainty, self.sample_size, self.units) | |
def __iter__(self): | |
return iter(self.as_tuple()) | |
def __str__(self): | |
return str(self.as_tuple()) | |
def __repr__(self): | |
return str(self) | |
class measured_value(object): | |
"""An object that represents a value determined by multiple measurements. | |
Attributes: | |
quantity (scalar) : | |
The quantity of the value, e.g. the arithmetic mean. | |
uncertainty (scalar) : | |
The measurement uncertainty, e.g. the sample standard deviation. | |
sample_size (`int`) : | |
The number of observations contributing to the value. | |
units (units class or `None`) : | |
The units the value is measured in. | |
""" | |
def __init__(self, quantity, uncertainty, sample_size = 1, units = None): | |
self.quantity = quantity | |
self.uncertainty = uncertainty | |
self.sample_size = sample_size | |
self.units = units | |
def as_tuple(self): | |
return (self.quantity, self.uncertainty, self.sample_size, self.units) | |
def __iter__(self): | |
return iter(self.as_tuple()) | |
def __str__(self): | |
return str(self.as_tuple()) | |
def __repr__(self): | |
return str(self) | |
############################################################################### | |
def arithmetic_mean(X): | |
"""Computes the arithmetic mean of the sequence `X`. | |
Let: | |
* `n = len(X)`. | |
* `u` denote the arithmetic mean of `X`. | |
.. math:: | |
u = \frac{\sum_{i = 0}^{n - 1} X_i}{n} | |
""" | |
return sum(X) / len(X) | |
def sample_variance(X, u = None): | |
"""Computes the sample variance of the sequence `X`. | |
Let: | |
* `n = len(X)`. | |
* `u` denote the arithmetic mean of `X`. | |
* `s` denote the sample standard deviation of `X`. | |
.. math:: | |
v = \frac{\sum_{i = 0}^{n - 1} (X_i - u)^2}{n - 1} | |
Args: | |
X (`Iterable`) : The sequence of values. | |
u (number) : The arithmetic mean of `X`. | |
""" | |
if u is None: u = arithmetic_mean(X) | |
return sum(imap(lambda X_i: (X_i - u) ** 2, X)) / (len(X) - 1) | |
def sample_standard_deviation(X, u = None, v = None): | |
"""Computes the sample standard deviation of the sequence `X`. | |
Let: | |
* `n = len(X)`. | |
* `u` denote the arithmetic mean of `X`. | |
* `v` denote the sample variance of `X`. | |
* `s` denote the sample standard deviation of `X`. | |
.. math:: | |
s &= \sqrt{v} | |
&= \sqrt{\frac{\sum_{i = 0}^{n - 1} (X_i - u)^2}{n - 1}} | |
Args: | |
X (`Iterable`) : The sequence of values. | |
u (number) : The arithmetic mean of `X`. | |
v (number) : The sample variance of `X`. | |
""" | |
if u is None: u = arithmetic_mean(X) | |
if v is None: v = sample_variance(X, u) | |
return sqrt(v) | |
def combine_sample_size(As): | |
"""Computes the combined sample variance of a group of `measured_value`s. | |
Let: | |
* `g = len(As)`. | |
* `n_i = As[i].samples`. | |
* `n` denote the combined sample size of `As`. | |
.. math:: | |
n = \sum{i = 0}^{g - 1} n_i | |
""" | |
return sum(imap(unpack_tuple(lambda u_i, s_i, n_i, t_i: n_i), As)) | |
def combine_arithmetic_mean(As, n = None): | |
"""Computes the combined arithmetic mean of a group of `measured_value`s. | |
Let: | |
* `g = len(As)`. | |
* `u_i = As[i].quantity`. | |
* `n_i = As[i].samples`. | |
* `n` denote the combined sample size of `As`. | |
* `u` denote the arithmetic mean of the quantities of `As`. | |
.. math:: | |
u = \frac{\sum{i = 0}^{g - 1} n_i u_i}{n} | |
""" | |
if n is None: n = combine_sample_size(As) | |
return sum(imap(unpack_tuple(lambda u_i, s_i, n_i, t_i: n_i * u_i), As)) / n | |
def combine_sample_variance(As, n = None, u = None): | |
"""Computes the combined sample variance of a group of `measured_value`s. | |
Let: | |
* `g = len(As)`. | |
* `u_i = As[i].quantity`. | |
* `s_i = As[i].uncertainty`. | |
* `n_i = As[i].samples`. | |
* `n` denote the combined sample size of `As`. | |
* `u` denote the arithmetic mean of the quantities of `As`. | |
* `v` denote the sample variance of `X`. | |
.. math:: | |
v = \frac{(\sum_{i = 0}^{g - 1} n_i (u_i - u)^2 + s_i^2 (n_i - 1))}{n - 1} | |
Args: | |
As (`Iterable` of `measured_value`s) : The sequence of values. | |
n (number) : The combined sample sizes of `As`. | |
u (number) : The combined arithmetic mean of `As`. | |
""" | |
if n <= 1: return 0 | |
if n is None: n = combine_sample_size(As) | |
if u is None: u = combine_arithmetic_mean(As, n) | |
return sum(imap(unpack_tuple( | |
lambda u_i, s_i, n_i, t_i: n_i * (u_i - u) ** 2 + (s_i ** 2) * (n_i - 1) | |
), As)) / (n - 1) | |
def combine_sample_standard_deviation(As, n = None, u = None, v = None): | |
"""Computes the combined sample standard deviation of a group of | |
`measured_value`s. | |
Let: | |
* `g = len(As)`. | |
* `u_i = As[i].quantity`. | |
* `s_i = As[i].uncertainty`. | |
* `n_i = As[i].samples`. | |
* `n` denote the combined sample size of `As`. | |
* `u` denote the arithmetic mean of the quantities of `As`. | |
* `v` denote the sample variance of `X`. | |
* `s` denote the sample standard deviation of `X`. | |
.. math:: | |
s &= \sqrt{v} | |
&= \sqrt{\frac{(\sum_{i = 0}^{g - 1} n_i (u_i - u)^2 + s_i^2 (n_i - 1))}{n - 1}} | |
Args: | |
As (`Iterable` of `measured_value`s) : The sequence of values. | |
n (number) : The combined sample sizes of `As`. | |
u (number) : The combined arithmetic mean of `As`. | |
v (number) : The combined sample variance of `As`. | |
""" | |
if n <= 1: return 0 | |
if n is None: n = combine_sample_size(As) | |
if u is None: u = combine_arithmetic_mean(As, n) | |
if v is None: v = combine_sample_variance(As, n, u) | |
return sqrt(v) | |
############################################################################### | |
def process_program_arguments(): | |
ap = argument_parser( | |
description = ( | |
"Aggregates the results of multiple runs of benchmark results stored in " | |
"CSV format." | |
) | |
) | |
ap.add_argument( | |
"-d", "--dependent-variable", | |
help = ("Treat the specified three variables as a dependent variable. The " | |
"1st variable is the measured quantity, the 2nd is the uncertainty " | |
"of the measurement and the 3rd is the sample size. The defaults " | |
"are the dependent variables of Thrust's benchmark suite. May be " | |
"specified multiple times."), | |
action = "append", type = str, dest = "dependent_variables", | |
metavar = "QUANTITY,UNCERTAINTY,SAMPLES" | |
) | |
ap.add_argument( | |
"-p", "--preserve-whitespace", | |
help = ("Don't trim leading and trailing whitespace from each CSV cell."), | |
action = "store_true", default = False | |
) | |
ap.add_argument( | |
"-o", "--output-file", | |
help = ("The file that results are written to. If `-`, results are " | |
"written to stdout."), | |
action = "store", type = str, default = "-", | |
metavar = "OUTPUT" | |
) | |
ap.add_argument( | |
"input_files", | |
help = ("Input CSV files. The first two rows should be a header. The 1st " | |
"header row specifies the name of each variable, and the 2nd " | |
"header row specifies the units for that variable."), | |
type = str, nargs = "+", | |
metavar = "INPUTS" | |
) | |
return ap.parse_args() | |
############################################################################### | |
def filter_comments(f, s = "#"): | |
"""Return an iterator to the file `f` which filters out all lines beginning | |
with `s`.""" | |
return filter(lambda line: not line.startswith(s), f) | |
############################################################################### | |
class io_manager(object): | |
"""Manages I/O operations and represents the input data as an `Iterable` | |
sequence of `dict`s. | |
It is `Iterable` and an `Iterator`. It can be used with `with`. | |
Attributes: | |
preserve_whitespace (`bool`) : | |
If `False`, leading and trailing whitespace is stripped from each CSV cell. | |
writer (`csv_dict_writer`) : | |
CSV writer object that the output is written to. | |
output_file (`file` or `stdout`) : | |
The output `file` object. | |
readers (`list` of `csv_dict_reader`s) : | |
List of input files as CSV reader objects. | |
input_files (list of `file`s) : | |
List of input `file` objects. | |
variable_names (`list` of `str`s) : | |
Names of the variables, in order. | |
variable_units (`list` of `str`s) : | |
Units of the variables, in order. | |
""" | |
def __init__(self, input_files, output_file, preserve_whitespace = True): | |
"""Read input files and open the output file and construct a new `io_manager` | |
object. | |
If `preserve_whitespace` is `False`, leading and trailing whitespace is | |
stripped from each CSV cell. | |
Raises | |
AssertionError : | |
If `len(input_files) <= 0` or `type(preserve_whitespace) != bool`. | |
""" | |
assert len(input_files) > 0, "No input files provided." | |
assert type(preserve_whitespace) == bool | |
self.preserve_whitespace = preserve_whitespace | |
self.readers = deque() | |
self.variable_names = None | |
self.variable_units = None | |
self.input_files = deque() | |
for input_file in input_files: | |
input_file_object = open(input_file) | |
reader = csv_dict_reader(filter_comments(input_file_object)) | |
if not self.preserve_whitespace: | |
strip_list(reader.fieldnames) | |
if self.variable_names is None: | |
self.variable_names = reader.fieldnames | |
else: | |
# Make sure all inputs have the same schema. | |
assert self.variable_names == reader.fieldnames, \ | |
"Input file (`" + input_file + "`) variable schema `" + \ | |
str(reader.fieldnames) + "` does not match the variable schema `" + \ | |
str(self.variable_names) + "`." | |
# Consume the next row, which should be the second line of the header. | |
variable_units = reader.next() | |
if not self.preserve_whitespace: | |
strip_dict(variable_units) | |
if self.variable_units is None: | |
self.variable_units = variable_units | |
else: | |
# Make sure all inputs have the same units schema. | |
assert self.variable_units == variable_units, \ | |
"Input file (`" + input_file + "`) units schema `" + \ | |
str(variable_units) + "` does not match the units schema `" + \ | |
str(self.variable_units) + "`." | |
self.readers.append(reader) | |
self.input_files.append(input_file_object) | |
if output_file == "-": # Output to stdout. | |
self.output_file = stdout | |
else: # Output to user-specified file. | |
self.output_file = open(output_file, "w") | |
self.writer = csv_dict_writer( | |
self.output_file, fieldnames = self.variable_names | |
) | |
def __enter__(self): | |
"""Called upon entering a `with` statement.""" | |
return self | |
def __exit__(self, *args): | |
"""Called upon exiting a `with` statement.""" | |
if self.output_file is stdout: | |
self.output_file = None | |
elif self.output_file is not None: | |
self.output_file.__exit__(*args) | |
for input_file in self.input_files: | |
input_file.__exit__(*args) | |
############################################################################# | |
# Input Stream. | |
def __iter__(self): | |
"""Return an iterator to the input sequence. | |
This is a requirement for the `Iterable` protocol. | |
""" | |
return self | |
def next(self): | |
"""Consume and return the next record (a `dict` representing a CSV row) in | |
the input. | |
This is a requirement for the `Iterator` protocol. | |
Raises: | |
StopIteration : If there is no more input. | |
""" | |
if len(self.readers) == 0: | |
raise StopIteration() | |
try: | |
row = self.readers[0].next() | |
if not self.preserve_whitespace: strip_dict(row) | |
return row | |
except StopIteration: | |
# The current reader is empty, so pop it, pop it's input file, close the | |
# input file, and then call ourselves again. | |
self.readers.popleft() | |
self.input_files.popleft().close() | |
return self.next() | |
############################################################################# | |
# Output. | |
def write_header(self): | |
"""Write the header for the output CSV file.""" | |
# Write the first line of the header. | |
self.writer.writeheader() | |
# Write the second line of the header. | |
self.writer.writerow(self.variable_units) | |
def write(self, d): | |
"""Write a record (a `dict`) to the output CSV file.""" | |
self.writer.writerow(d) | |
############################################################################### | |
class dependent_variable_parser(object): | |
"""Parses a `--dependent-variable=AVG,STDEV,TRIALS` command line argument.""" | |
############################################################################# | |
# Grammar | |
# Parse a variable_name. | |
variable_name_rule = r'[^,]+' | |
# Parse a variable classification. | |
dependent_variable_rule = r'(' + variable_name_rule + r')' \ | |
+ r',' \ | |
+ r'(' + variable_name_rule + r')' \ | |
+ r',' \ | |
+ r'(' + variable_name_rule + r')' | |
engine = regex_compile(dependent_variable_rule) | |
############################################################################# | |
def __call__(self, s): | |
"""Parses the string `s` with the form "AVG,STDEV,TRIALS". | |
Returns: | |
A `measured_variable`. | |
Raises: | |
AssertionError : If parsing fails. | |
""" | |
match = self.engine.match(s) | |
assert match is not None, \ | |
"Dependent variable (-d) `" +s+ "` is invalid, the format is " + \ | |
"`AVG,STDEV,TRIALS`." | |
return measured_variable(match.group(1), match.group(2), match.group(3)) | |
############################################################################### | |
class record_aggregator(object): | |
"""Consumes and combines records and represents the result as an `Iterable` | |
sequence of `dict`s. | |
It is `Iterable` and an `Iterator`. | |
Attributes: | |
dependent_variables (`list` of `measured_variable`s) : | |
A list of dependent variables provided on the command line. | |
dataset (`dict`) : | |
A mapping of distinguishing (e.g. control + independent) values (`tuple`s | |
of variable-quantity pairs) to `list`s of dependent values (`dict`s from | |
variables to lists of cells). | |
in_order_dataset_keys : | |
A list of unique dataset keys (e.g. distinguishing variables) in order of | |
appearance. | |
""" | |
parse_dependent_variable = dependent_variable_parser() | |
def __init__(self, raw_dependent_variables): | |
"""Parse dependent variables and construct a new `record_aggregator` object. | |
Raises: | |
AssertionError : If parsing of dependent variables fails. | |
""" | |
self.dependent_variables = [] | |
if raw_dependent_variables is not None: | |
for variable in raw_dependent_variables: | |
self.dependent_variables.append(self.parse_dependent_variable(variable)) | |
self.dataset = {} | |
self.in_order_dataset_keys = deque() | |
############################################################################# | |
# Insertion. | |
def append(self, record): | |
"""Add `record` to the dataset. | |
Raises: | |
ValueError : If any `str`-to-numeric conversions fail. | |
""" | |
# The distinguishing variables are the control and independent variables. | |
# They form the key for each record in the dataset. Records with the same | |
# distinguishing variables are treated as observations of the same data | |
# point. | |
dependent_values = {} | |
# To allow the same sample size variable to be used for multiple dependent | |
# variables, we don't pop sample size variables until we're done processing | |
# all variables. | |
sample_size_variables = [] | |
# Separate the dependent values from the distinguishing variables and | |
# perform `str`-to-numeric conversions. | |
for variable in self.dependent_variables: | |
quantity, uncertainty, sample_size, units = variable.as_tuple() | |
dependent_values[quantity] = [int_or_float(record.pop(quantity))] | |
dependent_values[uncertainty] = [int_or_float(record.pop(uncertainty))] | |
dependent_values[sample_size] = [int(record[sample_size])] | |
sample_size_variables.append(sample_size) | |
# Pop sample size variables. | |
for sample_size_variable in sample_size_variables: | |
# Allowed to fail, as we may have duplicates. | |
record.pop(sample_size_variable, None) | |
# `dict`s aren't hashable, so create a tuple of key-value pairs. | |
distinguishing_values = tuple(record.items()) | |
if distinguishing_values in self.dataset: | |
# These distinguishing values already exist, so get the `dict` they're | |
# mapped to, look up each key in `dependent_values` in the `dict`, and | |
# add the corresponding quantity in `dependent_values` to the list in the | |
# the `dict`. | |
for variable, columns in dependent_values.iteritems(): | |
self.dataset[distinguishing_values][variable] += columns | |
else: | |
# These distinguishing values aren't in the dataset, so add them and | |
# record them in `in_order_dataset_keys`. | |
self.dataset[distinguishing_values] = dependent_values | |
self.in_order_dataset_keys.append(distinguishing_values) | |
############################################################################# | |
# Postprocessing. | |
def combine_dependent_values(self, dependent_values): | |
"""Takes a mapping of dependent variables to lists of cells and returns | |
a new mapping with the cells combined. | |
Raises: | |
AssertionError : If class invariants were violated. | |
""" | |
combined_dependent_values = dependent_values.copy() | |
for variable in self.dependent_variables: | |
quantity, uncertainty, sample_size, units = variable.as_tuple() | |
quantities = dependent_values[quantity] | |
uncertainties = dependent_values[uncertainty] | |
sample_sizes = dependent_values[sample_size] | |
if type(sample_size) is list: | |
# Sample size hasn't been combined yet. | |
assert len(quantities) == len(uncertainties) \ | |
and len(uncertainties) == len(sample_sizes), \ | |
"Length of quantities list `(" + str(len(quantities)) + ")`, " + \ | |
"length of uncertainties list `(" + str(len(uncertainties)) + \ | |
"),` and length of sample sizes list `(" + str(len(sample_sizes)) + \ | |
")` are not the same." | |
else: | |
# Another dependent variable that uses our sample size has combined it | |
# already. | |
assert len(quantities) == len(uncertainties), \ | |
"Length of quantities list `(" + str(len(quantities)) + ")` and " + \ | |
"length of uncertainties list `(" + str(len(uncertainties)) + \ | |
")` are not the same." | |
# Convert the three separate `list`s into one list of `measured_value`s. | |
measured_values = [] | |
for i in range(len(quantities)): | |
mv = measured_value( | |
quantities[i], uncertainties[i], sample_sizes[i], units | |
) | |
measured_values.append(mv) | |
# Combine the `measured_value`s. | |
combined_sample_size = combine_sample_size( | |
measured_values | |
) | |
combined_arithmetic_mean = combine_arithmetic_mean( | |
measured_values, combined_sample_size | |
) | |
combined_sample_standard_deviation = combine_sample_standard_deviation( | |
measured_values, combined_sample_size, combined_arithmetic_mean | |
) | |
# Round the quantity and uncertainty to the significant digit of | |
# uncertainty and insert the combined values into the results. | |
sigdig = find_significant_digit(combined_sample_standard_deviation) | |
# combined_arithmetic_mean = round_with_int_conversion( | |
# combined_arithmetic_mean, sigdig | |
# ) | |
# combined_sample_standard_deviation = round_with_int_conversion( | |
# combined_sample_standard_deviation, sigdig | |
# ) | |
combined_dependent_values[quantity] = combined_arithmetic_mean | |
combined_dependent_values[uncertainty] = combined_sample_standard_deviation | |
combined_dependent_values[sample_size] = combined_sample_size | |
return combined_dependent_values | |
############################################################################# | |
# Output Stream. | |
def __iter__(self): | |
"""Return an iterator to the output sequence of separated distinguishing | |
variables and dependent variables (a tuple of two `dict`s). | |
This is a requirement for the `Iterable` protocol. | |
""" | |
return self | |
def records(self): | |
"""Return an iterator to the output sequence of CSV rows (`dict`s of | |
variables to values). | |
""" | |
return imap(unpack_tuple(lambda dist, dep: merge_dicts(dist, dep)), self) | |
def next(self): | |
"""Produce the components of the next output record - a tuple of two | |
`dict`s. The first `dict` is a mapping of distinguishing variables to | |
distinguishing values, the second `dict` is a mapping of dependent | |
variables to combined dependent values. Combining the two dicts forms a | |
CSV row suitable for output. | |
This is a requirement for the `Iterator` protocol. | |
Raises: | |
StopIteration : If there is no more output. | |
AssertionError : If class invariants were violated. | |
""" | |
assert len(self.dataset.keys()) == len(self.in_order_dataset_keys), \ | |
"Number of dataset keys (`" + str(len(self.dataset.keys())) + \ | |
"`) is not equal to the number of keys in the ordering list (`" + \ | |
str(len(self.in_order_dataset_keys)) + "`)." | |
if len(self.in_order_dataset_keys) == 0: | |
raise StopIteration() | |
# Get the next set of distinguishing values and convert them to a `dict`. | |
raw_distinguishing_values = self.in_order_dataset_keys.popleft() | |
distinguishing_values = dict(raw_distinguishing_values) | |
dependent_values = self.dataset.pop(raw_distinguishing_values) | |
combined_dependent_values = self.combine_dependent_values(dependent_values) | |
return (distinguishing_values, combined_dependent_values) | |
############################################################################### | |
args = process_program_arguments() | |
if args.dependent_variables is None: | |
args.dependent_variables = [ | |
"STL Average Walltime,STL Walltime Uncertainty,STL Trials", | |
"STL Average Throughput,STL Throughput Uncertainty,STL Trials", | |
"Thrust Average Walltime,Thrust Walltime Uncertainty,Thrust Trials", | |
"Thrust Average Throughput,Thrust Throughput Uncertainty,Thrust Trials" | |
] | |
# Read input files and open the output file. | |
with io_manager(args.input_files, | |
args.output_file, | |
args.preserve_whitespace) as iom: | |
# Parse dependent variable options. | |
ra = record_aggregator(args.dependent_variables) | |
# Add all input data to the `record_aggregator`. | |
for record in iom: | |
ra.append(record) | |
iom.write_header() | |
# Write combined results out. | |
for record in ra.records(): | |
iom.write(record) | |