99import os
1010import sys
1111import json
12- import timeit
13- import concurrent .futures
14- from concurrent .futures import ThreadPoolExecutor
12+ import time
1513
1614from knack .cli import CLI
1715from knack .commands import CLICommandsLoader
@@ -100,14 +98,24 @@ def __init__(self, **kwargs):
10098
10199 azure_folder = self .config .config_dir
102100 ensure_dir (azure_folder )
103- ACCOUNT .load (os .path .join (azure_folder , 'azureProfile.json' ))
104- CONFIG .load (os .path .join (azure_folder , 'az.json' ))
105- SESSION .load (os .path .join (azure_folder , 'az.sess' ), max_age = 3600 )
106- INDEX .load (os .path .join (azure_folder , 'commandIndex.json' ))
107- EXTENSION_INDEX .load (os .path .join (azure_folder , 'extensionIndex.json' ))
108- HELP_INDEX .load (os .path .join (azure_folder , 'helpIndex.json' ))
109- EXTENSION_HELP_INDEX .load (os .path .join (azure_folder , 'extensionHelpIndex.json' ))
110- VERSIONS .load (os .path .join (azure_folder , 'versionCheck.json' ))
101+
102+ # Load session files in parallel — each open() costs ~20ms on Windows due to
103+ # filesystem/antivirus overhead, and file I/O releases the GIL.
104+ from concurrent .futures import ThreadPoolExecutor
105+ load_tasks = [
106+ (ACCOUNT , os .path .join (azure_folder , 'azureProfile.json' ), 0 ),
107+ (CONFIG , os .path .join (azure_folder , 'az.json' ), 0 ),
108+ (SESSION , os .path .join (azure_folder , 'az.sess' ), 3600 ),
109+ (INDEX , os .path .join (azure_folder , 'commandIndex.json' ), 0 ),
110+ (EXTENSION_INDEX , os .path .join (azure_folder , 'extensionIndex.json' ), 0 ),
111+ (HELP_INDEX , os .path .join (azure_folder , 'helpIndex.json' ), 0 ),
112+ (EXTENSION_HELP_INDEX , os .path .join (azure_folder , 'extensionHelpIndex.json' ), 0 ),
113+ (VERSIONS , os .path .join (azure_folder , 'versionCheck.json' ), 0 ),
114+ ]
115+ with ThreadPoolExecutor (max_workers = len (load_tasks )) as pool :
116+ futures = [pool .submit (s .load , path , max_age = age ) for s , path , age in load_tasks ]
117+ for f in futures :
118+ f .result ()
111119 handle_version_update ()
112120
113121 self .cloud = get_active_cloud (self )
@@ -322,14 +330,14 @@ def _update_command_table_from_modules(args, command_modules=None):
322330 except ImportError as e :
323331 logger .warning (e )
324332
325- start_time = timeit . default_timer ()
333+ start_time = time . perf_counter ()
326334 logger .debug ("Loading command modules..." )
327335 results = self ._load_modules (args , command_modules )
328336
329337 count , cumulative_group_count , cumulative_command_count = \
330338 self ._process_results_with_timing (results )
331339
332- total_elapsed_time = timeit . default_timer () - start_time
340+ total_elapsed_time = time . perf_counter () - start_time
333341 # Summary line
334342 logger .debug (self .item_format_string ,
335343 "Total ({})" .format (count ), total_elapsed_time ,
@@ -404,7 +412,7 @@ def _filter_modname(extensions):
404412 # Add to the map. This needs to happen before we load commands as registering a command
405413 # from an extension requires this map to be up-to-date.
406414 # self._mod_to_ext_map[ext_mod] = ext_name
407- start_time = timeit . default_timer ()
415+ start_time = time . perf_counter ()
408416 extension_command_table , extension_group_table , extension_command_loader = \
409417 _load_extension_command_loader (self , args , ext_mod )
410418 import_extension_breaking_changes (ext_mod )
@@ -427,7 +435,7 @@ def _filter_modname(extensions):
427435 self .command_table .update (extension_command_table )
428436 self .command_group_table .update (extension_group_table )
429437
430- elapsed_time = timeit . default_timer () - start_time
438+ elapsed_time = time . perf_counter () - start_time
431439 logger .debug (self .item_ext_format_string , ext_name , elapsed_time ,
432440 len (extension_group_table ), len (extension_command_table ),
433441 ext_dir )
@@ -667,6 +675,8 @@ def load_arguments(self, command=None):
667675
668676 def _load_modules (self , args , command_modules ):
669677 """Load command modules using ThreadPoolExecutor with timeout protection."""
678+ import concurrent .futures
679+ from concurrent .futures import ThreadPoolExecutor
670680 from azure .cli .core .commands import BLOCKED_MODS
671681
672682 results = []
@@ -708,10 +718,10 @@ def _load_single_module(self, mod, args):
708718 from azure .cli .core .commands import _load_module_command_loader
709719 import traceback
710720 try :
711- start_time = timeit . default_timer ()
721+ start_time = time . perf_counter ()
712722 module_command_table , module_group_table , command_loader = _load_module_command_loader (self , args , mod )
713723 import_module_breaking_changes (mod )
714- elapsed_time = timeit . default_timer () - start_time
724+ elapsed_time = time . perf_counter () - start_time
715725 return ModuleLoadResult (mod , module_command_table , module_group_table , elapsed_time , command_loader = command_loader )
716726 except Exception as ex : # pylint: disable=broad-except
717727 tb_str = traceback .format_exc ()
@@ -1227,7 +1237,7 @@ def update(self, command_table):
12271237
12281238 :param command_table: The command table built by azure.cli.core.MainCommandsLoader.load_command_table
12291239 """
1230- start_time = timeit . default_timer ()
1240+ start_time = time . perf_counter ()
12311241 self .INDEX [self ._COMMAND_INDEX_VERSION ] = __version__
12321242 self .INDEX [self ._COMMAND_INDEX_CLOUD_PROFILE ] = self .cloud_profile
12331243 from collections import defaultdict
@@ -1242,7 +1252,7 @@ def update(self, command_table):
12421252 if module_name not in index [top_command ]:
12431253 index [top_command ].append (module_name )
12441254
1245- elapsed_time = timeit . default_timer () - start_time
1255+ elapsed_time = time . perf_counter () - start_time
12461256 self .INDEX [self ._COMMAND_INDEX ] = index
12471257
12481258 self .update_extension_index (command_table )
0 commit comments