55import math
66import os
77import time
8+ from collections import defaultdict
89from collections .abc import Iterable , Mapping
910from copy import deepcopy
1011from datetime import UTC , datetime
@@ -102,7 +103,7 @@ def _generate_parameter_files(
102103 iens : int ,
103104 fs : Ensemble ,
104105 iteration : int ,
105- ) -> Mapping [str , Mapping [str , float | str ]]:
106+ ) -> tuple [ Mapping [str , Mapping [str , float | str ]], Mapping [ str , float ]]:
106107 """
107108 Generate parameter files that are placed in each runtime directory for
108109 forward-model jobs to consume.
@@ -117,27 +118,33 @@ def _generate_parameter_files(
117118 fs: Ensemble from which to load parameter data
118119
119120 Returns:
120- Returns the union of parameters returned by write_to_runpath for each
121- parameter_config.
121+ Returns a tuple containing: the union of parameters returned by
122+ write_to_runpath for each parameter_config, and a dict with
123+ timings/durations for each parameter type.
122124 """
123125 # preload scalar parameters for this realization
124126 keys = [
125127 p .name
126128 for p in parameter_configs
127129 if p .cardinality == ParameterCardinality .multiple_configs_per_ensemble_dataset
128130 ]
131+ export_timings : defaultdict [str , float ] = defaultdict (float )
129132 scalar_data : dict [str , float | str ] = {}
130133 if keys :
134+ start_time = time .perf_counter ()
131135 df = fs ._load_scalar_keys (keys = keys , realizations = iens , transformed = True )
132136 scalar_data = df .to_dicts ()[0 ]
137+ export_timings ["load_scalar_keys" ] = time .perf_counter () - start_time
133138 exports : dict [str , dict [str , float | str ]] = {}
134139 log_exports : dict [str , dict [str , float | str ]] = {}
140+
135141 for param in parameter_configs :
136142 # For the first iteration we do not write the parameter
137143 # to run path, as we expect to read if after the forward
138144 # model has completed.
139145 if param .forward_init and iteration == 0 :
140146 continue
147+ start_time = time .perf_counter ()
141148 export_values : dict [str , dict [str , float | str ]] | None = None
142149 log_export_values : dict [str , dict [str , float | str ]] | None = {}
143150 if param .name in scalar_data :
@@ -164,11 +171,15 @@ def _generate_parameter_files(
164171 if log_export_values :
165172 for group , vals in log_export_values .items ():
166173 log_exports .setdefault (group , {}).update (vals )
174+ export_timings [param .type ] += time .perf_counter () - start_time
167175 continue
168-
176+ start_time = time . perf_counter ()
169177 _value_export_txt (run_path , export_base_name , exports | log_exports )
178+ export_timings ["value_export_txt" ] = time .perf_counter () - start_time
179+ start_time = time .perf_counter ()
170180 _value_export_json (run_path , export_base_name , exports )
171- return exports
181+ export_timings ["value_export_json" ] = time .perf_counter () - start_time
182+ return (exports , dict (export_timings ))
172183
173184
174185def _manifest_to_json (ensemble : Ensemble , iens : int , iter_ : int ) -> dict [str , Any ]:
@@ -239,14 +250,19 @@ def create_run_path(
239250 if run_arg .active :
240251 run_path .mkdir (parents = True , exist_ok = True )
241252 start_time = time .perf_counter ()
242- param_data = _generate_parameter_files (
253+ ( param_data , detailed_parameter_timings ) = _generate_parameter_files (
243254 ensemble .experiment .parameter_configuration .values (),
244255 parameters_file ,
245256 run_path ,
246257 run_arg .iens ,
247258 ensemble ,
248259 ensemble .iteration ,
249260 )
261+ for parameter_type , duration in detailed_parameter_timings .items ():
262+ if parameter_type not in timings :
263+ timings [parameter_type ] = 0.0
264+ timings [parameter_type ] += duration
265+
250266 timings ["generate_parameter_files" ] += time .perf_counter () - start_time
251267 real_iter_substituter = substituter .real_iter_substituter (
252268 run_arg .iens , ensemble .iteration
0 commit comments