Skip to content

Commit 2ed57c6

Browse files
authored
Added analysis command and json output (#76)
new command for json output
1 parent b5736e2 commit 2ed57c6

File tree

5 files changed

+246
-1
lines changed

5 files changed

+246
-1
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,22 @@ You just need to use `deepview memory` instead of `deepview time`.
7373
python3 -m deepview_profile memory entry_point.py --output my_output_file.sqlite
7474
```
7575

76+
To export various available analysis to json file, you may use `deepview analysis --all` command for exact entry point and output file. It is required to later view the analysis on the web viewer.
77+
78+
It is also possible to run several optional analysis. There are such analysis available: `--measure-breakdown`, `--measure-throughput`, `--habitat-predict`, `--measure-utilization`, `--energy-compute`, `--exclude-source`
79+
80+
```zsh
81+
python3 -m deepview_profile analysis entry_point.py --all --exclude-source --output=complete_analysis.json
82+
```
83+
84+
`--exclude-source` option allows not adding `encodedFiles` section to output, that is available for `--measure-breakdown` analysis
85+
86+
or various combinations of optional analysis
87+
88+
```zsh
89+
python3 -m deepview_profile analysis entry_point.py --measure-breakdown --measure-throughput --habitat-predict --measure-utilization --energy-compute --output=various_analysis.json
90+
```
91+
7692
<h2 id="dev-setup">Development Environment Setup</h2>
7793

7894
From the project root, do

deepview_profile/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import deepview_profile.commands.interactive
99
import deepview_profile.commands.memory
1010
import deepview_profile.commands.time
11+
import deepview_profile.commands.analysis
1112

1213

1314
def main():
@@ -25,6 +26,7 @@ def main():
2526
deepview_profile.commands.interactive.register_command(subparsers)
2627
deepview_profile.commands.memory.register_command(subparsers)
2728
deepview_profile.commands.time.register_command(subparsers)
29+
deepview_profile.commands.analysis.register_command(subparsers)
2830
args = parser.parse_args()
2931

3032
if args.version:

deepview_profile/commands/analysis.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import logging
2+
import os
3+
import sys
4+
import json
5+
import platform
6+
7+
from deepview_profile.analysis.runner import analyze_project
8+
from deepview_profile.nvml import NVML
9+
from deepview_profile.utils import release_memory, next_message_to_dict, files_encoded_unique
10+
11+
from deepview_profile.initialization import (
12+
check_skyline_preconditions,
13+
initialize_skyline,
14+
)
15+
from deepview_profile.error_printing import print_analysis_error
16+
17+
logger = logging.getLogger(__name__)
18+
19+
def register_command(subparsers):
20+
parser = subparsers.add_parser(
21+
"analysis",
22+
help="Generate usage report for various analysis.",
23+
)
24+
parser.add_argument(
25+
"entry_point",
26+
help="The entry point file in this project that contains the DeepView "
27+
"provider functions."
28+
)
29+
parser.add_argument(
30+
"--all",
31+
action="store_true",
32+
help="The complete analysis of all methods"
33+
)
34+
parser.add_argument(
35+
"-breakdown", "--measure-breakdown",
36+
action="store_true",
37+
help="Adds breakdown data to results"
38+
)
39+
parser.add_argument(
40+
"-throughput", "--measure-throughput",
41+
action="store_true",
42+
help="Adds throughput data to results"
43+
)
44+
parser.add_argument(
45+
"-predict", "--habitat-predict",
46+
action="store_true",
47+
help="Adds habitat data prediction to results"
48+
)
49+
parser.add_argument(
50+
"-utilization", "--measure-utilization",
51+
action="store_true",
52+
help="Adds utilization data to results"
53+
)
54+
parser.add_argument(
55+
"-energy", "--energy-compute",
56+
action="store_true",
57+
help="Adds energy use to results"
58+
)
59+
parser.add_argument(
60+
"-o", "--output",
61+
help="The location where the complete report should be stored",
62+
required=True
63+
)
64+
parser.add_argument(
65+
"--log-file",
66+
help="The location of the log file",
67+
)
68+
parser.add_argument(
69+
"--exclude-source",
70+
action="store_true",
71+
help="Allows not adding encodedFiles section"
72+
)
73+
parser.add_argument("--debug", action="store_true", help="Log debug messages.")
74+
parser.set_defaults(func=main)
75+
76+
def measure_breakdown(session, nvml):
77+
print("analysis: running measure_breakdown()")
78+
yield session.measure_breakdown(nvml)
79+
release_memory()
80+
81+
def measure_throughput(session):
82+
print("analysis: running measure_throughput()")
83+
yield session.measure_throughput()
84+
release_memory()
85+
86+
def habitat_predict(session):
87+
print("analysis: running deepview_predict()")
88+
yield session.habitat_predict()
89+
release_memory()
90+
91+
def measure_utilization(session):
92+
print("analysis: running measure_utilization()")
93+
yield session.measure_utilization()
94+
release_memory()
95+
96+
def energy_compute(session):
97+
print("analysis: running energy_compute()")
98+
yield session.energy_compute()
99+
release_memory()
100+
101+
def hardware_information(nvml):
102+
103+
hardware_info = {
104+
'hostname': platform.node(),
105+
'os': " ".join(list(platform.uname())),
106+
'gpus': nvml.get_device_names()
107+
}
108+
return hardware_info
109+
110+
def actual_main(args):
111+
from deepview_profile.analysis.session import AnalysisSession
112+
from deepview_profile.exceptions import AnalysisError
113+
114+
if os.path.exists(args.output):
115+
print(
116+
"ERROR: The specified output file already exists.",
117+
file=sys.stderr,
118+
)
119+
sys.exit(1)
120+
121+
try:
122+
project_root = os.getcwd()
123+
data = {
124+
"analysisState": {
125+
"message_type": "analysis",
126+
"project_root": project_root,
127+
"project_entry_point": args.entry_point,
128+
"hardware_info": {},
129+
"throughput": {},
130+
"breakdown": {},
131+
"habitat": {},
132+
"additionalProviders": "",
133+
"energy": {},
134+
"utilization": {}
135+
},
136+
"epochs": 50,
137+
"iterPerEpoch": 1000,
138+
"encodedFiles": []
139+
}
140+
141+
session = AnalysisSession.new_from(project_root, args.entry_point)
142+
release_memory()
143+
144+
is_return_all = args.all
145+
146+
with NVML() as nvml:
147+
data['analysisState']['hardware_info'] = hardware_information(nvml)
148+
if args.measure_breakdown or is_return_all:
149+
data['analysisState']['breakdown'] = next_message_to_dict(measure_breakdown(session, nvml))
150+
151+
operation_tree = data['analysisState']['breakdown']['operationTree']
152+
if not args.exclude_source and operation_tree is not None:
153+
data['encodedFiles'] = files_encoded_unique(operation_tree)
154+
155+
if args.measure_throughput or is_return_all:
156+
data['analysisState']['throughput'] = next_message_to_dict(measure_throughput(session))
157+
158+
if args.habitat_predict or is_return_all:
159+
data['analysisState']['habitat'] = next_message_to_dict(habitat_predict(session))
160+
161+
if args.measure_utilization or is_return_all:
162+
data['analysisState']['utilization'] = next_message_to_dict(measure_utilization(session))
163+
164+
if args.energy_compute or is_return_all:
165+
data['analysisState']['energy'] = next_message_to_dict(energy_compute(session))
166+
167+
with open(args.output, "w") as json_file:
168+
json.dump(data, json_file, indent=4)
169+
170+
except AnalysisError as ex:
171+
print_analysis_error(ex)
172+
sys.exit(1)
173+
174+
def main(args):
175+
check_skyline_preconditions(args)
176+
initialize_skyline(args)
177+
actual_main(args)

deepview_profile/nvml.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,12 @@ def get_memory_capacity(self):
1919
# TODO: Support multiple devices
2020
handle = pynvml.nvmlDeviceGetHandleByIndex(0)
2121
return pynvml.nvmlDeviceGetMemoryInfo(handle)
22+
23+
def get_device_names(self):
24+
device_names = []
25+
for i in range(pynvml.nvmlDeviceGetCount()):
26+
handle = pynvml.nvmlDeviceGetHandleByIndex(i)
27+
device_name = pynvml.nvmlDeviceGetName(handle).decode("utf-8")
28+
device_names.append(device_name)
29+
return device_names
30+

deepview_profile/utils.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,51 @@
11
import torch
22
import logging
33
import gc
4+
import os
5+
import base64
6+
7+
from google.protobuf.json_format import MessageToDict
48

59
logger = logging.getLogger(__name__)
610

711
def release_memory():
812
logger.debug("Emptying cache")
913
gc.collect()
10-
torch.cuda.empty_cache()
14+
torch.cuda.empty_cache()
15+
16+
def next_message_to_dict(object):
17+
message = next(object)
18+
return MessageToDict(message)
19+
20+
def files_encoded_unique(operation_tree):
21+
encoded_files = []
22+
23+
for analysis in operation_tree:
24+
context_info_map = analysis['operation'].get('contextInfoMap', None)
25+
if context_info_map is not None and len(context_info_map) > 0:
26+
filename = list(context_info_map[0]['context']['filePath']['components']).pop()
27+
28+
already_in_list = next((item for item in encoded_files if item['name'] == filename), None)
29+
if not already_in_list:
30+
file_path = os.path.join("", *list(context_info_map[0]['context']['filePath']['components']))
31+
32+
encoded_file = encode_file("", file_path)
33+
encoded_files.append(encoded_file)
34+
35+
return encoded_files
36+
37+
def encode_file(root, file):
38+
file_dict = None
39+
if os.path.splitext(file)[1] == ".py" and file != "entry_point.py":
40+
file_dict = {
41+
"name": file,
42+
"content": ""
43+
}
44+
45+
filename = os.path.join(root, file)
46+
47+
with open(filename, "r") as f:
48+
file_content = f.read()
49+
file_dict["content"] = base64.b64encode(file_content.encode("utf-8")).decode("utf-8")
50+
51+
return file_dict

0 commit comments

Comments
 (0)