Skip to content

Commit ca077e5

Browse files
committed
Adds an analysis of the sub-tracked code in addition to a comparison of the analysis between tracking sessions
1 parent 98933dd commit ca077e5

File tree

5 files changed

+806
-100
lines changed

5 files changed

+806
-100
lines changed

src/gpu_tracker/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,4 @@
1010
__version__ = _gv(_path.join(_path.dirname(__file__), _path.pardir))
1111

1212
from .tracker import Tracker
13-
from .sub_tracker import SubTracker
14-
from .sub_tracker import sub_track
13+
from .sub_tracker import SubTracker, sub_track, SubTrackingAnalyzer, TrackingComparison

src/gpu_tracker/__main__.py

Lines changed: 117 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
Usage:
55
gpu-tracker -h | --help
66
gpu-tracker -v | --version
7-
gpu-tracker --execute=<command> [--output=<output>] [--format=<format>] [--st=<sleep-time>] [--ru=<ram-unit>] [--gru=<gpu-ram-unit>] [--tu=<time-unit>] [--nec=<num-cores>] [--guuids=<gpu-uuids>] [--disable-logs] [--gb=<gpu-brand>] [--tf=<tracking-file>]
7+
gpu-tracker --execute=<command> [--output=<output>] [--format=<format>] [--tconfig=<config-file>] [--st=<sleep-time>] [--ru=<ram-unit>] [--gru=<gpu-ram-unit>] [--tu=<time-unit>] [--nec=<num-cores>] [--guuids=<gpu-uuids>] [--disable-logs] [--gb=<gpu-brand>] [--tf=<tracking-file>] [--overwrite]
8+
gpu-tracker sub-track combine --stf=<sub-track-file> -p <file-path>...
9+
gpu-tracker sub-track analyze --tf=<tracking-file> --stf=<sub-track-file> [--output=<output>] [--format=<format>]
10+
gpu-tracker sub-track compare [--output=<output>] [--format=<format>] [--cconfig=<config-file>] [-m <name>=<file-path>...] [--stat=<statistic>]
811
912
Options:
1013
-h --help Show this help message and exit.
1114
-v --version Show package version and exit.
1215
-e --execute=<command> The command to run along with its arguments all within quotes e.g. "ls -l -a".
13-
-o --output=<output> File path to store the computational-resource-usage measurements. If not set, prints measurements to the screen.
14-
-f --format=<format> File format of the output. Either 'json' or 'text'. Defaults to 'text'.
16+
-o --output=<output> File path to store the computational-resource-usage measurements in the case of tracking or the analysis report in the case of sub-tracking. If not set, prints to the screen.
17+
-f --format=<format> File format of the output. Either 'json', 'text', or 'pickle'. Defaults to 'text'.
18+
--tconfig=<config-file> JSON config file containing the key word arguments to the ``Tracker`` class (see API) to be optionally used instead of the corresponding commandline options. If any commandline options are set, they will override the corresponding arguments provided by the config file.
1519
--st=<sleep-time> The number of seconds to sleep in between usage-collection iterations.
1620
--ru=<ram-unit> One of 'bytes', 'kilobytes', 'megabytes', 'gigabytes', or 'terabytes'.
1721
--gru=<gpu-ram-unit> One of 'bytes', 'kilobytes', 'megabytes', 'gigabytes', or 'terabytes'.
@@ -21,65 +25,134 @@
2125
--disable-logs If set, warnings are suppressed during tracking. Otherwise, the Tracker logs warnings as usual.
2226
--gb=<gpu-brand> The brand of GPU to profile. Valid values are nvidia and amd. Defaults to the brand of GPU detected in the system, checking NVIDIA first.
2327
--tf=<tracking-file> If specified, stores the individual resource usage measurements at each iteration. Valid file formats are CSV (.csv) and SQLite (.sqlite) where the SQLite file format stores the data in a table called "data" and allows for more efficient querying.
28+
--overwrite Whether to overwrite the tracking file if it already existed before the beginning of this tracking session. Do not set if the data in the existing tracking file is still needed.
29+
sub-track Perform sub-tracking related commands.
30+
combine Combines multiple sub-tracking files into one. This is usually a result of sub-tracking a code block that is called in multiple simultaneous processes.
31+
--stf=<sub-track-file> The path to the sub-tracking file used to specify the timestamps of specific code-blocks. If not generated by the gpu-tracker API, must be either a CSV or SQLite file (where the SQLite file contains a table called "data") where the headers are precisely process_id, code_block_name, position, and timestamp. The process_id is the ID of the process where the code block is called. code_block_name is the name of the code block. position is whether it is the start or the stopping point of the code block where 0 represents start and 1 represents stop. And timestamp is the timestamp where the code block starts or where it stops.
32+
-p <file-path> Paths to the sub-tracking files to combine. Must all be the same file format and the same file format as the resulting sub-tracking file (either .csv or .sqlite). If only one path is provided, it is interpreted as a path to a directory and all the files in this directory are combined.
33+
analyze Generate the sub-tracking analysis report using the tracking file and sub-tracking file for resource usage of specific code blocks.
34+
compare Compares multiple tracking sessions to determine differences in computational resource usage by loading sub-tracking results given their file paths. Sub-tracking results files must be in pickle format e.g. running the ``sub-track analyze`` command and specifying a file path for ``--output`` and 'pickle' for the ``--format`` option. If code block results are not included in the sub-tracking files (i.e. no code blocks were sub-tracked), then only overall results are compared.
35+
--cconfig=<config-file> JSON config file containing the ``file_path_map`` argument for the ``TrackerComparison`` class and ``statistic`` argument for its ``compare`` method (see API) that can be used instead of the corresponding ``-m <name>=<path>`` and ``--stat=<statistic>`` commandline options respectively. If additional ``-m <name>=<path>`` options are added on the commandline in addition to a config file, they will be added to the ``file_path_map`` in the config file. If a ``--stat`` option is provided on the commandline, it will override the ``statistic`` in the config file.
36+
-m <name>=<file-path> Mapping of tracking session names to the path of the file containing the sub-tracking results of said tracking session. Must be in pickle format.
37+
--stat=<statistic> The summary statistic of the measurements to compare. One of 'min', 'max', 'mean', or 'std'. Defaults to 'mean'.
2438
"""
2539
import docopt as doc
2640
import subprocess as subp
2741
import json
2842
import logging as log
2943
import sys
30-
from . import Tracker
31-
from . import __version__
44+
import os
45+
import pickle as pkl
46+
from . import Tracker, SubTrackingAnalyzer, TrackingComparison, __version__
3247

3348

3449
def main():
3550
args = doc.docopt(__doc__, version=__version__)
36-
command = args['--execute'].split()
3751
output = args['--output']
3852
output_format = args['--format'] if args['--format'] is not None else 'text'
39-
option_map = {
40-
'--st': 'sleep_time',
41-
'--ru': 'ram_unit',
42-
'--gru': 'gpu_ram_unit',
43-
'--tu': 'time_unit',
44-
'--nec': 'n_expected_cores',
45-
'--guuids': 'gpu_uuids',
46-
'--disable-logs': 'disable_logs',
47-
'--gb': 'gpu_brand',
48-
'--tf': 'tracking_file'
49-
}
50-
kwargs = {
51-
option_map[option]: value for option, value in args.items() if value is not None and option not in {
52-
'--execute', '--output', '--format', '--help', '--version'}}
53-
if 'sleep_time' in kwargs.keys():
54-
kwargs['sleep_time'] = float(kwargs['sleep_time'])
55-
if 'n_expected_cores' in kwargs.keys():
56-
kwargs['n_expected_cores'] = int(kwargs['n_expected_cores'])
57-
if 'gpu_uuids' in kwargs.keys():
58-
kwargs['gpu_uuids'] = set(kwargs['gpu_uuids'].split(','))
59-
if len(command) == 0:
60-
log.error('Empty command provided.')
61-
sys.exit(1)
62-
try:
63-
process = subp.Popen(command)
64-
except FileNotFoundError:
65-
log.error(f'Command not found: "{command[0]}"')
66-
sys.exit(1)
67-
except Exception as e:
68-
log.error(f'The following error occurred when starting the command "{command[0]}":')
69-
print(e)
70-
sys.exit(1)
71-
with Tracker(process_id=process.pid, **kwargs) as tracker:
72-
process.wait()
73-
print(f'Resource tracking complete. Process completed with status code: {process.returncode}')
53+
if args['sub-track']:
54+
if args['analyze']:
55+
tracking_file = args['--tf']
56+
sub_tracking_file = args['--stf']
57+
results = SubTrackingAnalyzer(tracking_file, sub_tracking_file).sub_tracking_results()
58+
_process_output(output_format, results, output)
59+
elif args['combine']:
60+
directory = None
61+
files = None
62+
files = args['-p']
63+
if len(files) == 1:
64+
[directory] = files
65+
files = [os.path.join(directory, file) for file in os.listdir(directory)]
66+
SubTrackingAnalyzer(None, args['--stf']).combine_sub_tracking_files(files)
67+
elif args['compare']:
68+
if args['--cconfig'] is not None:
69+
with open(args['--cconfig'], 'r') as file:
70+
config = json.load(file)
71+
file_path_map = config['file_path_map'] if 'file_path_map' in config else None
72+
statistic = config['statistic'] if 'statistic' in config else None
73+
else:
74+
file_path_map = dict[str, str]()
75+
statistic = None
76+
if not file_path_map and args['-m'] is None:
77+
raise ValueError(
78+
f'A mapping of tracking session name to file path must be provided either through the -m option or a config file.'
79+
)
80+
else:
81+
file_path_map.update({name: file for [name, file] in [option.split('=') for option in args['-m']]})
82+
if args['--stat'] is not None:
83+
statistic = args['--stat']
84+
elif statistic is None:
85+
statistic = 'mean'
86+
comparison = TrackingComparison(file_path_map)
87+
results = comparison.compare(statistic)
88+
_process_output(output_format, results, output)
89+
else:
90+
command = args['--execute'].split()
91+
if args['--tconfig'] is not None:
92+
with open(args['--tconfig'], 'r') as file:
93+
kwargs = json.load(file)
94+
else:
95+
kwargs = dict()
96+
option_map = {
97+
'--st': 'sleep_time',
98+
'--ru': 'ram_unit',
99+
'--gru': 'gpu_ram_unit',
100+
'--tu': 'time_unit',
101+
'--nec': 'n_expected_cores',
102+
'--guuids': 'gpu_uuids',
103+
'--disable-logs': 'disable_logs',
104+
'--gb': 'gpu_brand',
105+
'--tf': 'tracking_file',
106+
'--overwrite': 'overwrite'
107+
}
108+
kwargs.update({
109+
option_map[option]: value for option, value in args.items() if value is not None and option not in {
110+
'--execute', '--output', '--format', '--help', '--version', 'sub-track', 'analyze', '--stf', 'combine', '-p', 'compare',
111+
'-m', '--tconfig', '--cconfig'
112+
}
113+
})
114+
if 'sleep_time' in kwargs.keys():
115+
kwargs['sleep_time'] = float(kwargs['sleep_time'])
116+
if 'n_expected_cores' in kwargs.keys():
117+
kwargs['n_expected_cores'] = int(kwargs['n_expected_cores'])
118+
if 'gpu_uuids' in kwargs.keys():
119+
# noinspection PyUnresolvedReferences
120+
kwargs['gpu_uuids'] = set(kwargs['gpu_uuids'].split(','))
121+
if len(command) == 0:
122+
log.error('Empty command provided.')
123+
sys.exit(1)
124+
try:
125+
process = subp.Popen(command)
126+
except FileNotFoundError:
127+
log.error(f'Command not found: "{command[0]}"')
128+
sys.exit(1)
129+
except Exception as e:
130+
log.error(f'The following error occurred when starting the command "{command[0]}":')
131+
print(e)
132+
sys.exit(1)
133+
with Tracker(process_id=process.pid, **kwargs) as tracker:
134+
process.wait()
135+
print(f'Resource tracking complete. Process completed with status code: {process.returncode}')
136+
_process_output(output_format, tracker, output)
137+
138+
139+
def _process_output(output_format: str, output_obj, output: str | None):
74140
if output_format == 'json':
75-
output_str = json.dumps(tracker.to_json(), indent=1)
141+
output_str = json.dumps(output_obj.to_json(), indent=1)
76142
elif output_format == 'text':
77-
output_str = str(tracker)
143+
output_str = str(output_obj)
144+
elif output_format == 'pickle':
145+
output_str = pkl.dumps(output_obj)
78146
else:
79147
log.error(f'"{output_format}" is not a valid format. Valid values are "json" or "text".')
80148
sys.exit(1)
81149
if output is None:
82150
print(output_str)
83151
else:
84-
with open(output, 'w') as file:
152+
mode = 'wb' if output_format == 'pickle' else 'w'
153+
with open(output, mode) as file:
85154
file.write(output_str)
155+
156+
157+
if __name__ == '__main__':
158+
main()

0 commit comments

Comments
 (0)