Skip to content

Commit c4738aa

Browse files
authored
Merge 03277b9 into 0fdedb4
2 parents 0fdedb4 + 03277b9 commit c4738aa

File tree

4 files changed

+71
-17
lines changed

4 files changed

+71
-17
lines changed

benchmarks/benchmarks/load/__init__.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,58 @@ def time_many_var_load(self) -> None:
132132
_ = load(str(self.FILE_PATH))
133133

134134

135+
class ManyCubes:
136+
FILE_PATH = BENCHMARK_DATA / "many_cube_file.nc"
137+
138+
@staticmethod
139+
def _create_file(save_path: str) -> None:
140+
"""Run externally - everything must be self-contained."""
141+
import numpy as np
142+
143+
from iris import save
144+
from iris.coords import AuxCoord, DimCoord
145+
from iris.cube import Cube, CubeList
146+
147+
data_len = 81920
148+
bnds_len = 3
149+
data = np.arange(data_len).astype(np.float32)
150+
bnds_data = (
151+
np.arange(data_len * bnds_len)
152+
.astype(np.float32)
153+
.reshape(data_len, bnds_len)
154+
)
155+
time = DimCoord(np.array([0]), standard_name="time")
156+
lat = AuxCoord(
157+
data, bounds=bnds_data, standard_name="latitude", units="degrees"
158+
)
159+
lon = AuxCoord(
160+
data, bounds=bnds_data, standard_name="longitude", units="degrees"
161+
)
162+
cube = Cube(data.reshape(1, -1), units="unknown")
163+
cube.add_dim_coord(time, 0)
164+
cube.add_aux_coord(lat, 1)
165+
cube.add_aux_coord(lon, 1)
166+
167+
n_cubes = 100
168+
cubes = CubeList()
169+
for i in range(n_cubes):
170+
cube = cube.copy()
171+
cube.long_name = f"var_{i}"
172+
cubes.append(cube)
173+
save(cubes, save_path)
174+
175+
def setup_cache(self) -> None:
176+
if not REUSE_DATA or not self.FILE_PATH.is_file():
177+
# See :mod:`benchmarks.generate_data` docstring for full explanation.
178+
_ = run_function_elsewhere(
179+
self._create_file,
180+
str(self.FILE_PATH),
181+
)
182+
183+
def time_many_cube_load(self) -> None:
184+
_ = load(str(self.FILE_PATH))
185+
186+
135187
class StructuredFF:
136188
"""Test structured loading of a large-ish fieldsfile.
137189

docs/src/whatsnew/latest.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ This document explains the changes made to Iris for this release
5050
🚀 Performance Enhancements
5151
===========================
5252

53-
#. N/A
53+
#. `@bouweandela`_ made loading :class:`~iris.cube.Cube`s from small NetCDF
54+
files faster. (:pull:`6229`)
5455

5556

5657
🔥 Deprecations

lib/iris/cube.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,8 @@ def __eq__(self, other):
924924
# For equality, require both globals + locals to match exactly.
925925
# NOTE: array content works correctly, since 'locals' and 'globals' are always
926926
# iris.common.mixin.LimitedAttributeDict, which gets this right.
927-
other = CubeAttrsDict(other)
927+
if not isinstance(other, CubeAttrsDict):
928+
other = CubeAttrsDict(other)
928929
result = self.locals == other.locals and self.globals == other.globals
929930
return result
930931

lib/iris/fileformats/cf.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,9 +1336,11 @@ def __init__(self, file_source, warn=False, monotonic=False):
13361336
self._trim_ugrid_variable_types()
13371337
self._with_ugrid = False
13381338

1339-
self._translate()
1340-
self._build_cf_groups()
1341-
self._reset()
1339+
# Read the variables in the dataset only once to reduce runtime.
1340+
variables = self._dataset.variables
1341+
self._translate(variables)
1342+
self._build_cf_groups(variables)
1343+
self._reset(variables)
13421344

13431345
def __enter__(self):
13441346
# Enable use as a context manager
@@ -1380,16 +1382,16 @@ def filename(self):
13801382
def __repr__(self):
13811383
return "%s(%r)" % (self.__class__.__name__, self._filename)
13821384

1383-
def _translate(self):
1385+
def _translate(self, variables):
13841386
"""Classify the netCDF variables into CF-netCDF variables."""
1385-
netcdf_variable_names = list(self._dataset.variables.keys())
1387+
netcdf_variable_names = list(variables.keys())
13861388

13871389
# Identify all CF coordinate variables first. This must be done
13881390
# first as, by CF convention, the definition of a CF auxiliary
13891391
# coordinate variable may include a scalar CF coordinate variable,
13901392
# whereas we want these two types of variables to be mutually exclusive.
13911393
coords = CFCoordinateVariable.identify(
1392-
self._dataset.variables, monotonic=self._check_monotonic
1394+
variables, monotonic=self._check_monotonic
13931395
)
13941396
self.cf_group.update(coords)
13951397
coordinate_names = list(self.cf_group.coordinates.keys())
@@ -1402,9 +1404,7 @@ def _translate(self):
14021404
if issubclass(variable_type, CFGridMappingVariable)
14031405
else coordinate_names
14041406
)
1405-
self.cf_group.update(
1406-
variable_type.identify(self._dataset.variables, ignore=ignore)
1407-
)
1407+
self.cf_group.update(variable_type.identify(variables, ignore=ignore))
14081408

14091409
# Identify global netCDF attributes.
14101410
attr_dict = {
@@ -1414,7 +1414,7 @@ def _translate(self):
14141414
self.cf_group.global_attributes.update(attr_dict)
14151415

14161416
# Identify and register all CF formula terms.
1417-
formula_terms = _CFFormulaTermsVariable.identify(self._dataset.variables)
1417+
formula_terms = _CFFormulaTermsVariable.identify(variables)
14181418

14191419
for cf_var in formula_terms.values():
14201420
for cf_root, cf_term in cf_var.cf_terms_by_root.items():
@@ -1433,9 +1433,9 @@ def _translate(self):
14331433
)
14341434

14351435
for name in data_variable_names:
1436-
self.cf_group[name] = CFDataVariable(name, self._dataset.variables[name])
1436+
self.cf_group[name] = CFDataVariable(name, variables[name])
14371437

1438-
def _build_cf_groups(self):
1438+
def _build_cf_groups(self, variables):
14391439
"""Build the first order relationships between CF-netCDF variables."""
14401440

14411441
def _build(cf_variable):
@@ -1489,7 +1489,7 @@ def _span_check(
14891489
ignore += coordinate_names
14901490

14911491
match = variable_type.identify(
1492-
self._dataset.variables,
1492+
variables,
14931493
ignore=ignore,
14941494
target=cf_variable.cf_name,
14951495
warn=False,
@@ -1569,9 +1569,9 @@ def _span_check(
15691569
promoted.add(cf_name)
15701570
not_promoted = ignored.difference(promoted)
15711571

1572-
def _reset(self):
1572+
def _reset(self, variables):
15731573
"""Reset the attribute touch history of each variable."""
1574-
for nc_var_name in self._dataset.variables.keys():
1574+
for nc_var_name in variables.keys():
15751575
self.cf_group[nc_var_name].cf_attrs_reset()
15761576

15771577
def _close(self):

0 commit comments

Comments
 (0)