12
12
import shutil
13
13
import sys
14
14
import argparse
15
+ from collections import deque
15
16
from enum import Enum
16
17
from pathlib import Path
17
- from concurrent .futures import ThreadPoolExecutor
18
- from multiprocessing import Lock
18
+ from multiprocessing import Lock , Process
19
19
import pandas as pd
20
20
from sklearn .metrics import mean_squared_error
21
21
import matplotlib .pyplot as plt
22
22
import distinctipy as dp
23
23
from colour import Color
24
24
import seaborn as sns
25
25
26
+ lock = Lock () # Used for multiprocessing
27
+
26
28
27
29
# pylint: disable=too-many-instance-attributes
28
30
# pylint: disable=too-few-public-methods
@@ -41,8 +43,7 @@ def __init__(self,
41
43
no_replace : bool ,
42
44
should_print : bool ,
43
45
percent_error_threshold : float ,
44
- exclusions : dict ,
45
- pool : ThreadPoolExecutor ):
46
+ exclusions : dict ):
46
47
# Output directory
47
48
self .output_dir = "./vtr_flow/tasks/lookahead_verifier_output"
48
49
# The graph types (pie, heatmap, bar, scatter) that will be created
@@ -106,9 +107,8 @@ def __init__(self,
106
107
"test name"
107
108
]
108
109
109
- # Lock and Pool for multithreading
110
- self .lock = Lock ()
111
- self .pool = pool
110
+ # Processes list for multiprocessing
111
+ self .processes = []
112
112
113
113
114
114
def check_valid_component (comp : str ):
@@ -132,21 +132,21 @@ def check_valid_df(df: pd.DataFrame, gv: GlobalVars):
132
132
raise Exception ("IncompleteDataFrame" )
133
133
134
134
135
- def make_dir (directory : str , clean : bool , gv : GlobalVars ):
135
+ def make_dir (directory : str , clean : bool ):
136
136
"""Create a directory"""
137
137
138
- gv . lock .acquire ()
138
+ lock .acquire ()
139
139
140
140
if os .path .exists (directory ):
141
141
if clean :
142
142
shutil .rmtree (directory )
143
143
else :
144
- gv . lock .release ()
144
+ lock .release ()
145
145
return
146
146
147
147
os .makedirs (directory )
148
148
149
- gv . lock .release ()
149
+ lock .release ()
150
150
151
151
152
152
def column_file_name (column_name : str ) -> str :
@@ -227,7 +227,7 @@ def __init__(
227
227
os .path .join (self .__directory , "proportion_under_threshold" ),
228
228
]
229
229
for directory in self .__sub_dirs :
230
- make_dir (directory , False , gv )
230
+ make_dir (directory , False )
231
231
232
232
self .__test_name = test_name
233
233
@@ -336,7 +336,7 @@ def make_scatter_plot(
336
336
if gv .no_replace and os .path .exists (os .path .join (curr_dir , file_name )):
337
337
return
338
338
339
- make_dir (curr_dir , False , gv )
339
+ make_dir (curr_dir , False )
340
340
341
341
# Determine colors for legend
342
342
num_colors = self .__df [legend_column ].nunique () + 1
@@ -427,7 +427,11 @@ def make_standard_scatter_plots(self, test_name_plot: bool, gv: GlobalVars):
427
427
if first_it and col == "iteration no." :
428
428
continue
429
429
430
- gv .pool .submit (self .make_scatter_plot , comp , plot_type , col , first_it , gv )
430
+ gv .processes .append ((self .make_scatter_plot , (comp ,
431
+ plot_type ,
432
+ col ,
433
+ first_it ,
434
+ gv )))
431
435
432
436
# pylint: disable=too-many-locals
433
437
def make_bar_graph (self ,
@@ -499,7 +503,7 @@ def make_bar_graph(self,
499
503
if gv .no_replace and os .path .exists (os .path .join (curr_dir , file_name )):
500
504
return
501
505
502
- make_dir (curr_dir , False , gv )
506
+ make_dir (curr_dir , False )
503
507
504
508
# Get DF with average error for each "type" encountered in column
505
509
avg_error = {}
@@ -548,7 +552,11 @@ def make_standard_bar_graphs(self, test_name_plot: bool, gv: GlobalVars):
548
552
for col in columns :
549
553
for use_abs in [True , False ]:
550
554
for first_it in [True , False ]:
551
- gv .pool .submit (self .make_bar_graph , comp , col , use_abs , first_it , gv )
555
+ gv .processes .append ((self .make_bar_graph , (comp ,
556
+ col ,
557
+ use_abs ,
558
+ first_it ,
559
+ gv )))
552
560
553
561
# pylint: disable=too-many-locals
554
562
def make_heatmap (
@@ -636,7 +644,7 @@ def make_heatmap(
636
644
if gv .no_replace and os .path .exists (os .path .join (curr_dir , file_name )):
637
645
return
638
646
639
- make_dir (curr_dir , False , gv )
647
+ make_dir (curr_dir , False )
640
648
641
649
# Get DF with average error for each "coordinate" in the heatmap
642
650
df_avgs = pd .DataFrame (columns = [x_column , y_column , scale_column ])
@@ -683,20 +691,18 @@ def make_standard_heatmaps(self, gv: GlobalVars):
683
691
for comp in gv .components :
684
692
for first_it in [True , False ]:
685
693
for use_abs in [True , False ]:
686
- gv .pool .submit (self .make_heatmap ,
687
- comp ,
688
- "sink cluster tile width" ,
689
- "sink cluster tile height" ,
690
- first_it ,
691
- use_abs ,
692
- gv )
693
- gv .pool .submit (self .make_heatmap ,
694
- comp ,
695
- "delta x" ,
696
- "delta y" ,
697
- first_it ,
698
- use_abs ,
699
- gv )
694
+ gv .processes .append ((self .make_heatmap , (comp ,
695
+ "sink cluster tile width" ,
696
+ "sink cluster tile height" ,
697
+ first_it ,
698
+ use_abs ,
699
+ gv )))
700
+ gv .processes .append ((self .make_heatmap , (comp ,
701
+ "delta x" ,
702
+ "delta y" ,
703
+ first_it ,
704
+ use_abs ,
705
+ gv )))
700
706
701
707
# pylint: disable=too-many-locals
702
708
def make_pie_chart (
@@ -764,7 +770,7 @@ def make_pie_chart(
764
770
if gv .no_replace and os .path .exists (os .path .join (curr_dir , file_name )):
765
771
return
766
772
767
- make_dir (curr_dir , False , gv )
773
+ make_dir (curr_dir , False )
768
774
769
775
# Constrict DF to columns whose error is under threshold
770
776
curr_df = curr_df [curr_df [f"{ comp } % error" ] < gv .percent_error_threshold ]
@@ -821,7 +827,11 @@ def make_standard_pie_charts(self, test_name_plot: bool, gv: GlobalVars):
821
827
for col in columns :
822
828
for first_it in [True , False ]:
823
829
for weighted in [True , False ]:
824
- gv .pool .submit (self .make_pie_chart , comp , col , first_it , weighted , gv )
830
+ gv .processes .append ((self .make_pie_chart , (comp ,
831
+ col ,
832
+ first_it ,
833
+ weighted ,
834
+ gv )))
825
835
826
836
def make_standard_plots (self , test_name_plot : bool , gv : GlobalVars ):
827
837
"""
@@ -961,7 +971,7 @@ def make_csv(df_out: pd.DataFrame, file_name: str):
961
971
gv .csv_data and
962
972
(not os .path .exists (os .path .join (directory , "data.csv" )) or not gv .no_replace )
963
973
):
964
- gv .pool . submit ( make_csv , df , os .path .join (directory , "data.csv" ))
974
+ gv .processes . append (( make_csv , ( df , os .path .join (directory , "data.csv" )) ))
965
975
966
976
if gv .should_print :
967
977
print ("Created " , os .path .join (directory , "data.csv" ), sep = "" )
@@ -1111,7 +1121,6 @@ def parse_args():
1111
1121
)
1112
1122
1113
1123
args = parser .parse_args ()
1114
- pool = ThreadPoolExecutor (max_workers = args .j )
1115
1124
1116
1125
if args .all :
1117
1126
graph_types = ["bar" , "scatter" , "heatmap" , "pie" ]
@@ -1162,7 +1171,6 @@ def parse_args():
1162
1171
args .print ,
1163
1172
args .threshold ,
1164
1173
{},
1165
- pool
1166
1174
)
1167
1175
1168
1176
for excl in args .exclude :
@@ -1202,7 +1210,7 @@ def create_complete_outputs(
1202
1210
if len (args .dir_app ) > 0 :
1203
1211
results_folder_path += f"{ args .dir_app [0 ]} "
1204
1212
1205
- make_dir (results_folder_path , False , gv )
1213
+ make_dir (results_folder_path , False )
1206
1214
1207
1215
df_complete = df_complete .reset_index (drop = True )
1208
1216
@@ -1228,7 +1236,7 @@ def create_benchmark_outputs(
1228
1236
1229
1237
results_folder = os .path .join (output_folder , "__all__" )
1230
1238
results_folder_path = os .path .join (gv .output_dir , results_folder )
1231
- make_dir (results_folder_path , False , gv )
1239
+ make_dir (results_folder_path , False )
1232
1240
1233
1241
df_benchmark = df_benchmark .reset_index (drop = True )
1234
1242
@@ -1270,7 +1278,7 @@ def create_circuit_outputs(
1270
1278
1271
1279
results_folder = os .path .join (output_folder , test_name )
1272
1280
results_folder_path = os .path .join (gv .output_dir , results_folder )
1273
- make_dir (results_folder_path , False , gv )
1281
+ make_dir (results_folder_path , False )
1274
1282
# Read csv with lookahead data (or, a csv previously created by this script)
1275
1283
df = pd .read_csv (file_path )
1276
1284
df = df .reset_index (drop = True )
@@ -1329,7 +1337,7 @@ def create_circuit_outputs(
1329
1337
def main ():
1330
1338
args , gv = parse_args ()
1331
1339
1332
- make_dir (gv .output_dir , False , gv )
1340
+ make_dir (gv .output_dir , False )
1333
1341
1334
1342
# The DF containing info across all given csv files
1335
1343
df_complete = pd .DataFrame (columns = gv .column_names )
@@ -1371,6 +1379,25 @@ def main():
1371
1379
if args .collect != "" :
1372
1380
create_complete_outputs (args , df_complete , gv )
1373
1381
1382
+ # Create output graphs simultaneously
1383
+ # This is the best way I have found to do this that avoids having a while
1384
+ # loop continually checking if any processes have finished while using an
1385
+ # entire CPU. Pool, concurrent.futures.ProcessPoolExecutor, and
1386
+ # concurrent.futures.ThreadPoolExecutor seem to have a lot of overhead.
1387
+ # This loop assumes that the graph-creating functions take approximately
1388
+ # the same amount of time, which is not the case. We could alternatively
1389
+ # use a global variable which we increment when we start a process, and
1390
+ # decrement at the end of every graph-creating function.
1391
+ q = deque ()
1392
+ for func , params in gv .processes :
1393
+ while len (q ) >= args .j :
1394
+ proc = q .popleft ()
1395
+ proc .join ()
1396
+
1397
+ proc = Process (target = func , args = params )
1398
+ proc .start ()
1399
+ q .append (proc )
1400
+
1374
1401
1375
1402
if __name__ == "__main__" :
1376
1403
main ()
0 commit comments