Skip to content

Zarr I/O PR 6: native Zarr saving - #7346

Open
trexfeathers wants to merge 73 commits into
SciTools:brownfieldfrom
trexfeathers:zarr-io-pr-6
Open

trexfeathers wants to merge 73 commits into
SciTools:brownfieldfrom
trexfeathers:zarr-io-pr-6

Conversation

@trexfeathers

@trexfeathers trexfeathers commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

(posted by Claude Opus)

This is the sixth of nine pull requests delivering native Zarr support for Iris, and the subject of #6980: iris.save(cube, "out.zarr") now writes a Zarr version 3 store, with compute=False returning a real dask.delayed.Delayed, Zarr's own storage keywords (chunks, shards, compressors, filters, serializer), a group= keyword, consolidate_metadata, and the CF masking, packing and fill-value handling that zarr-python does not do for free. Part of #6980. If any of that arithmetic is wrong the store still opens cleanly and reads back the wrong numbers rather than raising, so the masking and packing logic in zarr/_encode.py and ZarrDatasetVariable.__setitem__, and the fill-value declaration in zarr/saver.py's _backend_write, are where I would look first.

Public API added. iris.fileformats.zarr.save and iris.fileformats.zarr.Saver, and iris.save now reaches them for any target ending .zarr, local or remote, through a new "zarr" entry in iris.io._savers.

The base. This work was built on zarr-io-pr-4-5-base, a branch merging PR 4 (#7336) and PR 5 (#7338) into one tree, because it both edits PR 4's zarr/_dataset.py and subclasses PR 5's generic saver. Both predecessors have since merged, and this branch is now rebased onto brownfield with git rebase --onto upstream/brownfield zarr-io-pr-4-5-base zarr-io-pr-6, so the diff below is this pull request's own work and nothing else. An earlier preview of it ran as trexfeathers#100, based on that merge branch so that its diff showed only this work; the review comments there are answered in the commits here.

What the rebase changed, over and above replaying the commits. Three things landed in brownfield while this was being built, and meeting them is a commit of its own at the head of the branch. The generic saver is _CFSaver, renamed and made private at review of #7338, so every reference here follows it. iris.io no longer decides which savers take a whole CubeList by module name: it holds the saver functions themselves, in _savers.cubelist_savers, so iris.io._zarr_save is registered there beside the netCDF saver and iris.fileformats.zarr.save registers itself at the foot of its own module — the only place it can be named by identity without iris.io importing zarr to do it, and both routes a caller can take are covered by tests. And UGRID meshes now load from Zarr, added to #7336 on 2026-10-22, which turned the mesh round-trip test from a strict xfail into an ordinary passing one.

No changelog fragment. This programme writes its changelog fragments once, at the merge-back into main, not on the feature branches that land on brownfield — the same rule PRs 1 through 5 followed.

xarray is a new test dependency. xarray is added to the test section of requirements/py3{12,13,14}.yml, for the cross-reader conformance tests — xarray opens what Iris writes, and Iris loads what xarray wrote, which is how a private encoding decision gets caught. All three lock files are regenerated to match, so CI runs those tests rather than skipping them. The regeneration is its own commit, and deliberately late: #7336 regenerated all three lock files for zarr, and doing it again while that was outstanding would have guaranteed a conflict and a second slow environment rebuild on the rebase.

The first of two changes that touch the netCDF path, and why it does not touch a netCDF file. _CFSaver gains a fifth hook, _grid_mapping_writer, so the CF grid-mapping parameters can reach a non-netCDF store at all — the base implementation writes them through .attributes, which is what Zarr gets. netcdf.saver.Saver's override returns cf_var.variable, the same netCDF4.Variable object those parameters have always been assigned onto, so what lands in a netCDF file is unchanged: the override is an identity, not a behaviour change. The ASCII-to-bytes coercion that object still bypasses is Q8 in the design document, unblocked by this hook but not closed by it.

The second, and the one to look at hardest: a block moved out of netcdf.saver.save. Deciding which of a cube's attributes a store can hold globally is cube logic, not storage logic, but it was written inside netcdf.saver.save and nothing else could call it — so on the Zarr path every cube global attribute was simply dropped. With iris.FUTURE.save_split_attrs = True, the generic write deliberately writes no globals and leaves them to the caller: netCDF wrote them, Zarr did not, and title, institution, history, source and references vanished from a Zarr save. In the legacy default mode it is the local_keys computation that was missing, so a CubeList whose cubes disagreed about an attribute kept only the last cube's value where netCDF gave each data variable its own. The block is now cf.saver.marshal_attributes, called by both savers. The extraction is behaviour-preserving — its only edit is that the legacy deprecation message takes the format name, so netCDF passes "netcdf" and gets back the exact string it had — and lib/iris/tests/integration/test_netcdf__loadsaveattrs.py gives 2059 passed before and 2059 passed after, as does the whole netCDF suite. One thing did change, and it is worth saying so nobody discovers it in a traceback: those warnings now report lib/iris/fileformats/cf/saver.py as their source rather than lib/iris/fileformats/netcdf/saver.py. Their text, category and count are unchanged, and nothing in the repository filters on the location.

One consequence for a maintainer to rule on. Because the Zarr saver now shares that marshalling, a legacy-mode Zarr save emits the same attribute-handling deprecation netCDF has emitted since 3.8 — consistent with netCDF, and correct in that the legacy mode is deprecated for every format, but it does mean a brand-new format ships warning on its default path, since save_split_attrs still defaults to False. Suppressing it for Zarr alone would mean a flag on the shared function whose only purpose is to hide a deprecation from one format, which is the divergence the extraction exists to prevent. Left as it is, deliberately, for an owner to decide.

Packing needed its own implementation here, not a reuse of netCDF's. The generic saver's packing= support sets scale_factor and add_offset as attributes and streams the data through unchanged, because netCDF4-python applies that transform itself the moment the attributes are set on a variable it is about to receive data for. zarr-python has no such behaviour: left alone, packing= on a Zarr save would have set the attributes correctly and then written physical-unit values straight into a narrower, usually integer, stored dtype — numbers with no relationship to what a reader would reconstruct from them. zarr/_encode.py now has a pack function, the write-side mirror of the existing _decode.unpack, and ZarrDatasetVariable.__setitem__ applies it before the masked fill, because the fill value lives in packed space and packing an already-filled sentinel would send it through the same transform as real data. The Zarr saver also refuses a non-integer packing dtype, matching the check and the message netcdf/saver.py already has.

What this does not do. mode="a" adds new arrays to an existing store; it does not match an incoming coordinate against one the store already holds, so saving a second cube with its own latitude writes a second array, latitude_0, rather than reusing the first, because deciding when two coordinates are "the same" across unequal values, differing units and different calendars is its own piece of work. And a masked string variable is refused rather than written: the only sentinel a Zarr string array carries is the empty string, which is also a legitimate label, and Iris's own loader does not restore a mask from one — so writing it would lose the mask in silence. The netCDF saver cannot store a masked string coordinate either, so this is parity with it rather than a step back from it.

The full suite. pytest lib/iris/tests/unit lib/iris/tests/integration -q -n auto, with NPY_DISABLE_CPU_FEATURES set as the repository's own instructions ask, on the rebased result: 11485 passed, 59 skipped, nothing failing, 239s. pre-commit run --all-files is clean.

trexfeathers and others added 30 commits October 23, 2026 12:08
The parts of the plan that every task reads: what PR 6 delivers, the
constraints copied verbatim from the design document, the scope boundary,
how a branch over two unmerged pull requests gets rebased, nine findings
that refine the spec, the file structure and the interface the tasks
leave behind.

Four decisions taken with the user and recorded as findings: grid-mapping
writing gets a fifth hook rather than the regularisation Q8 defers (F2);
_attribute_handlers moves here, in its own commit (F3); the write-side
value conversions get their own module (F4); xarray goes in the
requirement files but the lock files are regenerated by hand later (F5).

Every zarr-python and xarray claim the write path leans on was re-checked
against zarr 3.4.1 and xarray 2026.9.0 and reproduces (F6).

Tasks follow in later stages. Review Focus is written last, when there
are tasks to pin each line to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
Task 1 moves the CF attribute handlers out of the netCDF package, which
is what §12.4 assigns to this pull request and what closes cf/saver.py's
last two-way import. Task 2 adds the fifth saver hook, so that the 63
grid-mapping parameters reach a store with no netCDF4 object behind it.

Both are prerequisites for any Zarr saving at all, and neither mentions
Zarr, so they lead and they commit separately.

The grid-mapping hook keeps netCDF writing to the same object it writes
to today, by returning it. That is what lets this pull request leave the
encoding question where Q8 put it, in its own pull request, rather than
changing what lands in a netCDF file as a side effect of adding Zarr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
Task 3 adds zarr/_encode.py: JSON-native attribute values, the
_FillValue and missing_value encodings, the default fill table and
masked-array filling. Pure functions, so the task tests them on plain
values with no store in sight.

Four findings behind it, each checked against zarr 3.4.1 and xarray
2026.9.0 rather than assumed:

- missing_value must be a plain number. Written as base64, like
  _FillValue, xarray opens the store and masks nothing (F10).
- zarr's default fill value is 0 for every dtype, floats included, so a
  masked cube saved with no fill_value would write zeros and declare
  nothing. Iris writes netCDF's default fills instead, and always
  declares them as _FillValue (F11). Decided with the user.
- create_variable never sees the data, so the spec's "derive the write
  grid from the source's chunking" cannot happen there. The invariant
  is enforced by rechunking at write time instead, with the data
  variable's chunks defaulted from the cube (F12).
- A masked array written straight to a zarr array raises inside
  numpy.ma, so filling is not an optimisation (F13).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
Task 4 opens the four write modes, defends the w- default that makes a
clobber deliberate, and puts the version 3 guard in one place. Task 5
turns the two creation calls CFSaver makes into Zarr arrays: a declared
dimension is a dict entry, an array carries its dimension names, and a
name that already exists is refused rather than overwritten.

The keyword table is here too. A netCDF storage keyword raises and names
the Zarr replacement where there is one, and says there is none where
there is not - which is what stops the translation layer the spec rules
out from growing back by accident.

The fill value a create gets when the caller names none is netCDFs
default for the dtype, and no _FillValue attribute goes with it. Who
declares the attribute, and when, is Task 11: it has to happen before
the metadata is consolidated, and only the saver knows the data is
masked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
Task 6 puts each kind of write at the one place it passes through:
attributes are converted before zarr sees them, because a value its
encoder rejects stays in the mapping; masked values are filled on the way
into an array; and finalise consolidates the whole store from the root,
with zarr's "not part of the version 3 specification" warning suppressed
and explained.

Task 7 is the saver. It is thin, which is the point of PR 5: opening a
store, one hook saying Zarr holds text natively and needs no trailing
character dimension, and the save() users call. Storage keywords are
validated before the store is created, so a mistyped one does not leave
an empty store behind.

Deferred writes, the alignment invariant and the masked _FillValue
declaration are Task 8, on the same class.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
Task 8 is the one where a mistake loses data rather than raising. The
_FillValue a masked source needs is declared in _backend_write, before
the metadata is consolidated, because under compute=False the data is
written long after. The lazy source is rechunked to the array's write
grid - shards when sharded, chunks otherwise - which is what licenses
lock=False, and the task carries the §4.5 reproduction as a test.
delayed_completion returns a real Delayed that owns the consolidation,
and binds the dataset locally rather than through self, because the
closure is serialised to every distributed worker.

Task 9 registers the saver. The entry is a shim in the zarr package
rather than the saver function itself: registration happens the first
time any format is saved, and reaching the real save would import
zarr-python with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
Task 10 saves realistic_4d_w_everything and loads it back, and pins the
two format behaviours Iris cannot fix: a deleted chunk reads as fill
with no warning, and consolidated metadata goes stale silently. Both
fail loudly here if a zarr-python release changes them.

Task 11 is the one that justifies two design decisions rather than
merely implementing them - xarray opens every store Iris writes, and
Iris loads what xarray wrote. It adds xarray to the three requirement
files and deliberately leaves the lock files alone.

Task 12 replaces the Saving section PR 4 wrote, whose whole content is
that Iris cannot save to Zarr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
The Review Focus section is written last, with the spec in front of me
and the tasks already down: five input classes a user meets in ordinary
work that no task's own tests would have reached - a mesh cube, packed
data, int64, a non-ASCII attribute and a dataless cube. Each got a test
added to the task that owns the code, rather than being noted and left.

The self-review found one real error: Task 10's fixture called
realistic_4d_w_everything(lazy=True), and the function's only parameter
is w_mesh. Fixed, and the mesh case now uses it deliberately.

Task 13 records the seven decisions in the design document, says what
PR 6 does and does not close of Q5 and Q8, and sets out what the pull
request body has to say - above all that it is stacked on two unmerged
branches and that CI has no xarray until the lock files are regenerated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
There is no tests/unit/fileformats/cf/saver/ yet - PR 5 left the
savers tests under netcdf/saver/, driving the generic machinery through
the only subclass there was. Task 2 adds the first test that belongs to
CFSaver itself, so it has to create the package marker too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: b8474d78-fdfa-43e5-b907-4bc3fe833fb3
cf/saver.py imported ATTRIBUTE_HANDLERS from the netCDF package, which
in turn imported cf/saver.py, surviving only because
_attribute_handlers itself imported nothing from iris.fileformats.cf.
The handlers serialise the Iris-only attribute types that every CF
store needs - STASH, GRIB_PARAM, ukmo__process_flags - so the CF
package is their natural home, and moving them there closes that
two-way import.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
_add_grid_mapping_to_dataset bound grid_variable to cf_var_grid.variable
and made sixty-three plain attribute assignments to it. That object is
netCDF4's own, so any store with no netCDF4 variable behind it raised a
bare AttributeError for every cube with a coordinate system.

Routing those assignments through .attributes instead would change how
some of them are typed in a netCDF file (crs_wkt as NC_STRING against
grid_mapping_name as NC_CHAR), which Q8 of the design document rules
out of this programme. So the fix is a fifth hook,
_grid_mapping_writer, that returns the assignment target: the base
implementation returns a small writer onto cf_var.attributes, and
netCDF's override returns cf_var.variable unchanged, so the netCDF
path writes to the identical object it always has.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
The native Zarr saver needs a few value conversions before anything
can reach a store: attributes into JSON-native types, `_FillValue`
and `missing_value` into the encodings xarray expects, a default
fill value per dtype, and masked arrays into filled ones. Keeping
these as pure functions, mirroring `_decode.py`, lets every rule be
tested without a store, ahead of the dataset and saver code that
will call them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
ZarrDataset.__init__ accepted mode="r" only. This adds the four write
modes zarr-python itself supports, with Iris's own default: "w-"
creates and refuses to clobber an existing store, because a Zarr
clobber is many non-atomic deletes rather than one atomic rename, so
replacing one is opted into via mode="w" instead of assumed. Writing
is guarded to version 3 in one place, in _WRITABLE_ZARR_FORMAT, so
that version 2 writing can be added later by implementing the three
version-specific points of the design document and deleting that
guard. Reading keeps working unchanged for either version.

_declared_dimensions is added as empty state for the next task to
fill, and the old _NO_SAVING constant is retired along with
_READ_MODES in favour of _WRITE_MODES and _MODES; the three still
unimplemented write members now raise their message inline.

An existing test asserted that mode="w" was refused outright; that is
superseded by this change and is removed, with its replacement now
covered in the new write test module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
A missing closing fence after the _dimension_size helper left every
heading from Task 6 onward inside a code block, so the three paragraphs
that introduce check_encoding_keywords rendered as Python and the tool
that extracts one task's text for an implementer could not find where
Task 5 ends.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
Zarr has no dimension object, so create_dimension records a name and
size and catches a conflicting redeclaration itself, rather than
letting two arrays silently disagree. create_variable then writes
those names onto a new array, refuses a Zarr storage keyword it does
not recognise rather than guessing a netCDF translation, and applies
the dtype-dependent _FillValue encoding to both the array's own fill
and its attribute.

The pre-existing write-surface test covered three members that always
refused; two of them now work on a writable dataset, so it is narrowed
to the one still unimplemented, finalise, with the read-only refusal
of the other two covered by the new tests instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
create_variable always looked a default fill up in the netCDF table
when none was given, but dtype=str has no entry there - it asks Zarr
for a variable-length string array, which is not a netCDF concept to
have a default for. Skip the lookup for that case and omit the
fill_value keyword entirely, so zarr-python's own default for the
dtype applies, matching what the docstring already promised.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
Attributes now convert to a JSON-native form before zarr sees them,
on every assignment rather than only at variable creation, so a
rejected value never lands in the attributes mapping. _FillValue and
missing_value get their own CF encodings, decided by the array's
dtype. A write now fills any masked array with its own fill value
first, since a Zarr array has no mask of its own and numpy.ma raises
deep inside the bytes codec otherwise. finalise() consolidates the
whole store's metadata, the step that makes a remote store usable to
a reader that fetches one document instead of one per node.

The _FillValue pre-encoding in create_variable was a stopgap for
when the attributes mapping did not yet encode anything; it is
removed now that __setitem__ does the encoding itself, so a fill
value is no longer at risk of being encoded twice.

test_finalise_refuses pinned the old NotImplementedError; it is
superseded by TestFinalise::test_a_read_only_dataset_refuses, which
pins the real refusal once finalise does its job.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
CFSaver already does the CF work; the Zarr-specific saver only opens a
store, says how a string is stored, provides the save() entry point
that validates Zarr's own encoding keywords before anything is created,
and completes the store with a consolidated metadata document.

delayed_completion() wraps the inherited lazy-write delayed with a real
Delayed that also runs the consolidation, so both the eager (compute=True)
and deferred (compute=False) paths finish through the same mechanism, and
a save that never completes never claims to have consolidated.

Also refuse, rather than silently rename, a cube whose data-variable
name already exists in the group when adding to an existing store:
the generic CFSaver renaming is right for two cubes of one call
sharing a name, and wrong for a store this call did not just create.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
mode="a" onto a store this saver had already consolidated raised a
bare KeyError: ZarrDataset.__init__ opened it with zarr-python's
default consolidated-metadata fast path, so the reopened group listed
only the arrays present at the last consolidation, never the one the
new write was about to add. Pass use_consolidated=False for every
write mode; a read mode keeps the fast path, which is what it is for.

Saver.write()'s own repeated-name refusal read self._dataset.variables
live, which create_variable invalidates on every call, so it also
caught a name created moments earlier by another cube of the same
save() call - exactly the case its own docstring says must still be
renamed. Compare against a snapshot taken once, when the Saver opens
the dataset, instead.

The refusal's message was duplicated verbatim between saver.py and
_dataset.py. Shared it as existing_name_message() in _dataset.py.

A scalar cube failed to save: CFSaver writes a whole variable as
cf_var[:] = data, and zarr rejects slice(None) on a 0-d array. Zarr
array indexing wants Ellipsis or () instead; ZarrDatasetVariable
.__setitem__ now makes that substitution for a 0-d array.

Corrected delayed_completion's docstring, which claimed the inherited
method returns a tuple for a sequence of sources: for one delayed
write it returns a plain dask.array.core.Array, never a tuple and
never actually a Delayed despite the annotation. The SciTools#7291 reference
and the reason for wrapping it both still hold.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
A lazy source handed straight to da.store(..., lock=False) can have
its dask chunking subdivide the array's write grid - its shards when
sharded, its chunks otherwise - so several concurrent tasks read-
modify-write one stored object and lose each other's updates. Rechunk
the source to the write grid first, so each task owns whole cells and
no cell is touched twice.

Declare a _FillValue when the saved data is masked, even when the
cube's own attributes name none, so a reader sees the mask rather than
plain sentinel values with nothing marking them missing.

Reshape delayed_completion's inner function into a module-level one so
the Delayed it returns survives a plain pickle round trip, not only
cloudpickle, which a distributed scheduler needs regardless.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
Drop the two finding labels from shipped test comments, and correct
one that described the netCDF compute=False path wrongly - it returns
a dask Array for one delayed write, not always a tuple.

Make _tiles_the_grid raise on a chunks/grid rank mismatch rather than
silently answering True for dimensions it never examined: the one
function whose whole job is to prevent a silent, mismatched write must
not default to "safe" for input it did not check.

Use np.ma.is_masked instead of np.ma.isMaskedArray to decide whether
real data needs a declared fill value: the latter is true of an np.ma
array with an all-False mask, which Iris produces routinely, and would
invent a missing-value declaration for data whose source never carried
one. Lazy data keeps is_lazy_masked_data, since it cannot be inspected
without computing it.

Rework the "aligned source is left alone" test: chunking it at exactly
the write-grid size made the old chunks-equality assertion pass
whether or not the early return existed, because dask's own rechunk()
is already a no-op in that case. A single oversized chunk forces
dask's rechunk to subdivide if asked, so identity is now a check that
can fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
An earlier pull request taught find_saver to match the .zarr suffix,
but iris.save still had no saver registered for it, so the lookup
returned None. Register the zarr package's _save_cubes shim for the
extension, keeping the import of zarr-python deferred until a Zarr
target is actually saved.

The shim's __module__ is "iris.fileformats.zarr", the package, not
"iris.fileformats.zarr.saver": iris.save reads __module__ to decide
whether a saver takes a whole CubeList at once, so that is the string
the frozenset needs, not the module the shim forwards to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
_save_cubes reaches iris.fileformats.zarr.saver with a plain
from .saver import save, which bypasses __getattr__ and the
_check_zarr_version call it makes on every load. On an installation
with zarr missing or too old, iris.load already explained the
problem clearly, but iris.save silently wrote a store (when zarr
was merely too old) or leaked a bare "No module named 'zarr'" (when
it was entirely absent). Repeat the same checks __getattr__ runs, so
all three routes into this package give the same clear error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
Everything so far has tested one piece of the Zarr saver in isolation.
This is the first test that puts a realistic cube with meshes, packed
data, int64 data, non-ASCII attributes and a dataless form through
iris.save and iris.load together, and checks what comes back - and
pins the two Zarr format behaviours Iris depends on but does not
control: a deleted chunk reads back as fill with no warning, and
consolidated metadata goes stale until Iris re-consolidates.

Two of the five ordinary-input cases fail and are left failing on
purpose: packed data round-trips to the wrong values, because nothing
in the Zarr saver applies the scale_factor/add_offset transform before
writing (netCDF relies on netCDF4-python doing this automatically, and
Zarr has no equivalent); and a mesh does not reattach on load, because
the Zarr loader (an earlier, already-merged piece of work) does not
build MeshXY objects, and iris.mesh.load_meshes explicitly ignores
non-netCDF sources. Both are real defects the tests found rather than
authoring mistakes, so they are reported rather than papered over.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
The generic CF saver only ever sets scale_factor/add_offset as
attributes and streams the unpacked values straight through, because
netCDF4-python applies the pack transform itself on assignment once
those attributes are set on the variable. Zarr has no such mechanism,
so a packed Zarr variable was silently storing the original values
rounded and cast into the narrow dtype, unrelated to what the
scale_factor/add_offset attributes claimed - correct metadata over
wrong data, with no warning on either the write or the read side.

ZarrDatasetVariable.__setitem__ is the one place every write passes
through, eager and lazy alike, so that is where the pack transform now
runs: round((value - add_offset) / scale_factor), narrowed to the
array's own dtype, with either attribute absent behaving as its
identity. It runs before the existing mask fill rather than after,
because the fill value lives in packed (stored) space and the data
arrives unpacked - filling first would push an already-filled sentinel
through the same transform as real data and lose it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
…nnot yet

The Zarr saver writes a complete, CF-UGRID-compliant mesh - a topology
variable, a connectivity with its start_index, and data-variable
mesh/location attributes - confirmed by reading the store directly.
Loading one back does not reattach it: the Zarr loader (already-merged,
out of this pull request's scope) never builds a MeshXY, and
iris.mesh.load_meshes filters to the netCDF handler only, so neither
public entry point recovers a mesh from a store this saver wrote.

Replaces the single round-trip test with two: one against the written
store, asserting the structure the saver actually delivers, and one
strict xfail for the full iris.save/iris.load round trip, so that
loader support landing later is caught rather than left silently
passing under a stale marker.

Also tightens test_several_cubes_round_trip to assert the exact count
of three cubes (the two saved plus the orography cube the cube
fixture's hybrid-height factory always brings along) and that both
saved names are present, rather than a bare count that said nothing
about which cubes they were.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
A non-integer packing dtype reached the store silently mishandled:
nothing validated the packspec before writing, and pack()'s unconditional
rounding mangled a float target instead of merely narrowing it. The
netCDF saver already refuses the same input outright, so the Zarr saver
now does too, with the same message, checked before the store is
created. pack() itself now rounds only for an integer target, matching
what netCDF4-python actually does on assignment rather than a rounding
version of it applied regardless.

Also closes two smaller gaps in the same function: scale_factor=0 now
raises naming it, rather than dividing by zero in silence, and the
final cast is reconstructed from a block's underlying data and mask
rather than calling MaskedArray.astype() directly, which otherwise
tries to cast the block's inherited (often very large) floating
fill_value into the narrow target dtype as well, and numpy warns about
that even though every real value casts cleanly - a warning a lazy
masked packed write would otherwise have carried all the way out to
the user with no connection to anything they wrote.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
test_a_missing_chunk_reads_back_filled asserted only the shape, which
is unchanged whether a missing chunk reads back in silence or with a
warning - so it could not have caught a future zarr-python release that
started warning about exactly that. It now also asserts no warning was
raised, and that the masked count went up against the original cube's,
which is the actual "data silently went missing" the test exists to
record.

test_a_group_round_trips saved the same cube to the root and to a named
group, so reading the root would have satisfied every one of its
assertions - a loader that ignored group= entirely would have passed.
It now saves a differently-named cube into the group and asserts that
name comes back, so ignoring group= raises instead.

test_packed_data_round_trips checked only that scale_factor was
present, not add_offset, and compared at a tolerance loose enough that
a systematic one-count bias would have passed. It now checks both
attributes and tightens the tolerance to one stored count - loose
enough for the quantisation error correct packing is allowed to
introduce, tight enough to fail a one-count bias.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
Every Zarr test so far has checked Iris against Iris. This checks it
against the rest of the world: xarray opens every store Iris writes,
in both directions. It is the only thing that can catch a swap of the
base64 _FillValue and the JSON-native missing_value encodings, since
Iris's own reader accepts either shape for either attribute.

xarray goes into the test section of each Python version's
environment file, not the lock files: the human developer regenerates
those by hand once the Zarr dependency PR has landed, so until then
continuous integration has no xarray and these tests skip there. They
run locally, which is where they are needed while the work is being
done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
The cross-reader module proved that a floating _FillValue has to be
base64, because xarray fails the whole dataset open on a plain
number. It did not yet prove the other half of the same pair: that
missing_value has to stay a plain number, because base64 there opens
cleanly and silently masks nothing. A unit test on the encoder alone
cannot see that failure, since it only checks what the function
returns, not what a reader does with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 6582234f-93d1-42d0-94ce-b54ee4a252af
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 96841741-ed31-4388-9014-e3f94eb82651
@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.34884% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.04%. Comparing base (426d718) to head (f594416).

Files with missing lines Patch % Lines
lib/iris/fileformats/zarr/_dataset.py 92.35% 10 Missing and 3 partials ⚠️
lib/iris/fileformats/zarr/saver.py 93.06% 5 Missing and 2 partials ⚠️
lib/iris/fileformats/cf/saver.py 98.29% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@              Coverage Diff               @@
##           brownfield    #7346      +/-   ##
==============================================
+ Coverage       90.88%   91.04%   +0.15%     
==============================================
  Files             104      106       +2     
  Lines           26813    27157     +344     
  Branches         4898     4963      +65     
==============================================
+ Hits            24369    24724     +355     
+ Misses           1689     1677      -12     
- Partials          755      756       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

trexfeathers and others added 2 commits October 23, 2026 12:45
Session-Id: 96841741-ed31-4388-9014-e3f94eb82651
The entry said they would be regenerated by hand once SciTools#7336 landed, and said
CI would skip the cross-reader conformance tests until that happened. Both
have now been done, so it says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 96841741-ed31-4388-9014-e3f94eb82651
@github-actions github-actions Bot added the benchmark_this Request that this pull request be benchmarked to check if it introduces performance shifts label Oct 23, 2026
Regenerating the lock files to add xarray moved a lot more than xarray -
cartopy 0.25 to 0.26 among it - and five `TestTrajectoryWrap` cases plus one
shapefile mask test then failed in CI, none of them Zarr. SciTools#7299 had already
fixed exactly those on `main`: a tolerance of five decimal places on the path
comparison, an adjustment in `iris.plot` for cartopy>=0.26, and the shapefile
test. `brownfield` has now taken `main`, so this merge is how they arrive.

The lock files are this branch's own, not the merged ones. Both sides had
regenerated all three, and a line-by-line merge of two independent solver
outputs is not a solve: it left `py312` and `py313` naming two different
`libpython` builds apiece. This branch's set was generated today, from
requirement files this merge does not touch, and is newer than the one SciTools#7299
carried, so it is taken whole.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 96841741-ed31-4388-9014-e3f94eb82651
Not this branch's code, and not its defect: `.hooks/check_docs_page_metadata.py`
came with SciTools#7308 and ruff moved from 0.15.20 to 0.16.6 with the `main` merge
`brownfield` has just taken, which flags ISC004 on the implicit concatenation
in it. Every pull request targeting `brownfield` meets the same failure. It is
fixed here because it is one line and this pull request is otherwise green;
fixing it on `brownfield` as well would be no bad thing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 96841741-ed31-4388-9014-e3f94eb82651
A cube with an unstructured :class:`~iris.mesh.MeshXY` saves correctly: the
store carries the full CF-UGRID structure, the same mesh, topology and
connectivity variables a netCDF save would write, and loading it back gives
you the mesh again.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this reads a bit weird due to its history of edits. I'd remove "and loading it back gives you the mesh again".

This section is also now connected to a following comment about attributes which is unrelated. This feels like it maybe belongs as a note in the loading section since it is about the behaviour of a loaded cube and this failure to save is not specific to zarr.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I wondered about that too. Probably a case of a too-narrow edit when rebasing. Will chase up

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trexfeathers trexfeathers left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(posted by GPT)

I audited this PR at f8e039b074f97516f98e5e597501cbb870091220 against brownfield at 426d7189342aa3b4db61ebd9e6b2200f97317081. I read the complete 3,044-line specification, including the decision log. Most loading and saving requirements have implementation and test evidence, but the branch does not fully satisfy the specification. No repository files were changed.

Concrete discrepancies

  1. [P2, introduced here] A subgroup save with consolidate_metadata=False leaves stale root metadata. _complete skips metadata work entirely when consolidation is disabled. Opening the subgroup unconsolidated does not invalidate the root document. I reproduced a root whose default group_keys() returned [] while an unconsolidated reader returned ['sub']. Opening the subgroup directly still worked. This violates the discovery/durability contract and the user guide's explicit promise to remove the document. The existing refusal test adds an array at the root; subsequent root attribute writes invalidate that document, so it misses subgroup saves. Root consolidated metadata needs invalidation when declining consolidation, including subgroup saves.

  2. [P2, introduced here] The new CF grid-mapping test fails without the optional Zarr dependency. tests/unit/fileformats/cf/saver/test_grid_mapping_writer.py::TestTheBaseWriter imports the Zarr saver without an importorskip guard. I invoked the test with Zarr imports blocked and obtained ModuleNotFoundError. The guards in the Zarr test packages do not protect a test under fileformats/cf. This violates §6's promise that Iris without Zarr installed acquires no new test failures.

  3. [P2, introduced here] Integer fill values can disagree between storage and CF metadata. ZarrDataset.create_variable casts the storage fill to the variable dtype but writes the original argument as _FillValue. Saving masked int16 data with fill_value=1.5 produced storage fill 1, attribute _FillValue=1.5, and stored values [2, 1]. §4.5 requires a JSON integer and the same native value in both places. Existing tests exercise representable integer sentinels only. Reject an incompatible sentinel or encode the same validated native value in both places.

  4. [P2, introduced here] Two dataset capability contracts fail at their boundaries. Reopening a store containing a two-element array on dimension x, then calling create_dimension('x', 3), succeeded and changed dataset.dimensions to {'x': 3}. The conflict check considers only declarations made during that dataset instance. Separately, supports_dtype(np.dtype('O')) returned True, but create_variable rejected that dtype as ambiguous. Both were reproduced. The ordinary saver's disambiguation tests do not exercise existing-array redeclaration; the capability tests do not establish that every affirmative answer can be created.

  5. [P2, inherited from brownfield] Empty-root diagnostics do not identify deeper data-bearing groups. zarr.loader._check_has_data_variables lists immediate subgroups. For data under measurements/reflectance/r10m, its error suggested only measurements, which also contained no data arrays. This misses §4.6's promise to name groups that contain data. The test uses an immediately data-bearing subgroup. This is an existing programme discrepancy, not a regression introduced by this PR.

  6. The required shared Zarr write contract is missing. test_ZarrDataset_contract.py subclasses only CFDatasetReadContract. No Zarr class runs CFDatasetWriteContract. Separate write tests provide substantial coverage, but they do not fulfil the shared-body requirement in §6 and the decision log.

  7. The specification has unresolved contradictions. §4.4 permits subdividing oversized storage chunks and warns; §6 still requires no subdivision and exact read alignment. Q4 still describes the discarded guard. §4.2 names storage_settings, while later requirements name the nonexistent encoding member. Q10 says the NETCDF3_64BIT defect is preserved, while the later decision log and implementation fix it. The progress header and PR table describe PR 4 as under review despite the later history saying it merged. These need reconciliation before an unqualified conformity judgement is possible.

Verification: the focused selection finished with 1,475 passed, 2 skipped. Adding netCDF tests produced 2,243 passed, 23 skipped, one missing-file failure and ten fixture setup errors. All ten setup-error tests passed separately. No Zarr test failed. Local-store reproductions completed outside the sandbox after standalone zarr.open_group() hung inside it. Actual remote access, minimum-version compatibility, real NOAA/EOPF data, benchmarks, documentation builds and a baseline test checkout were not tested. Real-data/benchmark work, the prose sweep and merge-back remain planned programme obligations rather than unexpected PR 6 regressions.

Core interface, attribute and dimension contracts are mostly delivered; capability boundaries and some specification statements remain partial.

Repeated requirements are consolidated here. Superseded decisions are not treated as current obligations; unresolved contradictory statements are retained. “Delivered” means implementation inspection plus relevant test evidence, or a directly checkable structural requirement. It does not mean every possible input was tested.

Evidence paths below are relative to lib/iris/. CF means fileformats/cf and tests/unit/fileformats/cf. Dataset means fileformats/zarr/_dataset.py and tests/unit/fileformats/zarr/dataset. NC means fileformats/netcdf and its unit tests. Load means the Zarr loader, its unit tests and tests/integration/zarr/test_load.py. Save means the Zarr saver, its unit tests and tests/integration/zarr/test_save.py. Decode and Encode mean the respective Zarr modules and test__decode.py/test__encode.py. IO means io/__init__.py, loading/format dispatch and tests/unit/io. Cross-reader means tests/integration/zarr/test_cross_reader.py.

Requirement Status Implementation and test evidence
CF becomes a package Delivered CF _variables, _group, _reader; test___init__
Existing CF public names remain exported Delivered Static __all__; export tests
Classic and UGRID classifiers remain together Delivered CF _variables; classifier tests
Generic loader is extracted Delivered CF loader; loader tests
Generic saver is extracted and remains private Delivered _CFSaver; Save inheritance test
Zarr exposes load_cubes, save, Saver Delivered Package forwarding table; API/dependency tests
Zarr imports remain lazy Delivered Package forwarding; subprocess import tests
Missing Zarr gives clear load/save errors Delivered Package message; dependency tests
Versions below 3.0.8 are rejected Delivered Metadata version guard; simulated old-version tests
Actual compatibility with Zarr 3.0.8 Unverified Execution used 3.4.1
Dataset and variable ABCs exist Delivered CF dataset; ABC/contract tests
Variable name, location, format are explicit Delivered Both implementations; read contracts
Shape, dtype, size, ndim and length are explicit Delivered Properties; scalar/read contracts
Mode, closed state and context management are explicit Delivered Both implementations; lifecycle contracts
Attributes are materialised mappings Delivered Attribute mappings; contract tests
Storage settings are readable Delivered storage_settings; dataset/property tests
Create-time storage settings remain inspectable Partial Chunks/shards/codecs tested; literal encoding API absent
Generic code avoids backend implementation type tests Delivered CF inspection; test_no_implementation_coupling
Generic dtype handling asks capability rather than format Delivered _ensure_valid_dtype; NC dtype tests
Capability answers are truthful for every input Partial Numeric cases pass; object-dtype reproduction contradicts it
Scalar creation defaults to no dimensions Delivered Dataset/shared/scalar-save tests
Unlimited dimensions are supported or explicitly refused Delivered NC implementation; Zarr refusal tests
Dimension-size conflicts are checked Partial New declarations checked; existing-array conflict accepted
Sync, finalise and close are separate Delivered Dataset implementations; finalisation tests
NetCDF finalise is a no-op Delivered NC implementation; write contract
Write handles work after close Delivered NC contract; Zarr deferred/pickle tests
Library CF attributes use .attributes Delivered Classifiers/load rules inspected; tracking tests
Reads track attribute usage Delivered TrackedAttributes; unused-attribute tests
Separate CF wrappers have independent read sets Delivered Per-wrapper views; tracking tests
Attribute tracking is a snapshot and loading does not write Delivered CF construction; reader isolation tests
Deprecated accessor works for both backends Delivered CF __getattr__; CF/Zarr reader tests
Accessor warns with replacement mapping/key Delivered Accessor; warning-message tests
Missing attributes raise AttributeError Delivered Miss path; missing/default tests
Accessor does not cache dynamic instance members Delivered Source; repeated-read tests
NetCDF-specific fallback survives with warning Delivered deprecated_netcdf_member; NC tests
Normal load avoids deprecation and recorded problems Delivered Silent-load test records warnings and LOAD_PROBLEMS
Colliding attribute names remain reachable Delivered Mapping-based load/save; collision tests
Both Zarr versions read Delivered Dataset version handling; V2/V3 load tests
V3 reads names from dimension_names Delivered _dimension_names; named-dimension tests
V2 reads names from _ARRAY_DIMENSIONS Delivered _dimension_names; V2 tests
Absent names remain anonymous Delivered Dataset; anonymous tests
Null entries remain anonymous Delivered Dataset; partly named tests
Scalars have no dimensions Delivered Dataset; scalar tests
Repeated names within an array are accepted Delivered Dataset; repeated-name test
Wrong-length names warn and become anonymous Delivered Warning path; malformed V2 attribute test
V3 malformation rejected before Iris is distinguished Delivered zarr-python rejection test; defensive Iris check
Dimension identity is (name, size) Delivered _DimensionIdentity; identity/spans tests
Anonymous dimensions match no other dimensions Delivered Identity; anonymous spans tests
Coordinate attachment uses the same identity Delivered _common_data_dims; repeated-name axis test
Conflicting dataset names are omitted with warning Delivered _assemble_dimensions; conflict tests
Variables retain their own names despite conflicts Delivered Dataset variables; conflict integration tests
No control forces equal name/size pairs apart Delivered API inspection; Q9 limitation
_ARRAY_DIMENSIONS does not reach cube attributes Delivered Withholding; V2 integration test
Root _NCProperties does not reach cube attributes Delivered Withholding; NCProperties test
_NCProperties withholding respects documented node scope Partial Withheld on every selected group, not only root
Fixed-width unicode reads normally Delivered Both-version dtype tests
Decoding, cache identity and lazy reading have test evidence; the old read-alignment requirement conflicts with the implemented decision.
Requirement Status Implementation and test evidence
V3 storage fill alone does not mask Delivered Decode precedence; valid-zero tests
V2 storage fill supplies fallback masking Delivered Decode; V2 fallback tests
CF sentinel beats storage fill Delivered Decode; differing-sentinel tests
Both missing-value attributes contribute masks Delivered Decode; combined-sentinel tests
Vector missing_value masks all valid sentinels Delivered Decode vector tests
Vector _FillValue warns and is ignored Delivered Decode vector-fill test
Floating base64 reads as little-endian float64 Delivered Decode NaN/finite tests
Numeric floating sentinel also reads Delivered Decode JSON-number tests
Invalid sentinels warn with variable/value Delivered Invalid/base64-width/range tests
Masking precedes unpacking Delivered mask_and_scale; packed-value masking test
Valid-min/max/range mask values Delivered Decode range tests
Scale/offset unpack data Delivered Pure/integration packing tests
Structured JSON survives uninterpreted Delivered Dataset/CF mappings; structured-attribute tests
Structured attributes are not silently coerced for netCDF Delivered NC refusal; structured-attribute save test
No Zarr read proxy Delivered Direct array graph construction inspected
Decode is an explicit lazy graph layer Delivered read_data; lazy/meta/dtype tests
Small reads can bypass chunk policy Delivered Small-array policy tests
Lazy meta matches decoded dtype/mask Delivered Declared-dtype/meta tests
as_lazy_data takes caller cache identity Delivered Lazy-data layer; cache tests
Lazy layer no longer imports netCDF proxy Delivered Source; loader/cache tests
NetCDF keeps repr(proxy) identity Delivered NC read_data; cache-key regression evidence
Zarr key includes address and complete metadata Delivered cache_key; stability/sensitivity tests
Shape/fill changes miss cache Delivered Rewritten-store tests
Chunks/shards/dtype/codecs/names/attributes affect key Delivered Metadata sensitivity parametrisation
Shared coordinates reuse cached lazy arrays Delivered Cache-population/reuse test
Same-metadata content changes remain acknowledged limitation Delivered Metadata-only implementation; Q6 disclosure
Sizing budgets decoded dtype Delivered Dataset sizing tests
AS_DASK retains Dask sizing semantics Delivered normalize_chunks; Dask-answer test
Read unit is inner chunk, not shard Delivered chunking; instrumented partial-read test
§4.4 allows oversized chunk subdivision and warns Delivered CF warning; subdivision test
§6 forbids subdivision and demands universal read alignment Missing Contradicts §4.4's implemented decision
Explicit chunk controls are honoured Delivered CF chunk-control tests
Oversized warning covers every explicit control Partial AS_DASK/pinned controls bypass warning
Iris leaves Zarr async config unchanged Delivered Source inspection
Async/Dask concurrency interaction is documented Delivered User guide Chunking section
Saving and write alignment are substantially delivered; subgroup invalidation, integer sentinels and dataset boundaries remain partial.
Requirement Status Implementation and test evidence
Write V3 only Delivered Dataset guard; write-version tests
Unsupported versions fail in dataset-level guard Delivered Version guard tests
Reading ignores requested creation version Delivered Read-ignore-version test
Generic saver does not branch on Zarr version Delivered CF source inspection
Dimension declarations determine shape and array names Delivered Dataset creation tests
NumPy scalars become JSON-native Delivered Encode conversion tests
NumPy arrays become lists Delivered Encode/Dataset tests
ASCII bytes become text Delivered Encode/Dataset tests
Non-ASCII strings remain text Delivered Encode/integration tests
Sequences recursively convert NumPy values Delivered Encode sequence tests
Unsupported attrs raise with name before backend write Delivered Encode validation; clean-store refusal test
Unknown objects are not stringified Delivered Encode refusal tests
Attribute tests compare values, not NumPy dtype Delivered Save/Cross-reader tests
General attributes avoid private base64 envelopes Delivered Encode/Cross-reader tests
Floating _FillValue writes base64 float64 Delivered Encode/Cross-reader masked/finite tests
Integer sentinel is integer and matches storage fill Partial Proper inputs pass; 1.5 reproduction fails
missing_value writes ordinary numbers/vectors Delivered Encode/Cross-reader sentinel tests
Masked values are filled without separate mask array Delivered Assignment; mask round-trip tests
Masked data declares _FillValue Delivered Saver eager/lazy tests
Default numeric fill agrees with netCDF Delivered Encode table tests
Unsupported default masked dtype is refused with advice Delivered Float16 refusal/explicit-fill tests
Masked strings are refused Delivered Saver masked-string test
Strings do not invent character dimension Delivered String hook; label tests
Scalar cubes save Delivered Scalar-save test
Zarr applies packing arithmetic Delivered Assignment/Encode; round-trip tests
Invalid packing dtype/dictionary is refused Delivered Prevalidation/refusal tests
Packing precedes masked filling Delivered Assignment; masked packing tests
compute=False returns genuine Delayed Delivered Completion wrapper/deferred tests
Lazy values wait for computation Delivered Deferred timing tests
One unlocked da.store handles collected lazy writes Delivered CF completion inspected; deferred tests
Write tasks tile chunks/shards, allowing truncated last cells Delivered Alignment helper/tests
Incompatible source is rechunked before write Delivered Alignment/sharded-write tests
Lazy chunks inherit source unless shards supplied Delivered Chunks/shards default tests
Whole graph survives plain pickle Delivered Module-level completion; pickle test
Serial/threaded/distributed graph execution works Delivered Save scheduler tests
Consolidation follows all lazy writes Delivered Delayed dependency; ordering test
Finalisation consolidates from whole-store root Delivered Dataset/subgroup reconsolidation tests
Consolidation failure raises Delivered Exception handling/finalisation tests
Default w- refusal is actionable Delivered Dataset/Saver refusal tests
Explicit w replaces Delivered Mode tests
a adds new arrays/groups without data append Delivered Collision/add/refusal tests
Existing data-variable collision names array/group Delivered Pre-existing-name check/tests
Existing array core metadata is not resized/rewritten Delivered Creation/collision paths; add/refusal tests
Old array objects remain byte-for-byte intact after subgroup add Unverified Value evidence; exact byte comparison absent
Declining consolidation never leaves stale root document Partial Root test passes; subgroup reproduction fails
Interrupted saves make no rollback promise Delivered Saver/user-guide notes
Missing-chunk silent fill is pinned Delivered Integration format-behaviour test
Stale consolidated discovery is pinned Delivered Integration format-behaviour test
Dask locks relocate to CF Delivered File location; NC lock tests
Generic saver holds no backend lock/scheduler Delivered Source/write-handle delegation
Zarr multi-process coordination remains deferred Delivered API/source; present alignment safety
Native storage keywords reach arrays Delivered Encoding tests
NetCDF keywords are refused with replacement/advice Delivered Validator/refusal tests
order and append are refused Delivered Keyword/refusal tests
Consolidation defaults on; expected Zarr warning suppressed Delivered Defaults/filter/tests
Dispatch, groups, meshes and deprecation aliases are delivered for covered cases; deep-group discovery and real remote access remain incomplete or unverified.
Requirement Status Implementation and test evidence
Root group is default Delivered Dataset/Load/Save tests
Slash-separated group selects one group Delivered Group tests
Group implies a unless explicit mode Delivered Saver mode tests
Nested groups are not auto-loaded Delivered Dataset/Load tests
Empty-group error names data-bearing groups Partial Immediate groups only; deep-hierarchy reproduction
Local marker, not suffix alone, determines store Delivered Registry/ZarrStore tests
Remote supported scheme plus suffix determines store Delivered Scheme parametrisation
File handle rules out ZarrStore Delivered File element; basename-collision regression
URI expansion preserves remote URLs Delivered IO remote tests
Stores use load_stores; files keep _load_files Delivered Caller inspection; IO tests
Mixed file/store globs work Delivered _generate_cubes; mixed-glob test
Glob expansion happens once Delivered Caller trace to private file loader
Duplicate matches from different patterns survive Unverified Implementation supports it; dedicated assertion not identified
Saver detection accepts trailing separator Delivered find_saver; separator tests
CubeList saver registration uses function identity Delivered IO registry/tests
Both extension and explicit Zarr saver routes work Delivered Wrapper/self-registration; tests
Mesh loading reaches Zarr handler Delivered UGRID dispatch; load_meshes tests
Cubes rebuild mesh coordinates/connectivity Delivered Shared helpers; V2/V3 mesh tests
Mesh cubes round-trip through save Delivered Save integration tests
NCZarr fragments keep separate dispatch Delivered URI categorisation/format tests
S3 docs describe native loading/saving Delivered User guide/S3 tutorial
Iris adds no credentials/filesystem dependencies Delivered Source/requirements inspection
Actual remote connectivity/credentials Unverified Mocked routing; no network tests
Loader aliases are identical objects and warn on use Delivered NC forwarding/relocated-name tests
Saver constants/map aliases retain names and warnings Delivered NC forwarding/alias tests
NetCDF import stays quiet Delivered Deferred forwarding/import tests
Deprecations are marked 3.17 Delivered Docstrings inspected
Eventual NEP29 removal Unverified Future release obligation
NetCDF save/load/Saver/proxy/DEBUG stay in place Delivered NC exports/API/DEBUG tests
cf_patch gets raw netCDF objects Delivered Hook override/tests
Borrowed netCDF datasets retain ownership/string decoding Delivered Wrapping/reader/user-dataset tests
Restricted netCDF integer formats narrow safely Delivered Capability/dtype tests
NETCDF3_64BIT narrows or rejects overflow Delivered Restricted-format constant/dtype tests
Shared Zarr write tests, real fixtures, benchmarks and merge-back work remain outstanding; release and external-process obligations were not verified.
Testing/programme obligation Status Evidence or remaining work
Shared read contract runs on both backends Delivered NC/Zarr read subclasses
Shared write contract runs on both backends Partial NC runs it; Zarr subclass missing
Automated backend-type coupling check Delivered CF source check test
Pure decode unit tests Delivered Decode suite
Sharded partial read measures bytes with decode present Delivered Dataset sharding test
Sharded write corruption case Delivered Alignment tests
Realistic round trips cover coordinates/bounds/factories/methods/systems Delivered Save round-trip suite
Bidirectional NaN/finite/integer sentinel conformance Delivered Cross-reader parametrisation
Xarray conformance covers every supported output form Partial Small numeric/attribute cases, no exhaustive matrix
Xarray test-only in all three requirement files Delivered py312/py313/py314 requirement sections
All three locks include Xarray Delivered Lock entries inspected
V2 synthetic and NCZarr-written fixtures Delivered Dataset/integration Load tests
Store tests avoid remote fetching Delivered Construction/routing tests inspected
Missing Zarr adds no test failures Partial Package guards; unguarded new CF test
NOAA fixture preserves attributes/sharding/licence/DOI Missing Synthetic GFS-like fixture only in repository tests
ESA cropped fixture preserves hierarchy/codecs/flat and nested metadata Missing No contributed EOPF integration fixture/tests
Fixtures contributed to iris-test-data Unverified External contribution not audited
Zarr save ASV benchmark Missing No Zarr benchmark source
Zarr load/realise ASV variants Missing No Zarr variants found
Final prose sweep complete Unverified No complete main-to-branch prose audit
Separate PRs, labels, attribution and courtesy credit Unverified GitHub posts/history not audited
Historical relocations preserve baseline behaviour Unverified Current tests run; historical base execution absent
Feature PR bodies explicitly defer fragments Unverified Bodies not inspected
Merge-back has closing keywords/five credited fragment types Missing Deferred merge-back work
Docs build against main with warnings as errors Unverified No Sphinx build
Deferred loader/mesh-dispatch unification issue raised at merge-back Missing TODO exists; merge-back obligation remains
Superseded questions/status reconciled Partial Q4/Q10/API naming/PR status contradictions
The focused suite passed; broader failures were a missing fixture and collection-dependent setup errors, not failing Zarr assertions.

I traced callers through URI categorisation, glob expansion, format selection, CF classification, coordinate attachment and mesh construction. For saving I traced both selection routes, attribute marshalling, creation, packing/filling, alignment, deferred completion and root finalisation. I inspected brownfield...HEAD changes rather than inferring delivery from commit descriptions or passing tests alone.

Run Result
Pure Zarr encode/decode 86 passed
Zarr/CF/load-rule/IO units and Zarr integration 1,475 passed, 2 skipped
Same selection plus netCDF units/integration 2,243 passed, 23 skipped, 1 failed, 10 setup errors
String failure and coordinate-system module separately 10 passed, 1 failed

Tests used python -m pytest -q -o addopts=-ra, with caches under /tmp. The focused directories were lib/iris/tests/unit/fileformats/zarr, lib/iris/tests/unit/fileformats/cf, lib/iris/tests/unit/fileformats/nc_load_rules, lib/iris/tests/unit/io, and lib/iris/tests/integration/zarr. The broader run added lib/iris/tests/unit/fileformats/netcdf and lib/iris/tests/integration/netcdf.

The remaining failure was TestChunkedCharData.test_load: external fixture NetCDF/testing/chunked_char_data.nc was absent. The ten errors came from the coordinate-system module fixture not yielding when another collected module had already set its temporary-directory attribute. All ten passed separately. Skips included unavailable iris_grib, one FUTURE-context case and unsupported netCDF string combinations. The test-data override pointed to /data/home/martin.yeo/repos/iris-test-data/test_data.

Execution used Python 3.12.14, NumPy 2.5.2, Dask 2026.8.0, Zarr 3.4.1 and Xarray 2026.9.0. Local-store boundary scripts verified subgroup discovery, existing dimension redeclaration, object-dtype capability, deep-group diagnostics and integer fill disagreement. The optional-dependency check directly invoked the new CF test after blocking Zarr imports. An initial pytest-based simulation did not demonstrate the absence condition; the direct blocked-import invocation did. No actual package was removed.

I did not run pre-commit hooks, which can rewrite files, or a documentation build. I did not test remote credentials/connectivity, Zarr 3.0.8, real NOAA/EOPF data, benchmarks or a baseline checkout. No repository files were changed.

@github-actions

Copy link
Copy Markdown
Contributor

⏱️ Performance Benchmark Report: 36f1d28

Performance shifts
| Change   | Before [426d7189]    | After [36f1d281]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 820±2k               | 1.23±0M             |    1.51 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |
Full benchmark results

Benchmarks that have stayed the same:

| Change   | Before [426d7189]    | After [36f1d281]    |   Ratio | Benchmark (Parameter)                                                                       |
|----------|----------------------|---------------------|---------|---------------------------------------------------------------------------------------------|
|          | 12.8±1ms             | 12.1±0.4ms          |    0.94 | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(False)                              |
|          | 29.8±0.7ms           | 31.1±0.8ms          |    1.05 | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(True)                               |
|          | 20.9±2ms             | 20.4±0.8ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(False)                    |
|          | 95.0±2ms             | 96.5±4ms            |    1.02 | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(True)                     |
|          | 13.7±2ms             | 13.2±0.6ms          |    0.96 | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(False)                              |
|          | 18.1±0.5ms           | 18.8±0.9ms          |    1.03 | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(True)                               |
|          | 14.1±2ms             | 13.8±0.8ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(False)                              |
|          | 17.9±0.3ms           | 18.2±0.7ms          |    1.02 | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(True)                               |
|          | 12.2±2ms             | 12.1±0.4ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_MAX(False)                                |
|          | 25.6±0.6ms           | 26.0±0.8ms          |    1.02 | aggregate_collapse.Aggregation.time_aggregated_by_MAX(True)                                 |
|          | 76.8±1ms             | 76.9±3ms            |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(False)                            |
|          | 73.1±1ms             | 75.1±2ms            |    1.03 | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(True)                             |
|          | 12.6±2ms             | 12.6±0.5ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(False)                               |
|          | 27.9±1ms             | 27.8±0.7ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(True)                                |
|          | 13.4±4ms             | 13.2±0.5ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(False)                             |
|          | 32.7±0.7ms           | 33.0±1ms            |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(True)                              |
|          | 12.1±2ms             | 11.9±0.4ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_MIN(False)                                |
|          | 25.7±0.6ms           | 24.7±0.6ms          |    0.96 | aggregate_collapse.Aggregation.time_aggregated_by_MIN(True)                                 |
|          | 547±20ms             | 549±10ms            |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(False)                               |
|          | 526±10ms             | 527±7ms             |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(True)                                |
|          | 92.8±2ms             | 92.6±2ms            |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(False)                         |
|          | 165±6ms              | 160±5ms             |    0.97 | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(True)                          |
|          | 12.7±1ms             | 12.8±0.5ms          |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(False)                         |
|          | 17.3±0.4ms           | 17.6±1ms            |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(True)                          |
|          | 12.8±1ms             | 12.9±0.6ms          |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_RMS(False)                                |
|          | 33.0±0.5ms           | 34.1±1ms            |    1.04 | aggregate_collapse.Aggregation.time_aggregated_by_RMS(True)                                 |
|          | 13.6±2ms             | 13.1±0.5ms          |    0.96 | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(False)                            |
|          | 35.3±2ms             | 34.1±0.8ms          |    0.97 | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(True)                             |
|          | 13.3±2ms             | 13.1±0.6ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(False)                           |
|          | 32.9±1ms             | 32.5±0.7ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(True)                            |
|          | 4.38±0.1ms           | 4.29±0.1ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(False)                               |
|          | 12.7±0.4ms           | 12.2±0.3ms          |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(True)                                |
|          | 11.0±0.2ms           | 11.0±0.3ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(False)                     |
|          | 74.1±10ms            | 70.1±2ms            |    0.95 | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(True)                      |
|          | 4.62±0.1ms           | 4.56±0.06ms         |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(False)                               |
|          | 11.3±0.4ms           | 10.7±0.4ms          |    0.95 | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(True)                                |
|          | 4.63±0.05ms          | 4.54±0.06ms         |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(False)                               |
|          | 11.2±0.4ms           | 11.5±0.6ms          |    1.02 | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(True)                                |
|          | 4.27±0.06ms          | 4.18±0.2ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_MAX(False)                                 |
|          | 11.3±0.4ms           | 11.0±0.2ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_MAX(True)                                  |
|          | 15.4±2ms             | 15.5±0.7ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(False)                             |
|          | 20.5±0.6ms           | 20.4±0.6ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(True)                              |
|          | 4.49±0.06ms          | 4.31±0.2ms          |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(False)                                |
|          | 11.7±0.3ms           | 11.3±0.4ms          |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(True)                                 |
|          | 4.90±0.05ms          | 4.80±0.09ms         |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(False)                              |
|          | 13.2±0.3ms           | 13.2±0.7ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(True)                               |
|          | 4.27±0.03ms          | 4.14±0.1ms          |    0.97 | aggregate_collapse.Aggregation.time_collapsed_by_MIN(False)                                 |
|          | 12.2±0.4ms           | 11.2±0.5ms          |    0.92 | aggregate_collapse.Aggregation.time_collapsed_by_MIN(True)                                  |
|          | 335±6ms              | 333±3ms             |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(False)                                |
|          | 336±2ms              | 335±3ms             |    1    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(True)                                 |
|          | 21.0±0.4ms           | 21.2±0.3ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(False)                          |
|          | 75.1±10ms            | 70.5±1ms            |    0.94 | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(True)                           |
|          | 4.54±0.2ms           | 4.37±0.05ms         |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(False)                          |
|          | 11.1±0.6ms           | 11.1±0.6ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(True)                           |
|          | 4.53±0.06ms          | 4.44±0.1ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_RMS(False)                                 |
|          | 12.7±0.2ms           | 12.4±0.5ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_RMS(True)                                  |
|          | 4.58±0.03ms          | 4.56±0.08ms         |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(False)                             |
|          | 12.7±0.4ms           | 12.5±0.4ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(True)                              |
|          | 4.66±0.07ms          | 4.62±0.05ms         |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(False)                            |
|          | 12.0±0.4ms           | 11.8±0.4ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(True)                             |
|          | 13.1±2ms             | 12.8±0.3ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(False)                     |
|          | 45.6±0.9ms           | 46.9±1ms            |    1.03 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(True)                      |
|          | 13.0±2ms             | 12.6±0.4ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(False)                      |
|          | 53.0±1ms             | 54.1±0.6ms          |    1.02 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(True)                       |
|          | 12.5±1ms             | 12.3±0.7ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(False)                      |
|          | 30.6±0.7ms           | 30.4±0.3ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(True)                       |
|          | 4.62±0.04ms          | 4.53±0.09ms         |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(False)                      |
|          | 16.2±0.5ms           | 15.8±0.4ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(True)                       |
|          | 4.49±0.05ms          | 4.49±0.06ms         |    1    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(False)                       |
|          | 17.1±0.8ms           | 16.7±0.5ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(True)                        |
|          | 4.43±0.2ms           | 4.31±0.06ms         |    0.97 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(False)                       |
|          | 12.9±0.4ms           | 13.1±0.7ms          |    1.02 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(True)                        |
|          | 85.9±0.9ms           | 87.2±0.8ms          |    1.01 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(False)               |
|          | 131±5ms              | 129±3ms             |    0.99 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(True)                |
|          | 671±10μs             | 663±10μs            |    0.99 | cube.CubeCreation.time_create(False, 'construct')                                           |
|          | 291±10μs             | 288±20μs            |    0.99 | cube.CubeCreation.time_create(False, 'instantiate')                                         |
|          | 1.02±0.02ms          | 1.02±0.02ms         |    1    | cube.CubeCreation.time_create(True, 'construct')                                            |
|          | 739±10μs             | 749±20μs            |    1.01 | cube.CubeCreation.time_create(True, 'instantiate')                                          |
|          | 50.8±1ms             | 52.0±2ms            |    1.02 | cube.CubeEquality.time_equality(False, False, 'all_equal')                                  |
|          | 15.0±0.6ms           | 16.1±1ms            |    1.07 | cube.CubeEquality.time_equality(False, False, 'coord_inequality')                           |
|          | 60.8±2ms             | 60.8±2ms            |    1    | cube.CubeEquality.time_equality(False, False, 'data_inequality')                            |
|          | 49.9±2μs             | 54.5±5μs            |    1.09 | cube.CubeEquality.time_equality(False, False, 'metadata_inequality')                        |
|          | 53.3±2ms             | 54.5±2ms            |    1.02 | cube.CubeEquality.time_equality(False, True, 'all_equal')                                   |
|          | 17.0±0.2ms           | 17.8±0.6ms          |    1.05 | cube.CubeEquality.time_equality(False, True, 'coord_inequality')                            |
|          | 62.5±1ms             | 62.9±3ms            |    1.01 | cube.CubeEquality.time_equality(False, True, 'data_inequality')                             |
|          | 52.0±2μs             | 53.0±2μs            |    1.02 | cube.CubeEquality.time_equality(False, True, 'metadata_inequality')                         |
|          | 100±5ms              | 100.0±4ms           |    1    | cube.CubeEquality.time_equality(True, False, 'all_equal')                                   |
|          | 40.1±6ms             | 38.2±4ms            |    0.95 | cube.CubeEquality.time_equality(True, False, 'coord_inequality')                            |
|          | 117±20ms             | 120±9ms             |    1.03 | cube.CubeEquality.time_equality(True, False, 'data_inequality')                             |
|          | 57.0±4μs             | 61.1±3μs            |    1.07 | cube.CubeEquality.time_equality(True, False, 'metadata_inequality')                         |
|          | 135±3ms              | 135±3ms             |    1.01 | cube.CubeEquality.time_equality(True, True, 'all_equal')                                    |
|          | 76.3±2ms             | 77.2±2ms            |    1.01 | cube.CubeEquality.time_equality(True, True, 'coord_inequality')                             |
|          | 160±4ms              | 157±5ms             |    0.99 | cube.CubeEquality.time_equality(True, True, 'data_inequality')                              |
|          | 214±6μs              | 215±5μs             |    1.01 | cube.CubeEquality.time_equality(True, True, 'metadata_inequality')                          |
|          | 387±6μs              | 389±20μs            |    1.01 | import_iris.Iris.time__concatenate                                                          |
|          | 81.6±3μs             | 81.6±2μs            |    1    | import_iris.Iris.time__constraints                                                          |
|          | 49.6±0.8μs           | 49.8±1μs            |    1.01 | import_iris.Iris.time__data_manager                                                         |
|          | 41.8±0.8μs           | 43.0±0.8μs          |    1.03 | import_iris.Iris.time__deprecation                                                          |
|          | 72.1±3μs             | 71.8±1μs            |    0.99 | import_iris.Iris.time__lazy_data                                                            |
|          | 426±5μs              | 434±8μs             |    1.02 | import_iris.Iris.time__merge                                                                |
|          | 34.1±0.3μs           | 35.4±0.4μs          |    1.04 | import_iris.Iris.time__representation                                                       |
|          | 316±6μs              | 321±20μs            |    1.02 | import_iris.Iris.time_analysis                                                              |
|          | 62.3±1μs             | 59.1±0.5μs          |    0.95 | import_iris.Iris.time_analysis__area_weighted                                               |
|          | 54.4±1μs             | 54.2±0.7μs          |    1    | import_iris.Iris.time_analysis__grid_angles                                                 |
|          | 112±3μs              | 106±1μs             |    0.95 | import_iris.Iris.time_analysis__interpolation                                               |
|          | 86.5±2μs             | 83.8±2μs            |    0.97 | import_iris.Iris.time_analysis__regrid                                                      |
|          | 48.5±2μs             | 48.3±1μs            |    1    | import_iris.Iris.time_analysis__scipy_interpolate                                           |
|          | 61.3±0.7μs           | 60.8±0.3μs          |    0.99 | import_iris.Iris.time_analysis_calculus                                                     |
|          | 150±2μs              | 149±7μs             |    0.99 | import_iris.Iris.time_analysis_cartography                                                  |
|          | 39.0±0.3μs           | 39.1±0.4μs          |    1    | import_iris.Iris.time_analysis_geomerty                                                     |
|          | 102±1μs              | 98.4±2μs            |    0.96 | import_iris.Iris.time_analysis_maths                                                        |
|          | 40.1±0.4μs           | 40.3±0.2μs          |    1    | import_iris.Iris.time_analysis_stats                                                        |
|          | 76.9±1μs             | 75.2±0.5μs          |    0.98 | import_iris.Iris.time_analysis_trajectory                                                   |
|          | 152±5μs              | 152±3μs             |    1    | import_iris.Iris.time_aux_factory                                                           |
|          | 37.8±0.2μs           | 38.6±0.2μs          |    1.02 | import_iris.Iris.time_common                                                                |
|          | 69.5±2μs             | 68.8±0.9μs          |    0.99 | import_iris.Iris.time_common_lenient                                                        |
|          | 696±10μs             | 723±5μs             |    1.04 | import_iris.Iris.time_common_metadata                                                       |
|          | 77.4±2μs             | 75.7±0.6μs          |    0.98 | import_iris.Iris.time_common_mixin                                                          |
|          | 656±5μs              | 651±6μs             |    0.99 | import_iris.Iris.time_common_resolve                                                        |
|          | 110±2μs              | 110±1μs             |    0.99 | import_iris.Iris.time_config                                                                |
|          | 57.4±0.3μs           | 56.8±0.4μs          |    0.99 | import_iris.Iris.time_coord_categorisation                                                  |
|          | 172±4μs              | 177±5μs             |    1.03 | import_iris.Iris.time_coord_systems                                                         |
|          | 440±10μs             | 457±7μs             |    1.04 | import_iris.Iris.time_coords                                                                |
|          | 435±9μs              | 423±4μs             |    0.97 | import_iris.Iris.time_cube                                                                  |
|          | 120±2μs              | 121±2μs             |    1.02 | import_iris.Iris.time_exceptions                                                            |
|          | 35.3±1μs             | 35.4±0.2μs          |    1    | import_iris.Iris.time_experimental                                                          |
|          | 97.3±2μs             | 97.4±1μs            |    1    | import_iris.Iris.time_fileformats                                                           |
|          | 114±5μs              | 114±3μs             |    1    | import_iris.Iris.time_fileformats__ff                                                       |
|          | 1.43±0.02ms          | 1.52±0.01ms         |    1.06 | import_iris.Iris.time_fileformats__ff_cross_references                                      |
|          | 34.4±1μs             | 35.1±0.5μs          |    1.02 | import_iris.Iris.time_fileformats__pp_lbproc_pairs                                          |
|          | 47.0±0.3μs           | 46.9±2μs            |    1    | import_iris.Iris.time_fileformats_abf                                                       |
|          | 39.7±0.2μs           | 40.0±0.3μs          |    1.01 | import_iris.Iris.time_fileformats_cf                                                        |
|          | 3.30±0.01ms          | 3.36±0.02ms         |    1.02 | import_iris.Iris.time_fileformats_dot                                                       |
|          | 31.4±0.3μs           | 32.0±0.3μs          |    1.02 | import_iris.Iris.time_fileformats_name                                                      |
|          | 110±1μs              | 109±4μs             |    1    | import_iris.Iris.time_fileformats_name_loaders                                              |
|          | 58.8±1μs             | 60.3±1μs            |    1.03 | import_iris.Iris.time_fileformats_netcdf                                                    |
|          | 67.2±1μs             | 69.4±0.9μs          |    1.03 | import_iris.Iris.time_fileformats_nimrod                                                    |
|          | 146±6μs              | 149±3μs             |    1.02 | import_iris.Iris.time_fileformats_nimrod_load_rules                                         |
|          | 414±7μs              | 418±10μs            |    1.01 | import_iris.Iris.time_fileformats_pp                                                        |
|          | 78.5±0.6μs           | 77.7±0.9μs          |    0.99 | import_iris.Iris.time_fileformats_pp_load_rules                                             |
|          | 58.6±2μs             | 58.6±1μs            |    1    | import_iris.Iris.time_fileformats_pp_save_rules                                             |
|          | 232±5μs              | 236±20μs            |    1.02 | import_iris.Iris.time_fileformats_rules                                                     |
|          | 93.5±1μs             | 92.0±0.9μs          |    0.98 | import_iris.Iris.time_fileformats_structured_array_identification                           |
|          | 37.8±0.8μs           | 37.9±0.6μs          |    1    | import_iris.Iris.time_fileformats_um                                                        |
|          | 69.8±4μs             | 66.9±0.8μs          |    0.96 | import_iris.Iris.time_fileformats_um__fast_load                                             |
|          | 56.6±2μs             | 54.9±0.4μs          |    0.97 | import_iris.Iris.time_fileformats_um__fast_load_structured_fields                           |
|          | 32.7±0.8μs           | 32.8±0.2μs          |    1    | import_iris.Iris.time_fileformats_um__ff_replacement                                        |
|          | 34.6±0.7μs           | 34.4±0.2μs          |    0.99 | import_iris.Iris.time_fileformats_um__optimal_array_structuring                             |
|          | 495±7μs              | 517±5μs             |    1.05 | import_iris.Iris.time_fileformats_um_cf_map                                                 |
|          | 70.4±2μs             | 69.1±0.5μs          |    0.98 | import_iris.Iris.time_io                                                                    |
|          | 83.5±3μs             | 82.4±1μs            |    0.99 | import_iris.Iris.time_io_format_picker                                                      |
|          | 97.1±2μs             | 97.4±2μs            |    1    | import_iris.Iris.time_iris                                                                  |
|          | 53.6±0.8μs           | 51.6±0.7μs          |    0.96 | import_iris.Iris.time_iterate                                                               |
|          | 3.56±0.04ms          | 3.53±0.03ms         |    0.99 | import_iris.Iris.time_palette                                                               |
|          | 1.04±0.04ms          | 1.06±0.08ms         |    1.02 | import_iris.Iris.time_plot                                                                  |
|          | 159±30μs             | 148±30μs            |    0.93 | import_iris.Iris.time_quickplot                                                             |
|          | 1.38±0.01ms          | 1.40±0.03ms         |    1.01 | import_iris.Iris.time_std_names                                                             |
|          | 842±10μs             | 876±9μs             |    1.04 | import_iris.Iris.time_symbols                                                               |
|          | 165±8ms              | 153±20ms            |    0.93 | import_iris.Iris.time_tests                                                                 |
|          | 131±0.3μs            | 133±2μs             |    1.01 | import_iris.Iris.time_third_party_cartopy                                                   |
|          | 3.25±0.1ms           | 3.24±0.03ms         |    1    | import_iris.Iris.time_third_party_cf_units                                                  |
|          | 62.3±0.7μs           | 64.2±0.5μs          |    1.03 | import_iris.Iris.time_third_party_cftime                                                    |
|          | 1.58±0.03ms          | 1.62±0.4ms          |    1.02 | import_iris.Iris.time_third_party_matplotlib                                                |
|          | 248±7μs              | 243±3μs             |    0.98 | import_iris.Iris.time_third_party_numpy                                                     |
|          | 85.4±1μs             | 84.9±0.5μs          |    0.99 | import_iris.Iris.time_third_party_scipy                                                     |
|          | 42.6±1μs             | 41.2±0.6μs          |    0.97 | import_iris.Iris.time_time                                                                  |
|          | 509±10μs             | 505±8μs             |    0.99 | import_iris.Iris.time_util                                                                  |
|          | 33.6±0.6μs           | 34.8±0.4μs          |    1.04 | iterate.IZip.time_izip                                                                      |
|          | 5.42±0.03ms          | 5.34±0.04ms         |    0.99 | load.LoadAndRealise.time_load((1280, 960, 5), False, 'FF')                                  |
|          | 8.63±0.3ms           | 8.34±0.6ms          |    0.97 | load.LoadAndRealise.time_load((1280, 960, 5), False, 'NetCDF')                              |
|          | 5.36±0.06ms          | 5.27±0.2ms          |    0.98 | load.LoadAndRealise.time_load((1280, 960, 5), False, 'PP')                                  |
|          | 5.34±0.07ms          | 5.28±0.05ms         |    0.99 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'FF')                                   |
|          | 6.41±0.2ms           | 6.35±0.2ms          |    0.99 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'NetCDF')                               |
|          | 5.31±0.2ms           | 5.16±0.2ms          |    0.97 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'PP')                                   |
|          | 774±9ms              | 767±7ms             |    0.99 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'FF')                                    |
|          | 5.94±0.04ms          | 5.89±0.2ms          |    0.99 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'NetCDF')                                |
|          | 767±20ms             | 754±10ms            |    0.98 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'PP')                                    |
|          | 766±20ms             | 770±20ms            |    1.01 | load.LoadAndRealise.time_load((2, 2, 1000), True, 'FF')                                     |
|          | 5.96±0.2ms           | 5.99±0.2ms          |    1.01 | load.LoadAndRealise.time_load((2, 2, 1000), True, 'NetCDF')                                 |
|          | 766±10ms             | 754±10ms            |    0.98 | load.LoadAndRealise.time_load((2, 2, 1000), True, 'PP')                                     |
|          | 2.94±0.03ms          | 2.90±0.05ms         |    0.99 | load.LoadAndRealise.time_load((50, 50, 2), False, 'FF')                                     |
|          | 5.70±0.1ms           | 5.59±0.2ms          |    0.98 | load.LoadAndRealise.time_load((50, 50, 2), False, 'NetCDF')                                 |
|          | 2.80±0.04ms          | 2.84±0.08ms         |    1.01 | load.LoadAndRealise.time_load((50, 50, 2), False, 'PP')                                     |
|          | 2.90±0.05ms          | 2.85±0.1ms          |    0.98 | load.LoadAndRealise.time_load((50, 50, 2), True, 'FF')                                      |
|          | 5.85±0.2ms           | 5.77±0.08ms         |    0.99 | load.LoadAndRealise.time_load((50, 50, 2), True, 'NetCDF')                                  |
|          | 2.86±0.06ms          | 2.83±0.1ms          |    0.99 | load.LoadAndRealise.time_load((50, 50, 2), True, 'PP')                                      |
|          | 16.2±0.9ms           | 15.1±0.5ms          |    0.93 | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'FF')                               |
|          | 14.9±0.4ms           | 14.9±0.4ms          |    1    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'NetCDF')                           |
|          | 8.48±0.8ms           | 8.27±0.7ms          |    0.98 | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'PP')                               |
|          | 18.0±3ms             | 17.9±1ms            |    1    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'FF')                                |
|          | 53.8±1ms             | 53.6±0.7ms          |    1    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'NetCDF')                            |
|          | 17.1±1ms             | 16.7±0.6ms          |    0.98 | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'PP')                                |
|          | 326±7ms              | 317±6ms             |    0.97 | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'FF')                                 |
|          | 1.85±0.04ms          | 1.79±0.03ms         |    0.97 | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'NetCDF')                             |
|          | 324±8ms              | 325±5ms             |    1    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'PP')                                 |
|          | 320±5ms              | 330±6ms             |    1.03 | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'FF')                                  |
|          | 2.08±0.2ms           | 1.87±0.06ms         |    0.9  | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'NetCDF')                              |
|          | 318±10ms             | 326±6ms             |    1.02 | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'PP')                                  |
|          | 1.27±0.06ms          | 1.23±0.1ms          |    0.97 | load.LoadAndRealise.time_realise((50, 50, 2), False, 'FF')                                  |
|          | 1.79±0.06ms          | 1.82±0.03ms         |    1.01 | load.LoadAndRealise.time_realise((50, 50, 2), False, 'NetCDF')                              |
|          | 1.22±0.04ms          | 1.24±0.01ms         |    1.02 | load.LoadAndRealise.time_realise((50, 50, 2), False, 'PP')                                  |
|          | 1.33±0.03ms          | 1.27±0.02ms         |    0.96 | load.LoadAndRealise.time_realise((50, 50, 2), True, 'FF')                                   |
|          | 2.01±0.1ms           | 1.85±0.2ms          |    0.92 | load.LoadAndRealise.time_realise((50, 50, 2), True, 'NetCDF')                               |
|          | 1.20±0.03ms          | 1.20±0.02ms         |    1    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'PP')                                   |
|          | 174±2ms              | 173±4ms             |    1    | load.ManyCubes.time_many_cube_load                                                          |
|          | 40.2±1ms             | 38.9±0.6ms          |    0.97 | load.ManyVars.time_many_var_load                                                            |
|          | 5.47±0.09ms          | 5.32±0.1ms          |    0.97 | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'FF')                            |
|          | 5.37±0.06ms          | 5.40±0.07ms         |    1.01 | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'PP')                            |
|          | 775±10ms             | 776±20ms            |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'FF')                              |
|          | 757±10ms             | 754±10ms            |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'PP')                              |
|          | 3.02±0.06ms          | 2.96±0.01ms         |    0.98 | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'FF')                                 |
|          | 3.07±0.3ms           | 2.88±0.02ms         |    0.94 | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'PP')                                 |
|          | 4.84±0.05ms          | 4.92±0.1ms          |    1.02 | load.StructuredFF.time_structured_load((1280, 960, 5), False)                               |
|          | 3.07±0.2ms           | 3.01±0.04ms         |    0.98 | load.StructuredFF.time_structured_load((1280, 960, 5), True)                                |
|          | 771±20ms             | 760±10ms            |    0.99 | load.StructuredFF.time_structured_load((2, 2, 1000), False)                                 |
|          | 200±3ms              | 199±2ms             |    1    | load.StructuredFF.time_structured_load((2, 2, 1000), True)                                  |
|          | 2.42±0.02ms          | 2.47±0.01ms         |    1.02 | load.StructuredFF.time_structured_load((2, 2, 2), False)                                    |
|          | 2.36±0.01ms          | 2.40±0.02ms         |    1.01 | load.StructuredFF.time_structured_load((2, 2, 2), True)                                     |
|          | 75.9±2ms             | 75.3±1ms            |    0.99 | load.TimeConstraint.time_time_constraint(20, 'FF')                                          |
|          | 7.30±0.2ms           | 7.54±0.2ms          |    1.03 | load.TimeConstraint.time_time_constraint(20, 'NetCDF')                                      |
|          | 73.4±2ms             | 76.1±2ms            |    1.04 | load.TimeConstraint.time_time_constraint(20, 'PP')                                          |
|          | 15.7±0.2ms           | 15.6±0.3ms          |    1    | load.TimeConstraint.time_time_constraint(3, 'FF')                                           |
|          | 7.09±0.06ms          | 7.00±0.02ms         |    0.99 | load.TimeConstraint.time_time_constraint(3, 'NetCDF')                                       |
|          | 15.2±0.3ms           | 15.3±0.2ms          |    1.01 | load.TimeConstraint.time_time_constraint(3, 'PP')                                           |
|          | 9.76±1ms             | 9.37±0.1ms          |    0.96 | load.ugrid.BasicLoading.time_load_file(1)                                                   |
|          | 28.0±2ms             | 27.6±1ms            |    0.99 | load.ugrid.BasicLoading.time_load_file(200000)                                              |
|          | 4.21±0.3ms           | 4.13±0.06ms         |    0.98 | load.ugrid.BasicLoading.time_load_mesh(1)                                                   |
|          | 9.40±0.6ms           | 9.44±0.5ms          |    1    | load.ugrid.BasicLoading.time_load_mesh(200000)                                              |
|          | 9.97±0.6ms           | 9.63±0.3ms          |    0.97 | load.ugrid.BasicLoadingTime.time_load_file(1)                                               |
|          | 10.9±0.5ms           | 10.0±0.2ms          |    0.92 | load.ugrid.BasicLoadingTime.time_load_file(200000)                                          |
|          | 4.11±0.1ms           | 4.20±0.05ms         |    1.02 | load.ugrid.BasicLoadingTime.time_load_mesh(1)                                               |
|          | 6.23±0.4ms           | 6.37±0.4ms          |    1.02 | load.ugrid.BasicLoadingTime.time_load_mesh(200000)                                          |
|          | 10.9±0.6ms           | 10.3±0.1ms          |    0.95 | load.ugrid.Callback.time_load_file_callback(1)                                              |
|          | 35.9±2ms             | 34.2±0.7ms          |    0.95 | load.ugrid.Callback.time_load_file_callback(200000)                                         |
|          | 10.5±0.6ms           | 10.5±0.2ms          |    1    | load.ugrid.CallbackTime.time_load_file_callback(1)                                          |
|          | 12.5±1ms             | 13.1±0.8ms          |    1.05 | load.ugrid.CallbackTime.time_load_file_callback(200000)                                     |
|          | 2.13±0.5ms           | 1.90±0.2ms          |    0.89 | load.ugrid.DataRealisation.time_realise_data(10000)                                         |
|          | 2.92±0.5ms           | 2.64±0.2ms          |    0.9  | load.ugrid.DataRealisation.time_realise_data(200000)                                        |
|          | 38.7±5ms             | 34.3±2ms            |    0.89 | load.ugrid.DataRealisationTime.time_realise_data(10000)                                     |
|          | 898±30ms             | 871±40ms            |    0.97 | load.ugrid.DataRealisationTime.time_realise_data(200000)                                    |
|          | 628±10ms             | 638±40ms            |    1.02 | merge_concat.Concatenate.time_concatenate(False)                                            |
|          | 230±3ms              | 231±6ms             |    1.01 | merge_concat.Concatenate.time_concatenate(True)                                             |
|          | 2.21±0G              | 2.21±0G             |    1    | merge_concat.Concatenate.tracemalloc_concatenate(False)                                     |
|          | 71.7±2M              | 71.9±1M             |    1    | merge_concat.Concatenate.tracemalloc_concatenate(True)                                      |
|          | 20.5±1ms             | 22.3±0.8ms          |    1.09 | merge_concat.Merge.time_merge                                                               |
|          | 126±0.03M            | 126±0.03M           |    1    | merge_concat.Merge.tracemalloc_merge                                                        |
|          | 3.35±0.04ms          | 3.43±0.07ms         |    1.02 | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(50)              |
|          | 76.8±1ms             | 76.0±0.6ms          |    0.99 | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(500)             |
|          | 769±3k               | 767±1k              |    1    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(50)       |
|          | 60.2±0M              | 60.2±0M             |    1    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(500)      |
|          | 10.1±0.2ms           | 10.4±0.2ms          |    1.04 | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(50)           |
|          | 12.6±0.4ms           | 13.4±0.5ms          |    1.06 | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(500)          |
|          | 24.6±0M              | 25±0M               |    1.02 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(500)   |
|          | 56.2±2ms             | 59.2±1ms            |    1.05 | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(50)         |
|          | 254±9ms              | 252±3ms             |    0.99 | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(500)        |
|          | 1.53±0.02M           | 1.51±0.03M          |    0.98 | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(50)  |
|          | 96.6±0.03M           | 96.5±0.04M          |    1    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(500) |
|          | 41.1±2ms             | 41.8±6ms            |    1.02 | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(50)                             |
|          | 229±20ms             | 228±4ms             |    0.99 | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(500)                            |
|          | 1.43±0.05M           | 1.41±0.03M          |    0.99 | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(50)                      |
|          | 96.4±0.04M           | 96.5±0.04M          |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(500)                     |
|          | 2.175361             | 2.175361            |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(50)                  |
|          | 216.01536099999998   | 216.01536099999998  |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(500)                 |
|          | 3.41±0.1ms           | 3.41±0.04ms         |    1    | plot.AuxSort.time_aux_sort                                                                  |
|          | 35.8±0.8ms           | 36.7±0.5ms          |    1.03 | regridding.CurvilinearRegridding.time_regrid_pic                                            |
|          | 136±3M               | 136±3M              |    1    | regridding.CurvilinearRegridding.tracemalloc_regrid_pic                                     |
|          | 52.2±4ms             | 50.4±4ms            |    0.97 | regridding.HorizontalChunkedRegridding.time_regrid_area_w                                   |
|          | 40.7±0.3ms           | 40.4±0.8ms          |    0.99 | regridding.HorizontalChunkedRegridding.time_regrid_area_w_new_grid                          |
|          | 107±0.1M             | 107±0.1M            |    1    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w                            |
|          | 147±0.03M            | 147±0.03M           |    1    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w_new_grid                   |
|          | 3.10±0.05ms          | 3.06±0.04ms         |    0.99 | save.NetcdfSave.time_netcdf_save_cube(50, False)                                            |
|          | 43.3±0.6ms           | 41.9±0.8ms          |    0.97 | save.NetcdfSave.time_netcdf_save_cube(50, True)                                             |
|          | 112±1ms              | 115±0.4ms           |    1.02 | save.NetcdfSave.time_netcdf_save_cube(600, False)                                           |
|          | 293±4ms              | 289±5ms             |    0.99 | save.NetcdfSave.time_netcdf_save_cube(600, True)                                            |
|          | 801±30ns             | 831±60ns            |    1.04 | save.NetcdfSave.time_netcdf_save_mesh(50, False)                                            |
|          | 32.8±0.8ms           | 32.4±0.9ms          |    0.99 | save.NetcdfSave.time_netcdf_save_mesh(50, True)                                             |
|          | 2.43±0.2μs           | 2.75±0.4μs          |    1.13 | save.NetcdfSave.time_netcdf_save_mesh(600, False)                                           |
|          | 185±3ms              | 179±2ms             |    0.97 | save.NetcdfSave.time_netcdf_save_mesh(600, True)                                            |
|          | 34±1k                | 33.8±1k             |    0.99 | save.NetcdfSave.tracemalloc_netcdf_save(50, False)                                          |
|          | 1.75±0.1M            | 1.9±0.2M            |    1.09 | save.NetcdfSave.tracemalloc_netcdf_save(50, True)                                           |
|          | 34.2±1k              | 34±1k               |    1    | save.NetcdfSave.tracemalloc_netcdf_save(600, False)                                         |
|          | 225±20M              | 225±10M             |    1    | save.NetcdfSave.tracemalloc_netcdf_save(600, True)                                          |
|          | 23.9±0.3ms           | 24.2±0.6ms          |    1.01 | stats.PearsonR.time_lazy                                                                    |
|          | 5.43±0.07ms          | 5.64±0.3ms          |    1.04 | stats.PearsonR.time_real                                                                    |
|          | 29.4±0.8M            | 29.4±0.8M           |    1    | stats.PearsonR.tracemalloc_lazy                                                             |
|          | 18.3±0M              | 18.3±0M             |    1    | stats.PearsonR.tracemalloc_real                                                             |
|          | 14.5±0.3ms           | 13.8±0.2ms          |    0.95 | trajectory.TrajectoryInterpolation.time_trajectory_linear                                   |
|          | 46.9±0.9ms           | 45.8±1ms            |    0.98 | trajectory.TrajectoryInterpolation.time_trajectory_nearest                                  |
|          | 17.6±0.01M           | 17.6±0.01M          |    1    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_linear                            |
|          | 7.74±0.01M           | 7.74±0.01M          |    1    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_nearest                           |

Benchmarks that have got worse:

| Change   | Before [426d7189]    | After [36f1d281]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 820±2k               | 1.23±0M             |    1.51 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |

Generated by GHA run 39958597711

@github-actions

Copy link
Copy Markdown
Contributor

⏱️ Performance Benchmark Report: 3e6c89d

Performance shifts
| Change   | Before [9fde70bd]    | After [3e6c89d1]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 820±2k               | 1.23±0M             |     1.5 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |
Full benchmark results

Benchmarks that have stayed the same:

| Change   | Before [9fde70bd]    | After [3e6c89d1]    | Ratio   | Benchmark (Parameter)                                                                       |
|----------|----------------------|---------------------|---------|---------------------------------------------------------------------------------------------|
|          | 18.8±1ms             | 18.5±0.3ms          | 0.98    | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(False)                              |
|          | 49.4±0.8ms           | 49.6±0.7ms          | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(True)                               |
|          | 31.0±1ms             | 30.6±0.4ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(False)                    |
|          | 157±2ms              | 156±3ms             | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(True)                     |
|          | 20.4±2ms             | 20.1±0.2ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(False)                              |
|          | 29.1±0.6ms           | 30.2±2ms            | 1.04    | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(True)                               |
|          | 20.6±2ms             | 20.3±0.2ms          | 0.98    | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(False)                              |
|          | 29.1±0.6ms           | 29.9±0.8ms          | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(True)                               |
|          | 18.5±2ms             | 18.4±0.2ms          | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_MAX(False)                                |
|          | 40.5±0.9ms           | 42.0±0.4ms          | 1.04    | aggregate_collapse.Aggregation.time_aggregated_by_MAX(True)                                 |
|          | 124±0.8ms            | 127±1ms             | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(False)                            |
|          | 122±1ms              | 126±1ms             | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(True)                             |
|          | 18.9±1ms             | 19.4±0.4ms          | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(False)                               |
|          | 44.1±0.8ms           | 45.6±0.8ms          | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(True)                                |
|          | 20.5±1ms             | 20.9±0.3ms          | 1.02    | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(False)                             |
|          | 52.3±1ms             | 54.2±1ms            | 1.04    | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(True)                              |
|          | 18.2±2ms             | 18.4±0.2ms          | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_MIN(False)                                |
|          | 41.0±0.7ms           | 42.3±0.6ms          | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_MIN(True)                                 |
|          | 932±7ms              | 935±10ms            | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(False)                               |
|          | 891±9ms              | 885±6ms             | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(True)                                |
|          | 153±4ms              | 154±1ms             | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(False)                         |
|          | 290±4ms              | 299±4ms             | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(True)                          |
|          | 19.1±1ms             | 19.5±0.3ms          | 1.02    | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(False)                         |
|          | 27.9±1ms             | 28.5±1ms            | 1.02    | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(True)                          |
|          | 19.6±2ms             | 19.9±0.6ms          | 1.02    | aggregate_collapse.Aggregation.time_aggregated_by_RMS(False)                                |
|          | 54.1±0.6ms           | 55.7±0.9ms          | 1.03    | aggregate_collapse.Aggregation.time_aggregated_by_RMS(True)                                 |
|          | 20.2±1ms             | 20.3±0.2ms          | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(False)                            |
|          | 57.9±0.9ms           | 59.2±0.6ms          | 1.02    | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(True)                             |
|          | 20.1±2ms             | 20.1±0.2ms          | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(False)                           |
|          | 55.0±3ms             | 55.9±0.7ms          | 1.02    | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(True)                            |
|          | 6.60±0.03ms          | 6.61±0.2ms          | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(False)                               |
|          | 20.5±0.3ms           | 20.9±0.4ms          | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(True)                                |
|          | 16.9±0.2ms           | 17.2±0.2ms          | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(False)                     |
|          | 115±2ms              | 120±2ms             | 1.04    | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(True)                      |
|          | 6.92±0.04ms          | 7.01±0.09ms         | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(False)                               |
|          | 18.6±0.4ms           | 18.6±0.2ms          | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(True)                                |
|          | 6.96±0.07ms          | 7.07±0.08ms         | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(False)                               |
|          | 19.0±0.6ms           | 18.6±0.2ms          | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(True)                                |
|          | 6.47±0.07ms          | 6.51±0.09ms         | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_MAX(False)                                 |
|          | 18.9±0.3ms           | 19.3±0.4ms          | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_MAX(True)                                  |
|          | 23.3±1ms             | 23.6±0.3ms          | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(False)                             |
|          | 33.6±1ms             | 33.8±0.4ms          | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(True)                              |
|          | 6.75±0.07ms          | 6.81±0.09ms         | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(False)                                |
|          | 18.9±0.3ms           | 19.7±0.2ms          | 1.04    | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(True)                                 |
|          | 7.54±0.06ms          | 7.62±0.1ms          | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(False)                              |
|          | 21.1±0.5ms           | 21.7±0.3ms          | 1.03    | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(True)                               |
|          | 6.48±0.1ms           | 6.49±0.08ms         | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_MIN(False)                                 |
|          | 19.1±0.6ms           | 19.5±0.5ms          | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_MIN(True)                                  |
|          | 500±6ms              | 507±0.5ms           | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(False)                                |
|          | 511±8ms              | 507±4ms             | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(True)                                 |
|          | 34.5±0.5ms           | 34.2±0.2ms          | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(False)                          |
|          | 123±6ms              | 122±3ms             | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(True)                           |
|          | 6.79±0.07ms          | 6.91±0.04ms         | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(False)                          |
|          | 18.5±0.6ms           | 19.0±0.3ms          | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(True)                           |
|          | 6.84±0.2ms           | 6.87±0.06ms         | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_RMS(False)                                 |
|          | 20.7±0.8ms           | 21.2±0.6ms          | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_RMS(True)                                  |
|          | 6.99±0.2ms           | 7.18±0.05ms         | 1.03    | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(False)                             |
|          | 20.7±1ms             | 21.0±0.4ms          | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(True)                              |
|          | 7.15±0.1ms           | 7.14±0.02ms         | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(False)                            |
|          | 20.0±0.8ms           | 20.5±0.4ms          | 1.02    | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(True)                             |
|          | 19.9±2ms             | 19.7±0.2ms          | 0.99    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(False)                     |
|          | 76.5±2ms             | 79.3±0.4ms          | 1.04    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(True)                      |
|          | 20.0±2ms             | 19.9±0.3ms          | 0.99    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(False)                      |
|          | 87.4±3ms             | 90.6±0.6ms          | 1.04    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(True)                       |
|          | 19.1±1ms             | 18.9±0.3ms          | 0.99    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(False)                      |
|          | 51.4±3ms             | 51.0±0.6ms          | 0.99    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(True)                       |
|          | 7.14±0.1ms           | 7.27±0.06ms         | 1.02    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(False)                      |
|          | 25.5±1ms             | 25.7±0.5ms          | 1.01    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(True)                       |
|          | 7.06±0.2ms           | 7.14±0.05ms         | 1.01    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(False)                       |
|          | 26.9±0.6ms           | 27.0±0.4ms          | 1.01    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(True)                        |
|          | 6.72±0.07ms          | 6.82±0.07ms         | 1.01    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(False)                       |
|          | 21.2±0.8ms           | 21.1±0.5ms          | 1.00    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(True)                        |
|          | 149±1ms              | 149±0.9ms           | 1.00    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(False)               |
|          | 222±3ms              | 218±2ms             | 0.98    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(True)                |
|          | 1.02±0.02ms          | 1.05±0.01ms         | 1.03    | cube.CubeCreation.time_create(False, 'construct')                                           |
|          | 409±5μs              | 415±20μs            | 1.01    | cube.CubeCreation.time_create(False, 'instantiate')                                         |
|          | 1.63±0.02ms          | 1.62±0.02ms         | 1.00    | cube.CubeCreation.time_create(True, 'construct')                                            |
|          | 1.17±0.02ms          | 1.15±0.02ms         | 0.99    | cube.CubeCreation.time_create(True, 'instantiate')                                          |
|          | 106±4ms              | 104±1ms             | 0.98    | cube.CubeEquality.time_equality(False, False, 'all_equal')                                  |
|          | 26.2±0.6ms           | 27.1±1ms            | 1.03    | cube.CubeEquality.time_equality(False, False, 'coord_inequality')                           |
|          | 122±5ms              | 123±0.6ms           | 1.00    | cube.CubeEquality.time_equality(False, False, 'data_inequality')                            |
|          | 65.9±4μs             | 68.0±3μs            | 1.03    | cube.CubeEquality.time_equality(False, False, 'metadata_inequality')                        |
|          | 106±2ms              | 108±0.9ms           | 1.01    | cube.CubeEquality.time_equality(False, True, 'all_equal')                                   |
|          | 28.8±0.9ms           | 28.8±0.6ms          | 1.00    | cube.CubeEquality.time_equality(False, True, 'coord_inequality')                            |
|          | 124±3ms              | 125±2ms             | 1.01    | cube.CubeEquality.time_equality(False, True, 'data_inequality')                             |
|          | 69.4±4μs             | 71.8±6μs            | 1.03    | cube.CubeEquality.time_equality(False, True, 'metadata_inequality')                         |
|          | 183±9ms              | 180±2ms             | 0.98    | cube.CubeEquality.time_equality(True, False, 'all_equal')                                   |
|          | 66.2±3ms             | 65.8±2ms            | 0.99    | cube.CubeEquality.time_equality(True, False, 'coord_inequality')                            |
|          | 220±10ms             | 218±4ms             | 0.99    | cube.CubeEquality.time_equality(True, False, 'data_inequality')                             |
|          | 84.8±1μs             | 85.5±3μs            | 1.01    | cube.CubeEquality.time_equality(True, False, 'metadata_inequality')                         |
|          | 248±10ms             | 252±3ms             | 1.02    | cube.CubeEquality.time_equality(True, True, 'all_equal')                                    |
|          | 133±7ms              | 137±3ms             | 1.03    | cube.CubeEquality.time_equality(True, True, 'coord_inequality')                             |
|          | 282±10ms             | 290±4ms             | 1.03    | cube.CubeEquality.time_equality(True, True, 'data_inequality')                              |
|          | 328±6μs              | 329±7μs             | 1.00    | cube.CubeEquality.time_equality(True, True, 'metadata_inequality')                          |
|          | 806±10μs             | 787±10μs            | 0.98    | import_iris.Iris.time__concatenate                                                          |
|          | 148±0.7μs            | 150±5μs             | 1.01    | import_iris.Iris.time__constraints                                                          |
|          | 79.7±1μs             | 80.3±1μs            | 1.01    | import_iris.Iris.time__data_manager                                                         |
|          | 68.2±2μs             | 66.6±0.4μs          | 0.98    | import_iris.Iris.time__deprecation                                                          |
|          | 123±2μs              | 123±2μs             | 1.00    | import_iris.Iris.time__lazy_data                                                            |
|          | 867±20μs             | 876±20μs            | 1.01    | import_iris.Iris.time__merge                                                                |
|          | 46.7±0.9μs           | 47.5±0.4μs          | 1.02    | import_iris.Iris.time__representation                                                       |
|          | 631±9μs              | 621±9μs             | 0.98    | import_iris.Iris.time_analysis                                                              |
|          | 96.4±1μs             | 94.7±0.7μs          | 0.98    | import_iris.Iris.time_analysis__area_weighted                                               |
|          | 85.4±1μs             | 84.5±0.8μs          | 0.99    | import_iris.Iris.time_analysis__grid_angles                                                 |
|          | 222±3μs              | 219±2μs             | 0.98    | import_iris.Iris.time_analysis__interpolation                                               |
|          | 158±4μs              | 154±2μs             | 0.98    | import_iris.Iris.time_analysis__regrid                                                      |
|          | 74.7±1μs             | 74.2±0.9μs          | 0.99    | import_iris.Iris.time_analysis__scipy_interpolate                                           |
|          | 99.9±1μs             | 97.9±2μs            | 0.98    | import_iris.Iris.time_analysis_calculus                                                     |
|          | 286±5μs              | 287±4μs             | 1.01    | import_iris.Iris.time_analysis_cartography                                                  |
|          | 60.8±0.5μs           | 60.5±0.6μs          | 1.00    | import_iris.Iris.time_analysis_geomerty                                                     |
|          | 165±3μs              | 164±2μs             | 1.00    | import_iris.Iris.time_analysis_maths                                                        |
|          | 62.5±1μs             | 62.1±0.7μs          | 0.99    | import_iris.Iris.time_analysis_stats                                                        |
|          | 131±2μs              | 132±2μs             | 1.00    | import_iris.Iris.time_analysis_trajectory                                                   |
|          | 290±2μs              | 283±2μs             | 0.98    | import_iris.Iris.time_aux_factory                                                           |
|          | 51.6±0.8μs           | 52.7±0.9μs          | 1.02    | import_iris.Iris.time_common                                                                |
|          | 123±1μs              | 123±1μs             | 1.00    | import_iris.Iris.time_common_lenient                                                        |
|          | 1.39±0.01ms          | 1.42±0.02ms         | 1.02    | import_iris.Iris.time_common_metadata                                                       |
|          | 141±2μs              | 140±1μs             | 0.99    | import_iris.Iris.time_common_mixin                                                          |
|          | 1.14±0.01ms          | 1.14±0.01ms         | 1.00    | import_iris.Iris.time_common_resolve                                                        |
|          | 197±2μs              | 199±3μs             | 1.01    | import_iris.Iris.time_config                                                                |
|          | 90.5±1μs             | 92.1±1μs            | 1.02    | import_iris.Iris.time_coord_categorisation                                                  |
|          | 347±5μs              | 343±6μs             | 0.99    | import_iris.Iris.time_coord_systems                                                         |
|          | 848±9μs              | 846±9μs             | 1.00    | import_iris.Iris.time_coords                                                                |
|          | 796±10μs             | 780±10μs            | 0.98    | import_iris.Iris.time_cube                                                                  |
|          | 223±5μs              | 211±2μs             | 0.95    | import_iris.Iris.time_exceptions                                                            |
|          | 47.4±0.6μs           | 47.8±0.3μs          | 1.01    | import_iris.Iris.time_experimental                                                          |
|          | 143±2μs              | 144±1μs             | 1.01    | import_iris.Iris.time_fileformats                                                           |
|          | 236±2μs              | 241±3μs             | 1.02    | import_iris.Iris.time_fileformats__ff                                                       |
|          | 2.60±0.03ms          | 2.72±0.02ms         | 1.05    | import_iris.Iris.time_fileformats__ff_cross_references                                      |
|          | 51.5±0.5μs           | 51.4±0.5μs          | 1.00    | import_iris.Iris.time_fileformats__pp_lbproc_pairs                                          |
|          | 74.5±0.7μs           | 74.6±0.7μs          | 1.00    | import_iris.Iris.time_fileformats_abf                                                       |
|          | 53.7±0.7μs           | 54.4±0.3μs          | 1.01    | import_iris.Iris.time_fileformats_cf                                                        |
|          | 3.59±0.04ms          | 3.67±0.04ms         | 1.02    | import_iris.Iris.time_fileformats_dot                                                       |
|          | 47.8±0.6μs           | 48.5±0.8μs          | 1.01    | import_iris.Iris.time_fileformats_name                                                      |
|          | 211±4μs              | 211±2μs             | 1.00    | import_iris.Iris.time_fileformats_name_loaders                                              |
|          | 83.7±1μs             | 83.8±1μs            | 1.00    | import_iris.Iris.time_fileformats_netcdf                                                    |
|          | 108±1μs              | 109±1μs             | 1.01    | import_iris.Iris.time_fileformats_nimrod                                                    |
|          | 284±3μs              | 288±2μs             | 1.01    | import_iris.Iris.time_fileformats_nimrod_load_rules                                         |
|          | 800±20μs             | 800±7μs             | 1.00    | import_iris.Iris.time_fileformats_pp                                                        |
|          | 127±3μs              | 133±4μs             | 1.04    | import_iris.Iris.time_fileformats_pp_load_rules                                             |
|          | 88.3±1μs             | 88.9±0.8μs          | 1.01    | import_iris.Iris.time_fileformats_pp_save_rules                                             |
|          | 496±6μs              | 499±6μs             | 1.01    | import_iris.Iris.time_fileformats_rules                                                     |
|          | 188±3μs              | 191±2μs             | 1.01    | import_iris.Iris.time_fileformats_structured_array_identification                           |
|          | 50.9±0.8μs           | 51.3±0.3μs          | 1.01    | import_iris.Iris.time_fileformats_um                                                        |
|          | 112±10μs             | 110±1μs             | 0.98    | import_iris.Iris.time_fileformats_um__fast_load                                             |
|          | 90.3±1μs             | 91.3±1μs            | 1.01    | import_iris.Iris.time_fileformats_um__fast_load_structured_fields                           |
|          | 48.4±0.3μs           | 48.6±0.6μs          | 1.00    | import_iris.Iris.time_fileformats_um__ff_replacement                                        |
|          | 52.5±0.6μs           | 51.9±0.4μs          | 0.99    | import_iris.Iris.time_fileformats_um__optimal_array_structuring                             |
|          | 952±20μs             | 998±8μs             | 1.05    | import_iris.Iris.time_fileformats_um_cf_map                                                 |
|          | 106±1μs              | 108±0.9μs           | 1.02    | import_iris.Iris.time_io                                                                    |
|          | 149±3μs              | 147±2μs             | 0.99    | import_iris.Iris.time_io_format_picker                                                      |
|          | 141±1μs              | 142±2μs             | 1.01    | import_iris.Iris.time_iris                                                                  |
|          | 85.7±1μs             | 85.7±1μs            | 1.00    | import_iris.Iris.time_iterate                                                               |
|          | 5.76±0.08ms          | 5.72±0.05ms         | 0.99    | import_iris.Iris.time_palette                                                               |
|          | 1.82±0.07ms          | 1.80±0.05ms         | 0.99    | import_iris.Iris.time_plot                                                                  |
|          | 246±80μs             | 227±20μs            | 0.92    | import_iris.Iris.time_quickplot                                                             |
|          | 2.44±0.03ms          | 2.36±0.01ms         | 0.96    | import_iris.Iris.time_std_names                                                             |
|          | 1.36±0.01ms          | 1.39±0.01ms         | 1.02    | import_iris.Iris.time_symbols                                                               |
|          | 102±10ms             | 111±10ms            | 1.09    | import_iris.Iris.time_tests                                                                 |
|          | 164±2μs              | 166±0.6μs           | 1.01    | import_iris.Iris.time_third_party_cartopy                                                   |
|          | 5.10±0.06ms          | 5.09±0.06ms         | 1.00    | import_iris.Iris.time_third_party_cf_units                                                  |
|          | 76.2±1μs             | 76.4±0.5μs          | 1.00    | import_iris.Iris.time_third_party_cftime                                                    |
|          | 2.33±0.02ms          | 2.34±0.02ms         | 1.00    | import_iris.Iris.time_third_party_matplotlib                                                |
|          | 507±5μs              | 497±7μs             | 0.98    | import_iris.Iris.time_third_party_numpy                                                     |
|          | 112±1μs              | 113±1μs             | 1.01    | import_iris.Iris.time_third_party_scipy                                                     |
|          | 64.8±1μs             | 65.9±0.5μs          | 1.02    | import_iris.Iris.time_time                                                                  |
|          | 912±10μs             | 879±8μs             | 0.96    | import_iris.Iris.time_util                                                                  |
|          | 58.6±0.8μs           | 60.7±2μs            | 1.04    | iterate.IZip.time_izip                                                                      |
|          | 8.61±0.2ms           | 8.82±0.08ms         | 1.02    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'FF')                                  |
|          | 13.3±0.3ms           | 13.2±0.2ms          | 0.99    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'NetCDF')                              |
|          | 8.62±0.07ms          | 8.63±0.03ms         | 1.00    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'PP')                                  |
|          | 8.62±0.06ms          | 8.69±0.06ms         | 1.01    | load.LoadAndRealise.time_load((1280, 960, 5), True, 'FF')                                   |
|          | 10.4±0.1ms           | 10.4±0.1ms          | 1.00    | load.LoadAndRealise.time_load((1280, 960, 5), True, 'NetCDF')                               |
|          | 8.66±0.07ms          | 8.75±0.08ms         | 1.01    | load.LoadAndRealise.time_load((1280, 960, 5), True, 'PP')                                   |
|          | 1.27±0.01s           | 1.29±0.01s          | 1.01    | load.LoadAndRealise.time_load((2, 2, 1000), False, 'FF')                                    |
|          | 9.93±0.2ms           | 9.96±0.1ms          | 1.00    | load.LoadAndRealise.time_load((2, 2, 1000), False, 'NetCDF')                                |
|          | 1.26±0.01s           | 1.27±0s             | 1.00    | load.LoadAndRealise.time_load((2, 2, 1000), False, 'PP')                                    |
|          | 1.28±0.01s           | 1.29±0s             | 1.00    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'FF')                                     |
|          | 9.85±0.09ms          | 10.0±0.1ms          | 1.02    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'NetCDF')                                 |
|          | 1.27±0.01s           | 1.28±0s             | 1.01    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'PP')                                     |
|          | 4.68±0.04ms          | 4.71±0.04ms         | 1.01    | load.LoadAndRealise.time_load((50, 50, 2), False, 'FF')                                     |
|          | 9.48±0.3ms           | 9.36±0.09ms         | 0.99    | load.LoadAndRealise.time_load((50, 50, 2), False, 'NetCDF')                                 |
|          | 4.53±0.04ms          | 4.54±0.04ms         | 1.00    | load.LoadAndRealise.time_load((50, 50, 2), False, 'PP')                                     |
|          | 4.69±0.08ms          | 4.72±0.02ms         | 1.01    | load.LoadAndRealise.time_load((50, 50, 2), True, 'FF')                                      |
|          | 9.36±0.3ms           | 9.38±0.1ms          | 1.00    | load.LoadAndRealise.time_load((50, 50, 2), True, 'NetCDF')                                  |
|          | 4.53±0.09ms          | 4.53±0.02ms         | 1.00    | load.LoadAndRealise.time_load((50, 50, 2), True, 'PP')                                      |
|          | 32.0±2ms             | 30.3±0.7ms          | 0.95    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'FF')                               |
|          | 31.1±1ms             | 30.8±0.4ms          | 0.99    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'NetCDF')                           |
|          | 15.1±2ms             | 16.5±2ms            | 1.09    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'PP')                               |
|          | 33.2±1ms             | 33.3±1ms            | 1.00    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'FF')                                |
|          | 52.6±2ms             | 53.5±0.9ms          | 1.02    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'NetCDF')                            |
|          | 33.4±2ms             | 32.9±2ms            | 0.99    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'PP')                                |
|          | 554±20ms             | 568±3ms             | 1.03    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'FF')                                 |
|          | 3.29±0.2ms           | 3.15±0.1ms          | 0.96    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'NetCDF')                             |
|          | 564±20ms             | 569±2ms             | 1.01    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'PP')                                 |
|          | 576±10ms             | 584±5ms             | 1.01    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'FF')                                  |
|          | 3.22±0.2ms           | 3.28±0.08ms         | 1.02    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'NetCDF')                              |
|          | 568±20ms             | 577±4ms             | 1.02    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'PP')                                  |
|          | 2.20±0.1ms           | 2.10±0.07ms         | 0.95    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'FF')                                  |
|          | 3.28±0.2ms           | 3.21±0.1ms          | 0.98    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'NetCDF')                              |
|          | 2.26±0.09ms          | 2.18±0.1ms          | 0.96    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'PP')                                  |
|          | 2.13±0.09ms          | 2.14±0.06ms         | 1.01    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'FF')                                   |
|          | 3.22±0.1ms           | 3.22±0.2ms          | 1.00    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'NetCDF')                               |
|          | 2.25±0.1ms           | 2.18±0.06ms         | 0.97    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'PP')                                   |
|          | 309±1ms              | 308±2ms             | 1.00    | load.ManyCubes.time_many_cube_load                                                          |
|          | 65.5±0.5ms           | 67.0±0.9ms          | 1.02    | load.ManyVars.time_many_var_load                                                            |
|          | 8.91±0.1ms           | 8.94±0.09ms         | 1.00    | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'FF')                            |
|          | 8.85±0.1ms           | 8.83±0.06ms         | 1.00    | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'PP')                            |
|          | 1.29±0.01s           | 1.29±0.01s          | 1.01    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'FF')                              |
|          | 1.28±0.01s           | 1.29±0s             | 1.01    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'PP')                              |
|          | 4.74±0.07ms          | 4.76±0.06ms         | 1.00    | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'FF')                                 |
|          | 4.58±0.04ms          | 4.63±0.04ms         | 1.01    | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'PP')                                 |
|          | 8.04±0.1ms           | 8.08±0.04ms         | 1.00    | load.StructuredFF.time_structured_load((1280, 960, 5), False)                               |
|          | 4.89±0.02ms          | 4.97±0.03ms         | 1.02    | load.StructuredFF.time_structured_load((1280, 960, 5), True)                                |
|          | 1.27±0.01s           | 1.28±0.01s          | 1.01    | load.StructuredFF.time_structured_load((2, 2, 1000), False)                                 |
|          | 351±1ms              | 352±0.8ms           | 1.00    | load.StructuredFF.time_structured_load((2, 2, 1000), True)                                  |
|          | 4.04±0.04ms          | 3.99±0.01ms         | 0.99    | load.StructuredFF.time_structured_load((2, 2, 2), False)                                    |
|          | 3.81±0.03ms          | 3.80±0.05ms         | 1.00    | load.StructuredFF.time_structured_load((2, 2, 2), True)                                     |
|          | 132±0.3ms            | 132±0.3ms           | 1.00    | load.TimeConstraint.time_time_constraint(20, 'FF')                                          |
|          | 12.1±0.1ms           | 12.1±0.1ms          | 1.00    | load.TimeConstraint.time_time_constraint(20, 'NetCDF')                                      |
|          | 130±0.5ms            | 131±1ms             | 1.01    | load.TimeConstraint.time_time_constraint(20, 'PP')                                          |
|          | 26.8±0.3ms           | 26.6±0.1ms          | 0.99    | load.TimeConstraint.time_time_constraint(3, 'FF')                                           |
|          | 11.8±0.1ms           | 11.6±0.09ms         | 0.98    | load.TimeConstraint.time_time_constraint(3, 'NetCDF')                                       |
|          | 26.2±0.1ms           | 26.4±0.2ms          | 1.01    | load.TimeConstraint.time_time_constraint(3, 'PP')                                           |
|          | 15.6±0.8ms           | 15.7±0.5ms          | 1.01    | load.ugrid.BasicLoading.time_load_file(1)                                                   |
|          | 45.0±2ms             | 46.5±0.7ms          | 1.03    | load.ugrid.BasicLoading.time_load_file(200000)                                              |
|          | 7.02±0.5ms           | 6.95±0.3ms          | 0.99    | load.ugrid.BasicLoading.time_load_mesh(1)                                                   |
|          | 14.8±1ms             | 15.5±0.7ms          | 1.04    | load.ugrid.BasicLoading.time_load_mesh(200000)                                              |
|          | 15.4±1ms             | 15.7±0.4ms          | 1.02    | load.ugrid.BasicLoadingTime.time_load_file(1)                                               |
|          | 16.2±0.9ms           | 16.7±0.6ms          | 1.03    | load.ugrid.BasicLoadingTime.time_load_file(200000)                                          |
|          | 7.06±0.5ms           | 7.15±0.5ms          | 1.01    | load.ugrid.BasicLoadingTime.time_load_mesh(1)                                               |
|          | 9.90±1ms             | 10.3±0.5ms          | 1.04    | load.ugrid.BasicLoadingTime.time_load_mesh(200000)                                          |
|          | 17.3±0.9ms           | 17.6±0.5ms          | 1.02    | load.ugrid.Callback.time_load_file_callback(1)                                              |
|          | 56.9±3ms             | 57.3±1ms            | 1.01    | load.ugrid.Callback.time_load_file_callback(200000)                                         |
|          | 17.4±1ms             | 17.8±0.4ms          | 1.02    | load.ugrid.CallbackTime.time_load_file_callback(1)                                          |
|          | 19.3±2ms             | 19.9±0.7ms          | 1.03    | load.ugrid.CallbackTime.time_load_file_callback(200000)                                     |
|          | 3.32±0.3ms           | 3.60±0.09ms         | 1.08    | load.ugrid.DataRealisation.time_realise_data(10000)                                         |
|          | 5.49±0.5ms           | 5.71±0.2ms          | 1.04    | load.ugrid.DataRealisation.time_realise_data(200000)                                        |
|          | 40.9±6ms             | 46.5±3ms            | 1.14    | load.ugrid.DataRealisationTime.time_realise_data(10000)                                     |
|          | 1.01±0.03s           | 1.05±0.02s          | 1.04    | load.ugrid.DataRealisationTime.time_realise_data(200000)                                    |
|          | 1.25±0.04s           | 1.27±0.02s          | 1.01    | merge_concat.Concatenate.time_concatenate(False)                                            |
|          | 397±9ms              | 403±3ms             | 1.01    | merge_concat.Concatenate.time_concatenate(True)                                             |
|          | 2.21±0G              | 2.21±0G             | 1.00    | merge_concat.Concatenate.tracemalloc_concatenate(False)                                     |
|          | 71.7±2M              | 71.7±2M             | 1.00    | merge_concat.Concatenate.tracemalloc_concatenate(True)                                      |
|          | 50.2±2ms             | 48.2±2ms            | 0.96    | merge_concat.Merge.time_merge                                                               |
|          | 126±0.03M            | 126±0.03M           | 1.00    | merge_concat.Merge.tracemalloc_merge                                                        |
|          | 5.05±0.03ms          | 5.07±0.07ms         | 1.00    | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(50)              |
|          | 126±0.9ms            | 128±0.8ms           | 1.01    | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(500)             |
|          | 769±3k               | 768±1k              | 1.00    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(50)       |
|          | 60.2±0M              | 60.2±0M             | 1.00    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(500)      |
|          | 16.7±0.1ms           | 17.0±0.2ms          | 1.02    | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(50)           |
|          | 22.3±0.3ms           | 22.9±0.7ms          | 1.03    | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(500)          |
|          | 24.6±0M              | 25±0M               | 1.02    | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(500)   |
|          | 99.3±1ms             | 98.8±0.7ms          | 1.00    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(50)         |
|          | 409±4ms              | 409±5ms             | 1.00    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(500)        |
|          | 1.49±0.03M           | 1.53±0.03M          | 1.03    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(50)  |
|          | 96.6±0.01M           | 96.5±0.03M          | 1.00    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(500) |
|          | 68.3±1ms             | 69.1±0.6ms          | 1.01    | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(50)                             |
|          | 375±4ms              | 371±3ms             | 0.99    | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(500)                            |
|          | 1.39±0.04M           | 1.41±0.03M          | 1.01    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(50)                      |
|          | 96.5±0.04M           | 96.5±0.04M          | 1.00    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(500)                     |
|          | 2.175361             | 2.175361            | 1.00    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(50)                  |
|          | 216.01536099999998   | 216.01536099999998  | 1.00    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(500)                 |
|          | 5.69±0.1ms           | 5.63±0.04ms         | 0.99    | plot.AuxSort.time_aux_sort                                                                  |
|          | 77.5±3ms             | 76.6±0.7ms          | 0.99    | regridding.CurvilinearRegridding.time_regrid_pic                                            |
|          | 136±3M               | 136±3M              | 1.00    | regridding.CurvilinearRegridding.tracemalloc_regrid_pic                                     |
|          | 114±5ms              | 113±5ms             | 0.99    | regridding.HorizontalChunkedRegridding.time_regrid_area_w                                   |
|          | 80.8±3ms             | 81.3±0.9ms          | 1.01    | regridding.HorizontalChunkedRegridding.time_regrid_area_w_new_grid                          |
|          | 107±0.1M             | 107±0.1M            | 1.00    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w                            |
|          | 147±0.03M            | 147±0.03M           | 1.00    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w_new_grid                   |
|          | 4.94±0.3ms           | 4.95±0.04ms         | 1.00    | save.NetcdfSave.time_netcdf_save_cube(50, False)                                            |
|          | 77.0±4ms             | 77.1±0.6ms          | 1.00    | save.NetcdfSave.time_netcdf_save_cube(50, True)                                             |
|          | 101±4ms              | 103±5ms             | 1.02    | save.NetcdfSave.time_netcdf_save_cube(600, False)                                           |
|          | 410±20ms             | 395±3ms             | 0.97    | save.NetcdfSave.time_netcdf_save_cube(600, True)                                            |
|          | 1.21±0.3μs           | 1.39±0.06μs         | 1.15    | save.NetcdfSave.time_netcdf_save_mesh(50, False)                                            |
|          | 60.2±3ms             | 59.3±0.7ms          | 0.99    | save.NetcdfSave.time_netcdf_save_mesh(50, True)                                             |
|          | 2.29±0.1μs           | 2.82±0.2μs          | ~1.24   | save.NetcdfSave.time_netcdf_save_mesh(600, False)                                           |
|          | 322±7ms              | 319±4ms             | 0.99    | save.NetcdfSave.time_netcdf_save_mesh(600, True)                                            |
|          | 34.1±1k              | 33.8±1k             | 0.99    | save.NetcdfSave.tracemalloc_netcdf_save(50, False)                                          |
|          | 1.8±0.1M             | 1.8±0.2M            | 1.00    | save.NetcdfSave.tracemalloc_netcdf_save(50, True)                                           |
|          | 34.2±1k              | 33.8±1k             | 0.99    | save.NetcdfSave.tracemalloc_netcdf_save(600, False)                                         |
|          | 225±20M              | 191±20M             | 0.85    | save.NetcdfSave.tracemalloc_netcdf_save(600, True)                                          |
|          | 42.0±2ms             | 42.7±0.7ms          | 1.02    | stats.PearsonR.time_lazy                                                                    |
|          | 12.0±1ms             | 12.6±0.07ms         | 1.05    | stats.PearsonR.time_real                                                                    |
|          | 29.3±0.9M            | 29±0.6M             | 0.99    | stats.PearsonR.tracemalloc_lazy                                                             |
|          | 18.3±0M              | 18.3±0M             | 1.00    | stats.PearsonR.tracemalloc_real                                                             |
|          | 23.7±1ms             | 23.7±0.3ms          | 1.00    | trajectory.TrajectoryInterpolation.time_trajectory_linear                                   |
|          | 68.1±2ms             | 69.6±0.3ms          | 1.02    | trajectory.TrajectoryInterpolation.time_trajectory_nearest                                  |
|          | 17.6±0.01M           | 17.6±0.01M          | 1.00    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_linear                            |
|          | 7.74±0.01M           | 7.74±0.01M          | 1.00    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_nearest                           |

Benchmarks that have got worse:

| Change   | Before [9fde70bd]    | After [3e6c89d1]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 820±2k               | 1.23±0M             |     1.5 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |

Generated by GHA run 39955931750

@github-actions

Copy link
Copy Markdown
Contributor

⏱️ Performance Benchmark Report: 989c1ba

Performance shifts
| Change   | Before [426d7189]    | After [989c1ba2]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 821±2k               | 1.23±0M             |     1.5 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |
Full benchmark results

Benchmarks that have stayed the same:

| Change   | Before [426d7189]    | After [989c1ba2]    | Ratio   | Benchmark (Parameter)                                                                       |
|----------|----------------------|---------------------|---------|---------------------------------------------------------------------------------------------|
|          | 21.3±2ms             | 20.8±0.4ms          | 0.98    | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(False)                              |
|          | 52.2±1ms             | 52.5±0.7ms          | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(True)                               |
|          | 35.6±1ms             | 35.2±0.7ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(False)                    |
|          | 165±3ms              | 172±2ms             | 1.04    | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(True)                     |
|          | 23.2±1ms             | 23.2±0.5ms          | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(False)                              |
|          | 31.8±0.8ms           | 32.2±0.8ms          | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(True)                               |
|          | 23.0±1ms             | 23.0±0.6ms          | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(False)                              |
|          | 32.3±0.5ms           | 32.2±0.6ms          | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(True)                               |
|          | 21.7±2ms             | 20.7±0.6ms          | 0.95    | aggregate_collapse.Aggregation.time_aggregated_by_MAX(False)                                |
|          | 44.3±1ms             | 44.1±0.6ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_MAX(True)                                 |
|          | 134±1ms              | 134±2ms             | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(False)                            |
|          | 136±3ms              | 137±2ms             | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(True)                             |
|          | 22.4±1ms             | 21.8±0.7ms          | 0.97    | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(False)                               |
|          | 48.8±0.9ms           | 48.6±1ms            | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(True)                                |
|          | 23.3±2ms             | 23.0±0.3ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(False)                             |
|          | 58.0±1ms             | 58.5±0.4ms          | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(True)                              |
|          | 20.8±1ms             | 20.5±0.2ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_MIN(False)                                |
|          | 44.4±0.8ms           | 43.9±1ms            | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_MIN(True)                                 |
|          | 1.16±0.01s           | 1.17±0.02s          | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(False)                               |
|          | 1.07±0.01s           | 1.07±0.01s          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(True)                                |
|          | 219±2ms              | 225±5ms             | 1.02    | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(False)                         |
|          | 381±10ms             | 374±6ms             | 0.98    | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(True)                          |
|          | 22.0±1ms             | 21.7±0.7ms          | 0.98    | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(False)                         |
|          | 30.3±0.6ms           | 30.1±0.5ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(True)                          |
|          | 22.0±2ms             | 21.8±0.2ms          | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_RMS(False)                                |
|          | 58.0±1ms             | 58.5±0.7ms          | 1.01    | aggregate_collapse.Aggregation.time_aggregated_by_RMS(True)                                 |
|          | 23.2±1ms             | 23.2±0.6ms          | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(False)                            |
|          | 62.9±0.7ms           | 62.2±1ms            | 0.99    | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(True)                             |
|          | 23.2±2ms             | 22.5±0.3ms          | 0.97    | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(False)                           |
|          | 58.3±1ms             | 58.5±1ms            | 1.00    | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(True)                            |
|          | 7.89±0.1ms           | 7.68±0.06ms         | 0.97    | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(False)                               |
|          | 21.8±0.3ms           | 21.6±0.4ms          | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(True)                                |
|          | 20.0±0.2ms           | 19.5±0.2ms          | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(False)                     |
|          | 125±2ms              | 123±3ms             | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(True)                      |
|          | 8.30±0.08ms          | 8.00±0.1ms          | 0.96    | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(False)                               |
|          | 19.7±0.2ms           | 20.0±0.3ms          | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(True)                                |
|          | 8.08±0.03ms          | 8.08±0.05ms         | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(False)                               |
|          | 19.9±0.5ms           | 19.6±0.2ms          | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(True)                                |
|          | 7.81±0.05ms          | 7.57±0.03ms         | 0.97    | aggregate_collapse.Aggregation.time_collapsed_by_MAX(False)                                 |
|          | 20.0±0.5ms           | 20.1±0.1ms          | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_MAX(True)                                  |
|          | 25.8±2ms             | 25.4±0.2ms          | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(False)                             |
|          | 35.8±0.5ms           | 35.4±0.6ms          | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(True)                              |
|          | 7.92±0.09ms          | 7.81±0.05ms         | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(False)                                |
|          | 20.4±0.2ms           | 20.4±0.2ms          | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(True)                                 |
|          | 8.91±0.07ms          | 8.87±0.03ms         | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(False)                              |
|          | 22.4±0.2ms           | 22.5±0.3ms          | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(True)                               |
|          | 7.71±0.1ms           | 7.60±0.05ms         | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_MIN(False)                                 |
|          | 20.2±0.3ms           | 19.8±0.3ms          | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_MIN(True)                                  |
|          | 580±0.8ms            | 579±1ms             | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(False)                                |
|          | 576±2ms              | 576±1ms             | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(True)                                 |
|          | 46.2±0.6ms           | 46.4±0.3ms          | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(False)                          |
|          | 138±5ms              | 139±4ms             | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(True)                           |
|          | 7.91±0.05ms          | 7.89±0.07ms         | 1.00    | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(False)                          |
|          | 19.7±0.2ms           | 19.5±0.4ms          | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(True)                           |
|          | 8.07±0.05ms          | 7.92±0.07ms         | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_RMS(False)                                 |
|          | 22.1±0.5ms           | 21.7±0.3ms          | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_RMS(True)                                  |
|          | 8.20±0.06ms          | 8.25±0.1ms          | 1.01    | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(False)                             |
|          | 21.8±0.5ms           | 21.6±0.3ms          | 0.99    | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(True)                              |
|          | 8.37±0.06ms          | 8.19±0.1ms          | 0.98    | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(False)                            |
|          | 21.5±0.4ms           | 21.0±0.4ms          | 0.97    | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(True)                             |
|          | 22.7±1ms             | 22.2±0.3ms          | 0.98    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(False)                     |
|          | 82.8±0.8ms           | 82.0±2ms            | 0.99    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(True)                      |
|          | 22.7±1ms             | 22.2±0.6ms          | 0.98    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(False)                      |
|          | 94.9±0.9ms           | 96.2±0.9ms          | 1.01    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(True)                       |
|          | 21.7±1ms             | 21.0±0.3ms          | 0.97    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(False)                      |
|          | 54.2±0.8ms           | 54.7±0.8ms          | 1.01    | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(True)                       |
|          | 8.37±0.1ms           | 8.26±0.1ms          | 0.99    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(False)                      |
|          | 27.5±0.6ms           | 26.4±0.6ms          | 0.96    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(True)                       |
|          | 8.25±0.05ms          | 8.16±0.1ms          | 0.99    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(False)                       |
|          | 28.2±0.6ms           | 28.3±0.3ms          | 1.00    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(True)                        |
|          | 8.03±0.07ms          | 7.84±0.06ms         | 0.98    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(False)                       |
|          | 22.3±0.2ms           | 22.2±0.2ms          | 1.00    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(True)                        |
|          | 209±4ms              | 209±3ms             | 1.00    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(False)               |
|          | 277±5ms              | 275±4ms             | 0.99    | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(True)                |
|          | 1.23±0.02ms          | 1.23±0.02ms         | 1.00    | cube.CubeCreation.time_create(False, 'construct')                                           |
|          | 490±20μs             | 491±20μs            | 1.00    | cube.CubeCreation.time_create(False, 'instantiate')                                         |
|          | 1.98±0.02ms          | 1.93±0.03ms         | 0.98    | cube.CubeCreation.time_create(True, 'construct')                                            |
|          | 1.40±0.01ms          | 1.36±0.02ms         | 0.97    | cube.CubeCreation.time_create(True, 'instantiate')                                          |
|          | 77.4±3ms             | 78.2±2ms            | 1.01    | cube.CubeEquality.time_equality(False, False, 'all_equal')                                  |
|          | 26.4±0.4ms           | 25.6±0.9ms          | 0.97    | cube.CubeEquality.time_equality(False, False, 'coord_inequality')                           |
|          | 89.5±1ms             | 90.4±0.8ms          | 1.01    | cube.CubeEquality.time_equality(False, False, 'data_inequality')                            |
|          | 61.5±1μs             | 62.5±2μs            | 1.02    | cube.CubeEquality.time_equality(False, False, 'metadata_inequality')                        |
|          | 81.4±2ms             | 81.1±2ms            | 1.00    | cube.CubeEquality.time_equality(False, True, 'all_equal')                                   |
|          | 28.1±0.9ms           | 29.2±0.4ms          | 1.04    | cube.CubeEquality.time_equality(False, True, 'coord_inequality')                            |
|          | 91.5±1ms             | 92.8±2ms            | 1.01    | cube.CubeEquality.time_equality(False, True, 'data_inequality')                             |
|          | 60.7±3μs             | 59.6±3μs            | 0.98    | cube.CubeEquality.time_equality(False, True, 'metadata_inequality')                         |
|          | 155±8ms              | 155±4ms             | 1.00    | cube.CubeEquality.time_equality(True, False, 'all_equal')                                   |
|          | 65.4±4ms             | 65.7±0.8ms          | 1.00    | cube.CubeEquality.time_equality(True, False, 'coord_inequality')                            |
|          | 181±6ms              | 186±4ms             | 1.02    | cube.CubeEquality.time_equality(True, False, 'data_inequality')                             |
|          | 92.5±8μs             | 87.2±1μs            | 0.94    | cube.CubeEquality.time_equality(True, False, 'metadata_inequality')                         |
|          | 229±10ms             | 234±10ms            | 1.02    | cube.CubeEquality.time_equality(True, True, 'all_equal')                                    |
|          | 137±6ms              | 140±3ms             | 1.02    | cube.CubeEquality.time_equality(True, True, 'coord_inequality')                             |
|          | 247±9ms              | 255±3ms             | 1.03    | cube.CubeEquality.time_equality(True, True, 'data_inequality')                              |
|          | 396±4μs              | 404±10μs            | 1.02    | cube.CubeEquality.time_equality(True, True, 'metadata_inequality')                          |
|          | 868±10μs             | 848±3μs             | 0.98    | import_iris.Iris.time__concatenate                                                          |
|          | 186±1μs              | 188±1μs             | 1.01    | import_iris.Iris.time__constraints                                                          |
|          | 116±1μs              | 116±0.9μs           | 1.00    | import_iris.Iris.time__data_manager                                                         |
|          | 100±2μs              | 102±2μs             | 1.01    | import_iris.Iris.time__deprecation                                                          |
|          | 168±0.9μs            | 169±2μs             | 1.00    | import_iris.Iris.time__lazy_data                                                            |
|          | 985±20μs             | 979±10μs            | 0.99    | import_iris.Iris.time__merge                                                                |
|          | 75.0±0.2μs           | 73.6±0.8μs          | 0.98    | import_iris.Iris.time__representation                                                       |
|          | 635±7μs              | 637±10μs            | 1.00    | import_iris.Iris.time_analysis                                                              |
|          | 145±3μs              | 145±2μs             | 0.99    | import_iris.Iris.time_analysis__area_weighted                                               |
|          | 125±1μs              | 124±0.8μs           | 0.99    | import_iris.Iris.time_analysis__grid_angles                                                 |
|          | 255±2μs              | 255±3μs             | 1.00    | import_iris.Iris.time_analysis__interpolation                                               |
|          | 197±3μs              | 196±3μs             | 0.99    | import_iris.Iris.time_analysis__regrid                                                      |
|          | 110±2μs              | 113±2μs             | 1.03    | import_iris.Iris.time_analysis__scipy_interpolate                                           |
|          | 144±1μs              | 142±0.8μs           | 0.98    | import_iris.Iris.time_analysis_calculus                                                     |
|          | 347±4μs              | 337±3μs             | 0.97    | import_iris.Iris.time_analysis_cartography                                                  |
|          | 91.3±0.5μs           | 92.8±1μs            | 1.02    | import_iris.Iris.time_analysis_geomerty                                                     |
|          | 210±2μs              | 208±2μs             | 0.99    | import_iris.Iris.time_analysis_maths                                                        |
|          | 94.3±1μs             | 94.7±1μs            | 1.00    | import_iris.Iris.time_analysis_stats                                                        |
|          | 174±1μs              | 170±2μs             | 0.98    | import_iris.Iris.time_analysis_trajectory                                                   |
|          | 319±5μs              | 319±7μs             | 1.00    | import_iris.Iris.time_aux_factory                                                           |
|          | 81.1±0.9μs           | 81.2±1μs            | 1.00    | import_iris.Iris.time_common                                                                |
|          | 161±1μs              | 162±3μs             | 1.01    | import_iris.Iris.time_common_lenient                                                        |
|          | 1.56±0.01ms          | 1.55±0.01ms         | 0.99    | import_iris.Iris.time_common_metadata                                                       |
|          | 175±4μs              | 176±1μs             | 1.00    | import_iris.Iris.time_common_mixin                                                          |
|          | 1.17±0ms             | 1.16±0.01ms         | 0.99    | import_iris.Iris.time_common_resolve                                                        |
|          | 259±5μs              | 255±1μs             | 0.99    | import_iris.Iris.time_config                                                                |
|          | 133±4μs              | 133±1μs             | 1.00    | import_iris.Iris.time_coord_categorisation                                                  |
|          | 379±4μs              | 370±6μs             | 0.98    | import_iris.Iris.time_coord_systems                                                         |
|          | 841±20μs             | 820±5μs             | 0.97    | import_iris.Iris.time_coords                                                                |
|          | 746±20μs             | 742±10μs            | 0.99    | import_iris.Iris.time_cube                                                                  |
|          | 262±2μs              | 257±1μs             | 0.98    | import_iris.Iris.time_exceptions                                                            |
|          | 76.0±0.7μs           | 74.5±0.5μs          | 0.98    | import_iris.Iris.time_experimental                                                          |
|          | 205±1μs              | 207±0.3μs           | 1.01    | import_iris.Iris.time_fileformats                                                           |
|          | 253±4μs              | 255±4μs             | 1.01    | import_iris.Iris.time_fileformats__ff                                                       |
|          | 2.69±0.04ms          | 2.69±0.04ms         | 1.00    | import_iris.Iris.time_fileformats__ff_cross_references                                      |
|          | 77.2±0.7μs           | 77.6±0.5μs          | 1.01    | import_iris.Iris.time_fileformats__pp_lbproc_pairs                                          |
|          | 115±1μs              | 114±1μs             | 0.99    | import_iris.Iris.time_fileformats_abf                                                       |
|          | 86.6±0.7μs           | 85.6±0.9μs          | 0.99    | import_iris.Iris.time_fileformats_cf                                                        |
|          | 4.57±0.1ms           | 4.57±0.1ms          | 1.00    | import_iris.Iris.time_fileformats_dot                                                       |
|          | 72.3±0.6μs           | 72.0±0.4μs          | 1.00    | import_iris.Iris.time_fileformats_name                                                      |
|          | 250±4μs              | 251±1μs             | 1.00    | import_iris.Iris.time_fileformats_name_loaders                                              |
|          | 121±1μs              | 120±0.3μs           | 0.99    | import_iris.Iris.time_fileformats_netcdf                                                    |
|          | 160±3μs              | 162±2μs             | 1.01    | import_iris.Iris.time_fileformats_nimrod                                                    |
|          | 317±5μs              | 320±2μs             | 1.01    | import_iris.Iris.time_fileformats_nimrod_load_rules                                         |
|          | 807±10μs             | 814±10μs            | 1.01    | import_iris.Iris.time_fileformats_pp                                                        |
|          | 174±2μs              | 176±1μs             | 1.01    | import_iris.Iris.time_fileformats_pp_load_rules                                             |
|          | 132±1μs              | 135±1μs             | 1.02    | import_iris.Iris.time_fileformats_pp_save_rules                                             |
|          | 566±4μs              | 568±3μs             | 1.00    | import_iris.Iris.time_fileformats_rules                                                     |
|          | 226±4μs              | 225±3μs             | 0.99    | import_iris.Iris.time_fileformats_structured_array_identification                           |
|          | 82.6±1μs             | 81.8±0.5μs          | 0.99    | import_iris.Iris.time_fileformats_um                                                        |
|          | 157±4μs              | 158±1μs             | 1.01    | import_iris.Iris.time_fileformats_um__fast_load                                             |
|          | 137±2μs              | 139±1μs             | 1.02    | import_iris.Iris.time_fileformats_um__fast_load_structured_fields                           |
|          | 72.7±0.9μs           | 73.1±0.6μs          | 1.01    | import_iris.Iris.time_fileformats_um__ff_replacement                                        |
|          | 78.3±0.5μs           | 78.0±1μs            | 1.00    | import_iris.Iris.time_fileformats_um__optimal_array_structuring                             |
|          | 982±7μs              | 989±5μs             | 1.01    | import_iris.Iris.time_fileformats_um_cf_map                                                 |
|          | 159±2μs              | 160±1μs             | 1.01    | import_iris.Iris.time_io                                                                    |
|          | 191±4μs              | 187±1μs             | 0.98    | import_iris.Iris.time_io_format_picker                                                      |
|          | 204±2μs              | 205±2μs             | 1.00    | import_iris.Iris.time_iris                                                                  |
|          | 130±1μs              | 128±2μs             | 0.98    | import_iris.Iris.time_iterate                                                               |
|          | 8.68±0.06ms          | 8.56±0.09ms         | 0.99    | import_iris.Iris.time_palette                                                               |
|          | 1.90±0.03ms          | 1.88±0.06ms         | 0.99    | import_iris.Iris.time_plot                                                                  |
|          | 257±20μs             | 256±10μs            | 1.00    | import_iris.Iris.time_quickplot                                                             |
|          | 2.26±0.02ms          | 2.21±0.01ms         | 0.98    | import_iris.Iris.time_std_names                                                             |
|          | 1.85±0.02ms          | 1.87±0.02ms         | 1.01    | import_iris.Iris.time_symbols                                                               |
|          | 230±10ms             | 231±10ms            | 1.01    | import_iris.Iris.time_tests                                                                 |
|          | 251±1μs              | 252±3μs             | 1.00    | import_iris.Iris.time_third_party_cartopy                                                   |
|          | 4.98±0.1ms           | 5.03±0.02ms         | 1.01    | import_iris.Iris.time_third_party_cf_units                                                  |
|          | 121±0.9μs            | 121±0.9μs           | 1.00    | import_iris.Iris.time_third_party_cftime                                                    |
|          | 2.56±0.02ms          | 2.51±0.02ms         | 0.98    | import_iris.Iris.time_third_party_matplotlib                                                |
|          | 519±2μs              | 553±10μs            | 1.07    | import_iris.Iris.time_third_party_numpy                                                     |
|          | 174±0.6μs            | 173±1μs             | 0.99    | import_iris.Iris.time_third_party_scipy                                                     |
|          | 99.8±1μs             | 101±1μs             | 1.01    | import_iris.Iris.time_time                                                                  |
|          | 855±10μs             | 848±5μs             | 0.99    | import_iris.Iris.time_util                                                                  |
|          | 63.4±0.8μs           | 64.2±1μs            | 1.01    | iterate.IZip.time_izip                                                                      |
|          | 9.89±0.09ms          | 9.89±0.1ms          | 1.00    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'FF')                                  |
|          | 14.8±0.4ms           | 15.5±0.8ms          | 1.05    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'NetCDF')                              |
|          | 9.78±0.2ms           | 9.84±0.1ms          | 1.01    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'PP')                                  |
|          | 9.88±0.08ms          | 9.92±0.05ms         | 1.00    | load.LoadAndRealise.time_load((1280, 960, 5), True, 'FF')                                   |
|          | 12.3±0.1ms           | 12.7±0.3ms          | 1.03    | load.LoadAndRealise.time_load((1280, 960, 5), True, 'NetCDF')                               |
|          | 9.69±0.05ms          | 9.91±0.2ms          | 1.02    | load.LoadAndRealise.time_load((1280, 960, 5), True, 'PP')                                   |
|          | 1.50±0.01s           | 1.52±0.01s          | 1.01    | load.LoadAndRealise.time_load((2, 2, 1000), False, 'FF')                                    |
|          | 11.3±0.09ms          | 11.3±0.2ms          | 1.00    | load.LoadAndRealise.time_load((2, 2, 1000), False, 'NetCDF')                                |
|          | 1.49±0.01s           | 1.50±0.01s          | 1.00    | load.LoadAndRealise.time_load((2, 2, 1000), False, 'PP')                                    |
|          | 1.52±0.02s           | 1.50±0.01s          | 0.99    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'FF')                                     |
|          | 11.3±0.03ms          | 11.6±0.1ms          | 1.03    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'NetCDF')                                 |
|          | 1.49±0.01s           | 1.47±0.01s          | 0.99    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'PP')                                     |
|          | 5.20±0.03ms          | 5.26±0.05ms         | 1.01    | load.LoadAndRealise.time_load((50, 50, 2), False, 'FF')                                     |
|          | 11.2±0.2ms           | 11.3±0.3ms          | 1.01    | load.LoadAndRealise.time_load((50, 50, 2), False, 'NetCDF')                                 |
|          | 5.07±0.02ms          | 5.17±0.04ms         | 1.02    | load.LoadAndRealise.time_load((50, 50, 2), False, 'PP')                                     |
|          | 5.25±0.06ms          | 5.25±0.05ms         | 1.00    | load.LoadAndRealise.time_load((50, 50, 2), True, 'FF')                                      |
|          | 11.2±0.3ms           | 11.0±0.4ms          | 0.98    | load.LoadAndRealise.time_load((50, 50, 2), True, 'NetCDF')                                  |
|          | 5.07±0.03ms          | 5.12±0.06ms         | 1.01    | load.LoadAndRealise.time_load((50, 50, 2), True, 'PP')                                      |
|          | 20.6±1ms             | 21.6±1ms            | 1.05    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'FF')                               |
|          | 20.3±0.6ms           | 20.0±0.3ms          | 0.99    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'NetCDF')                           |
|          | 11.6±1ms             | 12.3±0.8ms          | 1.06    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'PP')                               |
|          | 28.4±1ms             | 28.6±0.7ms          | 1.01    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'FF')                                |
|          | 68.9±0.3ms           | 69.2±0.1ms          | 1.01    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'NetCDF')                            |
|          | 27.6±1ms             | 28.4±1ms            | 1.03    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'PP')                                |
|          | 642±4ms              | 651±4ms             | 1.02    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'FF')                                 |
|          | 3.36±0.1ms           | 3.39±0.2ms          | 1.01    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'NetCDF')                             |
|          | 647±3ms              | 648±4ms             | 1.00    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'PP')                                 |
|          | 648±5ms              | 641±5ms             | 0.99    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'FF')                                  |
|          | 3.38±0.1ms           | 3.47±0.1ms          | 1.03    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'NetCDF')                              |
|          | 648±10ms             | 638±5ms             | 0.99    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'PP')                                  |
|          | 2.21±0.03ms          | 2.26±0.04ms         | 1.03    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'FF')                                  |
|          | 3.36±0.1ms           | 3.52±0.07ms         | 1.05    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'NetCDF')                              |
|          | 2.20±0.07ms          | 2.30±0.07ms         | 1.04    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'PP')                                  |
|          | 2.23±0.07ms          | 2.31±0.08ms         | 1.04    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'FF')                                   |
|          | 3.46±0.1ms           | 3.57±0.03ms         | 1.03    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'NetCDF')                               |
|          | 2.29±0.05ms          | 2.41±0.1ms          | 1.05    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'PP')                                   |
|          | 344±2ms              | 344±2ms             | 1.00    | load.ManyCubes.time_many_cube_load                                                          |
|          | 82.2±0.7ms           | 81.8±1ms            | 1.00    | load.ManyVars.time_many_var_load                                                            |
|          | 9.89±0.04ms          | 9.99±0.1ms          | 1.01    | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'FF')                            |
|          | 10.0±0.1ms           | 10.0±0.2ms          | 1.00    | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'PP')                            |
|          | 1.51±0.01s           | 1.53±0.01s          | 1.01    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'FF')                              |
|          | 1.50±0.01s           | 1.49±0.02s          | 1.00    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'PP')                              |
|          | 5.29±0.07ms          | 5.28±0.03ms         | 1.00    | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'FF')                                 |
|          | 5.19±0.04ms          | 5.10±0.05ms         | 0.98    | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'PP')                                 |
|          | 8.97±0.07ms          | 8.96±0.3ms          | 1.00    | load.StructuredFF.time_structured_load((1280, 960, 5), False)                               |
|          | 5.58±0.04ms          | 5.65±0.09ms         | 1.01    | load.StructuredFF.time_structured_load((1280, 960, 5), True)                                |
|          | 1.50±0.01s           | 1.49±0.01s          | 1.00    | load.StructuredFF.time_structured_load((2, 2, 1000), False)                                 |
|          | 423±5ms              | 426±6ms             | 1.01    | load.StructuredFF.time_structured_load((2, 2, 1000), True)                                  |
|          | 4.41±0.03ms          | 4.44±0.02ms         | 1.01    | load.StructuredFF.time_structured_load((2, 2, 2), False)                                    |
|          | 4.24±0.04ms          | 4.25±0.05ms         | 1.00    | load.StructuredFF.time_structured_load((2, 2, 2), True)                                     |
|          | 154±2ms              | 156±1ms             | 1.02    | load.TimeConstraint.time_time_constraint(20, 'FF')                                          |
|          | 14.2±0.4ms           | 14.0±0.4ms          | 0.99    | load.TimeConstraint.time_time_constraint(20, 'NetCDF')                                      |
|          | 155±0.7ms            | 156±2ms             | 1.01    | load.TimeConstraint.time_time_constraint(20, 'PP')                                          |
|          | 31.1±0.1ms           | 31.2±0.7ms          | 1.00    | load.TimeConstraint.time_time_constraint(3, 'FF')                                           |
|          | 14.0±0.2ms           | 13.5±0.2ms          | 0.96    | load.TimeConstraint.time_time_constraint(3, 'NetCDF')                                       |
|          | 31.4±0.3ms           | 30.7±0.3ms          | 0.98    | load.TimeConstraint.time_time_constraint(3, 'PP')                                           |
|          | 17.5±0.6ms           | 17.9±0.7ms          | 1.02    | load.ugrid.BasicLoading.time_load_file(1)                                                   |
|          | 47.1±1ms             | 48.1±0.8ms          | 1.02    | load.ugrid.BasicLoading.time_load_file(200000)                                              |
|          | 7.92±0.2ms           | 7.51±0.4ms          | 0.95    | load.ugrid.BasicLoading.time_load_mesh(1)                                                   |
|          | 15.0±1ms             | 15.9±0.3ms          | 1.06    | load.ugrid.BasicLoading.time_load_mesh(200000)                                              |
|          | 18.4±0.5ms           | 18.0±0.4ms          | 0.98    | load.ugrid.BasicLoadingTime.time_load_file(1)                                               |
|          | 18.3±0.5ms           | 18.2±0.4ms          | 0.99    | load.ugrid.BasicLoadingTime.time_load_file(200000)                                          |
|          | 7.71±0.3ms           | 7.67±0.5ms          | 1.00    | load.ugrid.BasicLoadingTime.time_load_mesh(1)                                               |
|          | 10.3±0.5ms           | 11.3±0.3ms          | 1.09    | load.ugrid.BasicLoadingTime.time_load_mesh(200000)                                          |
|          | 20.0±0.4ms           | 20.3±0.5ms          | 1.02    | load.ugrid.Callback.time_load_file_callback(1)                                              |
|          | 60.3±2ms             | 59.1±1ms            | 0.98    | load.ugrid.Callback.time_load_file_callback(200000)                                         |
|          | 19.9±0.6ms           | 20.2±0.7ms          | 1.01    | load.ugrid.CallbackTime.time_load_file_callback(1)                                          |
|          | 22.8±0.6ms           | 22.3±0.5ms          | 0.98    | load.ugrid.CallbackTime.time_load_file_callback(200000)                                     |
|          | 3.82±0.3ms           | 3.67±0.2ms          | 0.96    | load.ugrid.DataRealisation.time_realise_data(10000)                                         |
|          | 4.81±0.4ms           | 4.67±0.2ms          | 0.97    | load.ugrid.DataRealisation.time_realise_data(200000)                                        |
|          | 48.8±5ms             | 46.7±3ms            | 0.96    | load.ugrid.DataRealisationTime.time_realise_data(10000)                                     |
|          | 988±40ms             | 995±20ms            | 1.01    | load.ugrid.DataRealisationTime.time_realise_data(200000)                                    |
|          | 1.06±0.02s           | 1.05±0.02s          | 1.00    | merge_concat.Concatenate.time_concatenate(False)                                            |
|          | 442±2ms              | 448±5ms             | 1.01    | merge_concat.Concatenate.time_concatenate(True)                                             |
|          | 2.21±0G              | 2.21±0G             | 1.00    | merge_concat.Concatenate.tracemalloc_concatenate(False)                                     |
|          | 71.7±4M              | 71.9±2M             | 1.00    | merge_concat.Concatenate.tracemalloc_concatenate(True)                                      |
|          | 31.4±0.7ms           | 31.8±0.6ms          | 1.01    | merge_concat.Merge.time_merge                                                               |
|          | 126±0.03M            | 126±0.03M           | 1.00    | merge_concat.Merge.tracemalloc_merge                                                        |
|          | 5.63±0.09ms          | 5.64±0.06ms         | 1.00    | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(50)              |
|          | 147±2ms              | 144±0.6ms           | 0.98    | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(500)             |
|          | 769±3k               | 768±1k              | 1.00    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(50)       |
|          | 60.2±0M              | 60.2±0M             | 1.00    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(500)      |
|          | 18.8±0.4ms           | 19.0±0.4ms          | 1.01    | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(50)           |
|          | 21.8±0.3ms           | 21.8±0.3ms          | 1.00    | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(500)          |
|          | 24.6±0M              | 25±0M               | 1.02    | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(500)   |
|          | 113±3ms              | 111±2ms             | 0.98    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(50)         |
|          | 389±6ms              | 391±5ms             | 1.00    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(500)        |
|          | 1.51±0.03M           | 1.53±0.03M          | 1.01    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(50)  |
|          | 96.5±0.02M           | 96.6±0.03M          | 1.00    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(500) |
|          | 75.7±3ms             | 72.8±1ms            | 0.96    | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(50)                             |
|          | 354±4ms              | 349±4ms             | 0.99    | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(500)                            |
|          | 1.44±0.05M           | 1.42±0.03M          | 0.99    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(50)                      |
|          | 96.4±0.04M           | 96.5±0.04M          | 1.00    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(500)                     |
|          | 2.175361             | 2.175361            | 1.00    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(50)                  |
|          | 216.01536099999998   | 216.01536099999998  | 1.00    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(500)                 |
|          | 6.49±0.1ms           | 6.34±0.05ms         | 0.98    | plot.AuxSort.time_aux_sort                                                                  |
|          | 51.4±1ms             | 51.1±1ms            | 0.99    | regridding.CurvilinearRegridding.time_regrid_pic                                            |
|          | 136±3M               | 136±3M              | 1.00    | regridding.CurvilinearRegridding.tracemalloc_regrid_pic                                     |
|          | 105±6ms              | 97.5±5ms            | 0.92    | regridding.HorizontalChunkedRegridding.time_regrid_area_w                                   |
|          | 51.4±0.9ms           | 51.6±0.9ms          | 1.00    | regridding.HorizontalChunkedRegridding.time_regrid_area_w_new_grid                          |
|          | 107±0.1M             | 107±0.1M            | 1.00    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w                            |
|          | 147±0.03M            | 147±0.03M           | 1.00    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w_new_grid                   |
|          | 4.68±0.3ms           | 4.83±0.2ms          | 1.03    | save.NetcdfSave.time_netcdf_save_cube(50, False)                                            |
|          | 80.4±3ms             | 79.3±0.9ms          | 0.99    | save.NetcdfSave.time_netcdf_save_cube(50, True)                                             |
|          | 20.4±0.7ms           | 20.0±0.5ms          | 0.98    | save.NetcdfSave.time_netcdf_save_cube(600, False)                                           |
|          | 274±5ms              | 272±5ms             | 0.99    | save.NetcdfSave.time_netcdf_save_cube(600, True)                                            |
|          | 1.04±0.3μs           | 1.42±0.2μs          | ~1.36   | save.NetcdfSave.time_netcdf_save_mesh(50, False)                                            |
|          | 61.5±3ms             | 60.3±1ms            | 0.98    | save.NetcdfSave.time_netcdf_save_mesh(50, True)                                             |
|          | 2.35±0.1μs           | 2.62±0.6μs          | 1.12    | save.NetcdfSave.time_netcdf_save_mesh(600, False)                                           |
|          | 238±3ms              | 238±3ms             | 1.00    | save.NetcdfSave.time_netcdf_save_mesh(600, True)                                            |
|          | 34.2±1k              | 33.9±1k             | 0.99    | save.NetcdfSave.tracemalloc_netcdf_save(50, False)                                          |
|          | 1.88±0.2M            | 1.75±0.2M           | 0.93    | save.NetcdfSave.tracemalloc_netcdf_save(50, True)                                           |
|          | 34.1±1k              | 33.9±1k             | 0.99    | save.NetcdfSave.tracemalloc_netcdf_save(600, False)                                         |
|          | 225±30M              | 208±10M             | 0.92    | save.NetcdfSave.tracemalloc_netcdf_save(600, True)                                          |
|          | 40.7±0.8ms           | 40.9±0.8ms          | 1.01    | stats.PearsonR.time_lazy                                                                    |
|          | 9.48±0.2ms           | 9.51±0.1ms          | 1.00    | stats.PearsonR.time_real                                                                    |
|          | 29±1M                | 29±1M               | 1.00    | stats.PearsonR.tracemalloc_lazy                                                             |
|          | 18.3±0M              | 18.3±0M             | 1.00    | stats.PearsonR.tracemalloc_real                                                             |
|          | 23.4±0.5ms           | 23.1±0.4ms          | 0.99    | trajectory.TrajectoryInterpolation.time_trajectory_linear                                   |
|          | 81.6±0.4ms           | 81.3±0.3ms          | 1.00    | trajectory.TrajectoryInterpolation.time_trajectory_nearest                                  |
|          | 17.6±0.01M           | 17.6±0.01M          | 1.00    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_linear                            |
|          | 7.74±0.01M           | 7.74±0.01M          | 1.00    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_nearest                           |

Benchmarks that have got worse:

| Change   | Before [426d7189]    | After [989c1ba2]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 821±2k               | 1.23±0M             |     1.5 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |

Generated by GHA run 39959072675

trexfeathers and others added 8 commits October 23, 2026 14:48
The new CF grid-mapping test imported the Zarr saver with nothing to skip
on, so an Iris without zarr installed gained a test error -- the one thing
section 6 of the design says adding Zarr must not do.

The guard is per test rather than at the top of the module, because the
module's other two tests are netCDF's: skipping those where Zarr is missing
would trade a new failure for a silent gap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
create_dimension checked a new declaration against the declarations made in
this session, and not against the arrays already in the group. Reopening a
store whose "x" is two long and declaring "x" as three succeeded, leaving
dimensions reporting one length for a name two arrays hold at two.

Section 4.5's create-once rule means the array that is already there is
never resized, so there is no outcome where both sizes are true. It now
checks `dimensions`, which is the declarations and the arrays both. The
saver already renames its own dimension when it meets a collision; this is
what lets it see one.

The mapping is updated rather than dropped afterwards, because dropping it
reassembled it from every array in the group on the next declaration, over
a store that may be remote.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
Two places where the Zarr dataset said one thing and did another.

A fill value was cast to the array's dtype for storage and written to
'_FillValue' unconverted, so an int16 array asked for fill_value=1.5 stored
1 and declared 1.5. Iris read that back correctly, because it narrows a
declared sentinel to the stored dtype before comparing, which is what hid
it; xarray compares against the attribute and would have masked nothing.
Section 4.5 asks for one value in both places, so a sentinel the dtype
cannot hold is now refused. A floating sentinel may still lose precision,
and must: every float32 fill written as a Python float is a float64.

supports_dtype answered True for every dtype, including the ones
create_variable then refused -- object arrays, and the extended precisions
NumPy sizes by platform. Section 4.2 says it answers for what Zarr has a
data type for, and now it does. create_variable asks it rather than letting
zarr-python raise, so the two cannot disagree again, and the refusal is in
Iris's words.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
Saving into a subgroup with consolidate_metadata=False left the root's
consolidated document untouched. A default reader believes that document,
and it does not mention the group just written: the cubes were in the store
and invisible. The user guide already promised the document was removed,
and nothing removed it.

The root-level case passes today only by side effect -- writing an array at
the root makes zarr-python rewrite the root metadata, and the consolidated
section goes with it -- which is why the existing test did not see this. A
promise kept by side effect is kept only where the side effect reaches.

ZarrDataset gains drop_consolidated_metadata, the other half of finalise,
working from the whole store's root as finalise does. The saver's
completion now calls one or the other, rather than one or nothing, so it
waits for the data under compute=False like the consolidation it replaces.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
Section 6 asks for one test body run against every CFDataset
implementation, and the write half had only netCDF's subclass. This
module's own docstring said the Zarr half arrives with the saver, which it
now has.

It passes unchanged: the refusal of an unlimited dimension is the one
answer the contract sanctions two of, and Zarr gives the other one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
The review read the specification as current and found four places where it
contradicts itself. Each is a change made in section 4.2 or in the decision
log and never carried through to the sections that repeat it.

The CFDataset member renamed storage_settings on 2026-10-20 kept its old
name, `encoding`, in sections 5 and 6, where it names no member of
anything. The rename was never logged at all, which is what let it rot.

Section 6 still asked for the read-side alignment guard that was removed
the same day: that dask chunks tile the store grid in every case, and that
a chunk larger than the target is not subdivided. Both now say what the
loader does, and what its tests assert -- including that a pinned
CHUNK_CONTROL setting is honoured without a warning, because a caller who
pins has already taken the trade.

Section 6's skipping rule described one importorskip per Zarr test package,
which no longer covers everything after a test outside those packages came
to exercise the Zarr backend.

Q10 was closed into the decision log on 2026-10-16 but its row was left
standing in the open questions, still saying the NETCDF3_64BIT defect was
preserved rather than fixed. Removed; a question is closed by moving it,
and half a move leaves the table disagreeing with the log.

Q4 is closed here. It asked whether the netCDF read path should get the
same alignment guard as Zarr, and there is no such guard on either path
any more, so the asymmetry it existed to resolve cannot arise.

Also records PR 4 as merged, and what this review found in the code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
The first fix listed the dtypes to exclude, and review found two more it
had missed: a structured dtype with an object field, and a zero-length
void. Both answered True and then failed in create_variable.

A list of exclusions is the wrong shape of answer, and the version floor
proves it. zarr-python 3.1 replaced a fixed enum of fifteen data types with
an extensible registry, so 3.4 stores a structured dtype and a datetime64
that 3.0.8 -- the oldest Iris accepts -- has no data type for. Any list
written against the installed version is wrong on the declared one.

So the answer now comes from a table of kinds and item sizes: bool, the
integers and unsigneds, floats to 8 bytes, complex to 16, and fixed-length
text and bytes of non-zero length. That is the intersection across the
versions Iris accepts, which makes a few answers conservative -- datetime64
is False although zarr-python 3.4 would store it. Conservative is the safe
direction, because create_variable asks this before it creates anything: a
False is a refusal in Iris's words, while a True the store goes back on is
the thing section 4.2's rule exists to prevent.

Byte order stays out of the answer, although 3.0.8's table is little-endian
only. A PP or GRIB load yields big-endian arrays, and a False there would
send a big-endian int64 into the saver's narrowing path on every version,
including the ones that would have stored it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
The header said PR 4 was in review and was the next action, while §12.1
recorded it as merged. That is the second time the header has gone stale
against §12.1, so it now carries the rule as well as the summary: it is a
summary of §12.1 and nothing more, and a reader who stops at the header
must not be told something §12 contradicts.

§4.2 gains what supports_dtype answers from, and why: a table of what Zarr
has rather than a list of what it lacks, the intersection across the
zarr-python versions Iris accepts rather than what the newest holds, and
byte order left out of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
Comment thread lib/iris/fileformats/zarr/_dataset.py Outdated
@@ -662,17 +662,39 @@ def cache_key(self) -> str:
#: makes that case safe.
_EXACT_FILL_KINDS = "biu"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be clearer as a list rather than a string. Iterating through a string seems like bad code smell to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supports_dtype answered True for a big-endian dtype, which zarr-python
3.0.8 has no data type for at all: `>i8` raises KeyError. The last round
found this and recorded it as an acknowledged exception rather than fixing
it, on the grounds that answering False would be worse. That much was
right. supports_dtype is consulted for int64 and unsigned, so a False would
have narrowed big-endian int64 data, and create_variable -- which now asks
before it creates -- would have refused big-endian floats outright. A PP or
GRIB load produces both, so Iris would have stopped saving ordinary cubes.

But the choice was never between those two. create_variable now creates in
the platform's own byte order whatever order it is handed, and NumPy
converts on assignment: the values stored are the caller's and only the
layout is the store's. supports_dtype can then answer True for `>i8` on
every supported version, because every supported version can keep it.

This is what the netCDF saver has always done with the same data -- a `>f4`
cube is float32 in the file it writes -- so the Zarr path is matching it
rather than adding a second rule.

Checked by running it, not by reading zarr-python's source: zarr-python
3.0.8 installed in a throwaway environment, and every dtype the table
answers True for created in turn, on 3.0.8 and 3.4.1 both. Every one
creates on both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe

@trexfeathers trexfeathers left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(posted by GPT)

I am satisfied that findings 1–4, 6 and 7 from my specification audit are addressed at df4f443307dd460f2c14829199189abd5fd488cc. Finding 5 remains deferred to @trexfeathers as agreed. This closes those findings; it is not a claim that the whole programme’s specification is delivered.

The final byte-order change resolves the remaining capability concern. I ran the actual Iris dtype and byte-order tests against Zarr 3.0.8, including the big-endian cube round trip. Array creation normalises byte order and preserves the values.

Two separate test assertions still fail on Zarr 3.0.8: U5 and S5 create successfully, but their reported dtypes are StringDType() and object, while the tests expect the original fixed-width dtypes. Those assertions need reconciliation with the supported minimum version. They do not reopen the big-endian finding.

Validation: 465 regression tests passed on Zarr 3.4.1; minimum-version tests passed except for two string dtype assertions
Environment Tests run Result
Zarr 3.4.1 Zarr dataset and saver unit tests, CF dataset unit tests, Zarr save integration tests 465 passed
Zarr 3.0.8 Dtype capability and byte-order unit tests, big-endian cube round trip 64 passed, 2 failed; both failures are the string dtype assertions described above

The specification corrections were checked by inspection. No repository files were edited.

trexfeathers and others added 3 commits October 23, 2026 15:50
lib/iris/AGENTS.md puts prose that explains a subsystem in the module
docstring, holds a comment to three lines, and says a fact a test can pin
belongs in that test with one line in the source naming it. Three of the
comments added with the dtype work were essays instead: twelve lines on
byte order at the assignment, and twenty on the constant.

What Zarr can hold and how that varies by zarr-python version is now a
fourth trap in the module docstring, beside the three already there. The
constants carry a label each, create_variable keeps one line pointing at
the docstring and at TestByteOrder, and the docstrings lose the sentences
that defended the design rather than stating it.

No behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
docs/AGENTS.md: published prose must never cite an artefact of how the work
was done -- a review finding, a task or plan number, a phase. The decision
log and the history table had entries turning on "the third round of the
same finding" and "the second round", which resolve to nothing for anyone
who was not there. Each now states the change and leaves the dates and the
pull request numbers to anchor it, as the surrounding entries already do.

No design change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 91b685bc-5a5a-4512-a687-b8bc67b9e3fe
The mesh paragraph in the saving section claimed that loading a store gives
the mesh back. That sentence replaced a caveat saying loading did not yet
rebuild the mesh, and outlived the reason it existed: the loading section
states the same fact three lines from the top. Removed.

The note about a Zarr attribute netCDF cannot hold moves from the mesh
subsection, where it has nothing to do with meshes, to the loading section,
where the cube it describes comes from.

_EXACT_FILL_KINDS and _STORABLE_TEXT_KINDS are frozensets rather than bare
strings, matching _TEXT_KINDS in zarr/saver.py. _STORABLE_TEXT_KINDS gains
"T", so the two agree on what text is: NumPy's variable-width StringDType
creates on zarr-python 3.0.8 and 3.4.1 alike, and supports_dtype had been
answering False for it.

The dtype contract test asserted that a created array reports the dtype it
was given, which holds for the numeric kinds but not the text ones: zarr
3.0.8 reports StringDType() for U5 and object for S5, where 3.4 reports the
fixed width. For text it now asserts what both versions promise, that the
values written come back.

Verified on both: 156 passed in the dataset write tests against zarr 3.0.8,
and the Zarr and CF suites pass against 3.4.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Session-Id: 76a82181-7fe1-4063-9fd7-deb10fb0d658

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not relevant to this file specifically, but I wanted somewhere to start a conversation thread:

Testing on this PR has revealed that Iris does not support Zarr down to version 3.0.8 as claimed. The code was written against 3.4.1. 3.0.8 was chosen as the 'floor' due to a known bug with older versions. Options:

  • Raise the minimum supported version to the very latest version of Zarr, the one that the code was written against
  • Additional work to support 3.0.8 properly
    • Here
    • Another PR

@github-actions

Copy link
Copy Markdown
Contributor

⏱️ Performance Benchmark Report: 91c9407

Performance shifts
| Change   | Before [426d7189]    | After [91c9407e]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 820±2k               | 1.23±0M             |    1.5  | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |
| +        | 1.15±0.1μs           | 1.89±0.2μs          |    1.64 | save.NetcdfSave.time_netcdf_save_mesh(50, False)                                         |
| +        | 2.08±0.1μs           | 3.24±0.3μs          |    1.55 | save.NetcdfSave.time_netcdf_save_mesh(600, False)                                        |
Full benchmark results

Benchmarks that have stayed the same:

| Change   | Before [426d7189]    | After [91c9407e]    |   Ratio | Benchmark (Parameter)                                                                       |
|----------|----------------------|---------------------|---------|---------------------------------------------------------------------------------------------|
|          | 22.0±2ms             | 21.5±0.6ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(False)                              |
|          | 54.1±1ms             | 53.6±0.7ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_COUNT(True)                               |
|          | 38.3±3ms             | 37.1±0.8ms          |    0.97 | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(False)                    |
|          | 175±6ms              | 179±3ms             |    1.03 | aggregate_collapse.Aggregation.time_aggregated_by_FAST_PERCENTILE(True)                     |
|          | 24.4±2ms             | 23.3±0.6ms          |    0.96 | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(False)                              |
|          | 33.7±2ms             | 32.8±1ms            |    0.97 | aggregate_collapse.Aggregation.time_aggregated_by_GMEAN(True)                               |
|          | 24.9±2ms             | 23.6±0.2ms          |    0.95 | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(False)                              |
|          | 33.0±1ms             | 32.6±1ms            |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_HMEAN(True)                               |
|          | 22.0±1ms             | 20.9±0.4ms          |    0.95 | aggregate_collapse.Aggregation.time_aggregated_by_MAX(False)                                |
|          | 45.6±1ms             | 46.0±0.9ms          |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_MAX(True)                                 |
|          | 138±5ms              | 144±2ms             |    1.04 | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(False)                            |
|          | 139±2ms              | 140±1ms             |    1    | aggregate_collapse.Aggregation.time_aggregated_by_MAX_RUN(True)                             |
|          | 23.2±2ms             | 22.8±0.4ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(False)                               |
|          | 50.9±1ms             | 50.0±0.4ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_MEAN(True)                                |
|          | 24.8±2ms             | 23.5±0.4ms          |    0.95 | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(False)                             |
|          | 59.1±0.7ms           | 59.7±0.6ms          |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_MEDIAN(True)                              |
|          | 22.0±1ms             | 21.2±0.2ms          |    0.96 | aggregate_collapse.Aggregation.time_aggregated_by_MIN(False)                                |
|          | 46.1±1ms             | 46.5±1ms            |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_MIN(True)                                 |
|          | 1.19±0.01s           | 1.19±0.01s          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(False)                               |
|          | 1.09±0s              | 1.09±0.01s          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_PEAK(True)                                |
|          | 228±3ms              | 229±3ms             |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(False)                         |
|          | 394±8ms              | 389±10ms            |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_PERCENTILE(True)                          |
|          | 22.8±2ms             | 22.4±0.5ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(False)                         |
|          | 32.2±1ms             | 31.4±1ms            |    0.97 | aggregate_collapse.Aggregation.time_aggregated_by_PROPORTION(True)                          |
|          | 23.5±2ms             | 23.3±0.9ms          |    0.99 | aggregate_collapse.Aggregation.time_aggregated_by_RMS(False)                                |
|          | 60.7±2ms             | 60.6±0.9ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_RMS(True)                                 |
|          | 24.3±1ms             | 24.2±0.6ms          |    1    | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(False)                            |
|          | 66.4±2ms             | 65.2±1ms            |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_STD_DEV(True)                             |
|          | 24.6±2ms             | 24.1±0.6ms          |    0.98 | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(False)                           |
|          | 60.3±2ms             | 61.1±1ms            |    1.01 | aggregate_collapse.Aggregation.time_aggregated_by_VARIANCE(True)                            |
|          | 7.97±0.06ms          | 7.85±0.07ms         |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(False)                               |
|          | 22.7±0.5ms           | 22.4±0.5ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_COUNT(True)                                |
|          | 19.9±0.7ms           | 20.2±0.2ms          |    1.01 | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(False)                     |
|          | 131±8ms              | 131±5ms             |    1    | aggregate_collapse.Aggregation.time_collapsed_by_FAST_PERCENTILE(True)                      |
|          | 8.45±0.1ms           | 8.22±0.1ms          |    0.97 | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(False)                               |
|          | 20.7±0.6ms           | 20.6±0.4ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_GMEAN(True)                                |
|          | 8.42±0.2ms           | 8.35±0.2ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(False)                               |
|          | 20.4±0.7ms           | 21.0±0.4ms          |    1.03 | aggregate_collapse.Aggregation.time_collapsed_by_HMEAN(True)                                |
|          | 7.86±0.04ms          | 7.77±0.1ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_MAX(False)                                 |
|          | 21.3±0.9ms           | 20.8±0.3ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_MAX(True)                                  |
|          | 27.6±2ms             | 26.3±0.4ms          |    0.95 | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(False)                             |
|          | 36.7±0.8ms           | 36.7±0.5ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_MAX_RUN(True)                              |
|          | 8.28±0.08ms          | 7.93±0.09ms         |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(False)                                |
|          | 21.5±0.4ms           | 21.0±0.4ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_MEAN(True)                                 |
|          | 9.04±0.3ms           | 9.08±0.1ms          |    1    | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(False)                              |
|          | 23.9±0.6ms           | 22.6±0.6ms          |    0.95 | aggregate_collapse.Aggregation.time_collapsed_by_MEDIAN(True)                               |
|          | 7.93±0.1ms           | 7.81±0.05ms         |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_MIN(False)                                 |
|          | 21.1±0.5ms           | 20.7±0.5ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_MIN(True)                                  |
|          | 583±1ms              | 583±0.6ms           |    1    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(False)                                |
|          | 580±0.9ms            | 580±2ms             |    1    | aggregate_collapse.Aggregation.time_collapsed_by_PEAK(True)                                 |
|          | 48.0±0.6ms           | 47.6±1ms            |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(False)                          |
|          | 144±3ms              | 139±3ms             |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_PERCENTILE(True)                           |
|          | 8.16±0.09ms          | 7.94±0.05ms         |    0.97 | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(False)                          |
|          | 20.1±0.3ms           | 19.8±0.5ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_PROPORTION(True)                           |
|          | 8.19±0.1ms           | 8.05±0.1ms          |    0.98 | aggregate_collapse.Aggregation.time_collapsed_by_RMS(False)                                 |
|          | 23.0±0.4ms           | 22.2±0.2ms          |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_RMS(True)                                  |
|          | 8.55±0.09ms          | 8.23±0.07ms         |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(False)                             |
|          | 23.0±0.5ms           | 22.2±0.3ms          |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_STD_DEV(True)                              |
|          | 8.55±0.1ms           | 8.24±0.07ms         |    0.96 | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(False)                            |
|          | 21.9±0.3ms           | 21.8±0.3ms          |    0.99 | aggregate_collapse.Aggregation.time_collapsed_by_VARIANCE(True)                             |
|          | 23.6±3ms             | 22.9±0.4ms          |    0.97 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(False)                     |
|          | 84.1±1ms             | 85.7±0.8ms          |    1.02 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_MEAN(True)                      |
|          | 24.2±2ms             | 22.5±0.4ms          |    0.93 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(False)                      |
|          | 98.4±0.9ms           | 96.8±0.5ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_RMS(True)                       |
|          | 22.2±2ms             | 21.3±0.4ms          |    0.96 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(False)                      |
|          | 55.9±0.9ms           | 54.9±0.8ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_aggregated_by_SUM(True)                       |
|          | 8.53±0.1ms           | 8.45±0.1ms          |    0.99 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(False)                      |
|          | 27.5±0.5ms           | 26.9±0.7ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_MEAN(True)                       |
|          | 8.43±0.1ms           | 8.23±0.07ms         |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(False)                       |
|          | 29.4±0.4ms           | 28.4±0.5ms          |    0.97 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_RMS(True)                        |
|          | 8.04±0.1ms           | 7.90±0.05ms         |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(False)                       |
|          | 22.8±0.3ms           | 22.3±0.2ms          |    0.98 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_SUM(True)                        |
|          | 213±2ms              | 211±2ms             |    0.99 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(False)               |
|          | 286±4ms              | 276±7ms             |    0.96 | aggregate_collapse.WeightedAggregation.time_w_collapsed_by_WPERCENTILE(True)                |
|          | 1.27±0.03ms          | 1.25±0.02ms         |    0.99 | cube.CubeCreation.time_create(False, 'construct')                                           |
|          | 469±20μs             | 475±20μs            |    1.01 | cube.CubeCreation.time_create(False, 'instantiate')                                         |
|          | 2.04±0.03ms          | 1.96±0.01ms         |    0.96 | cube.CubeCreation.time_create(True, 'construct')                                            |
|          | 1.44±0.02ms          | 1.36±0.03ms         |    0.95 | cube.CubeCreation.time_create(True, 'instantiate')                                          |
|          | 82.7±1ms             | 82.0±1ms            |    0.99 | cube.CubeEquality.time_equality(False, False, 'all_equal')                                  |
|          | 26.4±0.7ms           | 26.4±1ms            |    1    | cube.CubeEquality.time_equality(False, False, 'coord_inequality')                           |
|          | 95.0±2ms             | 92.4±2ms            |    0.97 | cube.CubeEquality.time_equality(False, False, 'data_inequality')                            |
|          | 65.0±5μs             | 62.6±4μs            |    0.96 | cube.CubeEquality.time_equality(False, False, 'metadata_inequality')                        |
|          | 85.1±1ms             | 84.4±1ms            |    0.99 | cube.CubeEquality.time_equality(False, True, 'all_equal')                                   |
|          | 29.5±0.3ms           | 30.5±1ms            |    1.03 | cube.CubeEquality.time_equality(False, True, 'coord_inequality')                            |
|          | 95.7±2ms             | 96.6±0.9ms          |    1.01 | cube.CubeEquality.time_equality(False, True, 'data_inequality')                             |
|          | 64.1±1μs             | 63.5±2μs            |    0.99 | cube.CubeEquality.time_equality(False, True, 'metadata_inequality')                         |
|          | 164±4ms              | 160±4ms             |    0.98 | cube.CubeEquality.time_equality(True, False, 'all_equal')                                   |
|          | 68.2±2ms             | 67.3±2ms            |    0.99 | cube.CubeEquality.time_equality(True, False, 'coord_inequality')                            |
|          | 190±3ms              | 192±4ms             |    1.01 | cube.CubeEquality.time_equality(True, False, 'data_inequality')                             |
|          | 91.7±2μs             | 92.3±1μs            |    1.01 | cube.CubeEquality.time_equality(True, False, 'metadata_inequality')                         |
|          | 242±3ms              | 239±4ms             |    0.99 | cube.CubeEquality.time_equality(True, True, 'all_equal')                                    |
|          | 142±2ms              | 143±4ms             |    1    | cube.CubeEquality.time_equality(True, True, 'coord_inequality')                             |
|          | 271±6ms              | 266±4ms             |    0.98 | cube.CubeEquality.time_equality(True, True, 'data_inequality')                              |
|          | 419±10μs             | 398±10μs            |    0.95 | cube.CubeEquality.time_equality(True, True, 'metadata_inequality')                          |
|          | 881±9μs              | 873±5μs             |    0.99 | import_iris.Iris.time__concatenate                                                          |
|          | 188±1μs              | 188±1μs             |    1    | import_iris.Iris.time__constraints                                                          |
|          | 119±2μs              | 118±1μs             |    1    | import_iris.Iris.time__data_manager                                                         |
|          | 103±1μs              | 104±1μs             |    1    | import_iris.Iris.time__deprecation                                                          |
|          | 171±0.5μs            | 172±1μs             |    1.01 | import_iris.Iris.time__lazy_data                                                            |
|          | 1.00±0.01ms          | 987±10μs            |    0.98 | import_iris.Iris.time__merge                                                                |
|          | 77.2±0.3μs           | 75.2±0.7μs          |    0.97 | import_iris.Iris.time__representation                                                       |
|          | 645±7μs              | 623±8μs             |    0.97 | import_iris.Iris.time_analysis                                                              |
|          | 147±3μs              | 145±1μs             |    0.99 | import_iris.Iris.time_analysis__area_weighted                                               |
|          | 127±0.7μs            | 126±0.5μs           |    0.99 | import_iris.Iris.time_analysis__grid_angles                                                 |
|          | 265±3μs              | 260±2μs             |    0.98 | import_iris.Iris.time_analysis__interpolation                                               |
|          | 204±1μs              | 195±4μs             |    0.96 | import_iris.Iris.time_analysis__regrid                                                      |
|          | 114±0.8μs            | 113±2μs             |    0.99 | import_iris.Iris.time_analysis__scipy_interpolate                                           |
|          | 148±2μs              | 141±3μs             |    0.96 | import_iris.Iris.time_analysis_calculus                                                     |
|          | 354±4μs              | 340±4μs             |    0.96 | import_iris.Iris.time_analysis_cartography                                                  |
|          | 94.2±0.9μs           | 93.0±0.5μs          |    0.99 | import_iris.Iris.time_analysis_geomerty                                                     |
|          | 217±7μs              | 211±2μs             |    0.97 | import_iris.Iris.time_analysis_maths                                                        |
|          | 97.1±0.5μs           | 96.3±0.8μs          |    0.99 | import_iris.Iris.time_analysis_stats                                                        |
|          | 182±2μs              | 176±3μs             |    0.97 | import_iris.Iris.time_analysis_trajectory                                                   |
|          | 334±3μs              | 323±4μs             |    0.97 | import_iris.Iris.time_aux_factory                                                           |
|          | 82.7±0.6μs           | 82.2±2μs            |    0.99 | import_iris.Iris.time_common                                                                |
|          | 161±2μs              | 170±2μs             |    1.06 | import_iris.Iris.time_common_lenient                                                        |
|          | 1.56±0.02ms          | 1.56±0.02ms         |    1    | import_iris.Iris.time_common_metadata                                                       |
|          | 181±3μs              | 185±6μs             |    1.02 | import_iris.Iris.time_common_mixin                                                          |
|          | 1.18±0.01ms          | 1.20±0.02ms         |    1.02 | import_iris.Iris.time_common_resolve                                                        |
|          | 257±0.9μs            | 263±2μs             |    1.02 | import_iris.Iris.time_config                                                                |
|          | 137±0.7μs            | 135±0.7μs           |    0.99 | import_iris.Iris.time_coord_categorisation                                                  |
|          | 394±4μs              | 390±7μs             |    0.99 | import_iris.Iris.time_coord_systems                                                         |
|          | 836±7μs              | 829±20μs            |    0.99 | import_iris.Iris.time_coords                                                                |
|          | 745±8μs              | 761±30μs            |    1.02 | import_iris.Iris.time_cube                                                                  |
|          | 265±3μs              | 262±2μs             |    0.99 | import_iris.Iris.time_exceptions                                                            |
|          | 76.6±0.3μs           | 75.6±0.5μs          |    0.99 | import_iris.Iris.time_experimental                                                          |
|          | 213±3μs              | 208±2μs             |    0.98 | import_iris.Iris.time_fileformats                                                           |
|          | 263±5μs              | 264±10μs            |    1.01 | import_iris.Iris.time_fileformats__ff                                                       |
|          | 2.72±0.03ms          | 2.81±0.08ms         |    1.03 | import_iris.Iris.time_fileformats__ff_cross_references                                      |
|          | 78.1±1μs             | 77.4±0.4μs          |    0.99 | import_iris.Iris.time_fileformats__pp_lbproc_pairs                                          |
|          | 117±2μs              | 118±2μs             |    1.01 | import_iris.Iris.time_fileformats_abf                                                       |
|          | 88.1±1μs             | 86.2±1μs            |    0.98 | import_iris.Iris.time_fileformats_cf                                                        |
|          | 4.88±0.05ms          | 5.13±0.5ms          |    1.05 | import_iris.Iris.time_fileformats_dot                                                       |
|          | 72.5±0.4μs           | 73.0±2μs            |    1.01 | import_iris.Iris.time_fileformats_name                                                      |
|          | 254±2μs              | 255±2μs             |    1    | import_iris.Iris.time_fileformats_name_loaders                                              |
|          | 122±1μs              | 124±2μs             |    1.02 | import_iris.Iris.time_fileformats_netcdf                                                    |
|          | 160±3μs              | 163±2μs             |    1.02 | import_iris.Iris.time_fileformats_nimrod                                                    |
|          | 327±5μs              | 326±2μs             |    1    | import_iris.Iris.time_fileformats_nimrod_load_rules                                         |
|          | 824±10μs             | 834±20μs            |    1.01 | import_iris.Iris.time_fileformats_pp                                                        |
|          | 178±2μs              | 180±2μs             |    1.01 | import_iris.Iris.time_fileformats_pp_load_rules                                             |
|          | 132±0.6μs            | 136±2μs             |    1.03 | import_iris.Iris.time_fileformats_pp_save_rules                                             |
|          | 576±8μs              | 580±7μs             |    1.01 | import_iris.Iris.time_fileformats_rules                                                     |
|          | 231±4μs              | 230±3μs             |    1    | import_iris.Iris.time_fileformats_structured_array_identification                           |
|          | 82.4±0.5μs           | 82.5±0.9μs          |    1    | import_iris.Iris.time_fileformats_um                                                        |
|          | 159±2μs              | 163±3μs             |    1.02 | import_iris.Iris.time_fileformats_um__fast_load                                             |
|          | 140±3μs              | 143±1μs             |    1.02 | import_iris.Iris.time_fileformats_um__fast_load_structured_fields                           |
|          | 74.6±0.8μs           | 72.9±0.4μs          |    0.98 | import_iris.Iris.time_fileformats_um__ff_replacement                                        |
|          | 79.8±0.5μs           | 79.8±0.4μs          |    1    | import_iris.Iris.time_fileformats_um__optimal_array_structuring                             |
|          | 990±5μs              | 1.01±0.01ms         |    1.02 | import_iris.Iris.time_fileformats_um_cf_map                                                 |
|          | 165±4μs              | 162±1μs             |    0.99 | import_iris.Iris.time_io                                                                    |
|          | 196±5μs              | 190±6μs             |    0.97 | import_iris.Iris.time_io_format_picker                                                      |
|          | 206±0.9μs            | 208±3μs             |    1.01 | import_iris.Iris.time_iris                                                                  |
|          | 130±1μs              | 130±1μs             |    1.01 | import_iris.Iris.time_iterate                                                               |
|          | 8.81±0.02ms          | 8.87±0.2ms          |    1.01 | import_iris.Iris.time_palette                                                               |
|          | 1.91±0.03ms          | 1.95±0.05ms         |    1.02 | import_iris.Iris.time_plot                                                                  |
|          | 262±20μs             | 278±40μs            |    1.06 | import_iris.Iris.time_quickplot                                                             |
|          | 2.26±0.01ms          | 2.24±0.02ms         |    1    | import_iris.Iris.time_std_names                                                             |
|          | 1.89±0.01ms          | 1.89±0.01ms         |    1    | import_iris.Iris.time_symbols                                                               |
|          | 263±3ms              | 261±2ms             |    1    | import_iris.Iris.time_tests                                                                 |
|          | 255±1μs              | 256±3μs             |    1    | import_iris.Iris.time_third_party_cartopy                                                   |
|          | 5.14±0.03ms          | 5.26±0.09ms         |    1.02 | import_iris.Iris.time_third_party_cf_units                                                  |
|          | 125±0.4μs            | 125±1μs             |    1    | import_iris.Iris.time_third_party_cftime                                                    |
|          | 2.59±0.01ms          | 2.59±0.06ms         |    1    | import_iris.Iris.time_third_party_matplotlib                                                |
|          | 542±10μs             | 532±20μs            |    0.98 | import_iris.Iris.time_third_party_numpy                                                     |
|          | 175±0.8μs            | 175±0.7μs           |    1    | import_iris.Iris.time_third_party_scipy                                                     |
|          | 102±2μs              | 103±0.9μs           |    1.01 | import_iris.Iris.time_time                                                                  |
|          | 865±7μs              | 865±7μs             |    1    | import_iris.Iris.time_util                                                                  |
|          | 64.7±0.4μs           | 65.3±0.6μs          |    1.01 | iterate.IZip.time_izip                                                                      |
|          | 10.1±0.04ms          | 10.4±0.2ms          |    1.03 | load.LoadAndRealise.time_load((1280, 960, 5), False, 'FF')                                  |
|          | 15.6±0.7ms           | 16.2±0.3ms          |    1.04 | load.LoadAndRealise.time_load((1280, 960, 5), False, 'NetCDF')                              |
|          | 10.1±0.2ms           | 10.2±0.2ms          |    1    | load.LoadAndRealise.time_load((1280, 960, 5), False, 'PP')                                  |
|          | 10.1±0.09ms          | 10.5±0.07ms         |    1.04 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'FF')                                   |
|          | 12.8±0.3ms           | 13.4±0.2ms          |    1.04 | load.LoadAndRealise.time_load((1280, 960, 5), True, 'NetCDF')                               |
|          | 10.1±0.3ms           | 10.1±0.3ms          |    1    | load.LoadAndRealise.time_load((1280, 960, 5), True, 'PP')                                   |
|          | 1.53±0.02s           | 1.54±0.01s          |    1    | load.LoadAndRealise.time_load((2, 2, 1000), False, 'FF')                                    |
|          | 11.5±0.3ms           | 12.3±0.2ms          |    1.07 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'NetCDF')                                |
|          | 1.50±0.02s           | 1.51±0.01s          |    1.01 | load.LoadAndRealise.time_load((2, 2, 1000), False, 'PP')                                    |
|          | 1.52±0.01s           | 1.53±0.01s          |    1    | load.LoadAndRealise.time_load((2, 2, 1000), True, 'FF')                                     |
|          | 11.7±0.2ms           | 12.1±0.5ms          |    1.04 | load.LoadAndRealise.time_load((2, 2, 1000), True, 'NetCDF')                                 |
|          | 1.52±0.01s           | 1.51±0.01s          |    0.99 | load.LoadAndRealise.time_load((2, 2, 1000), True, 'PP')                                     |
|          | 5.32±0.03ms          | 5.41±0.2ms          |    1.02 | load.LoadAndRealise.time_load((50, 50, 2), False, 'FF')                                     |
|          | 11.3±0.2ms           | 11.6±0.2ms          |    1.03 | load.LoadAndRealise.time_load((50, 50, 2), False, 'NetCDF')                                 |
|          | 5.29±0.2ms           | 5.24±0.08ms         |    0.99 | load.LoadAndRealise.time_load((50, 50, 2), False, 'PP')                                     |
|          | 5.38±0.07ms          | 5.48±0.08ms         |    1.02 | load.LoadAndRealise.time_load((50, 50, 2), True, 'FF')                                      |
|          | 11.2±0.2ms           | 12.2±0.3ms          |    1.09 | load.LoadAndRealise.time_load((50, 50, 2), True, 'NetCDF')                                  |
|          | 5.27±0.06ms          | 5.21±0.05ms         |    0.99 | load.LoadAndRealise.time_load((50, 50, 2), True, 'PP')                                      |
|          | 23.0±1ms             | 22.4±1ms            |    0.98 | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'FF')                               |
|          | 21.6±0.8ms           | 21.6±0.9ms          |    1    | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'NetCDF')                           |
|          | 12.7±0.9ms           | 12.4±0.9ms          |    0.97 | load.LoadAndRealise.time_realise((1280, 960, 5), False, 'PP')                               |
|          | 29.5±1ms             | 29.2±0.5ms          |    0.99 | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'FF')                                |
|          | 70.5±0.1ms           | 70.3±0.2ms          |    1    | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'NetCDF')                            |
|          | 28.6±1ms             | 28.9±0.4ms          |    1.01 | load.LoadAndRealise.time_realise((1280, 960, 5), True, 'PP')                                |
|          | 667±7ms              | 663±5ms             |    0.99 | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'FF')                                 |
|          | 3.69±0.1ms           | 3.52±0.2ms          |    0.95 | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'NetCDF')                             |
|          | 662±2ms              | 663±4ms             |    1    | load.LoadAndRealise.time_realise((2, 2, 1000), False, 'PP')                                 |
|          | 662±3ms              | 660±2ms             |    1    | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'FF')                                  |
|          | 3.72±0.2ms           | 3.78±0.2ms          |    1.02 | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'NetCDF')                              |
|          | 656±6ms              | 661±5ms             |    1.01 | load.LoadAndRealise.time_realise((2, 2, 1000), True, 'PP')                                  |
|          | 2.27±0.07ms          | 2.28±0.2ms          |    1    | load.LoadAndRealise.time_realise((50, 50, 2), False, 'FF')                                  |
|          | 3.80±0.06ms          | 3.72±0.2ms          |    0.98 | load.LoadAndRealise.time_realise((50, 50, 2), False, 'NetCDF')                              |
|          | 2.47±0.1ms           | 2.31±0.07ms         |    0.93 | load.LoadAndRealise.time_realise((50, 50, 2), False, 'PP')                                  |
|          | 2.36±0.09ms          | 2.34±0.09ms         |    0.99 | load.LoadAndRealise.time_realise((50, 50, 2), True, 'FF')                                   |
|          | 3.66±0.1ms           | 3.66±0.2ms          |    1    | load.LoadAndRealise.time_realise((50, 50, 2), True, 'NetCDF')                               |
|          | 2.33±0.06ms          | 2.38±0.07ms         |    1.02 | load.LoadAndRealise.time_realise((50, 50, 2), True, 'PP')                                   |
|          | 355±9ms              | 354±2ms             |    1    | load.ManyCubes.time_many_cube_load                                                          |
|          | 83.5±2ms             | 84.1±0.9ms          |    1.01 | load.ManyVars.time_many_var_load                                                            |
|          | 10.2±0.2ms           | 10.7±0.3ms          |    1.05 | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'FF')                            |
|          | 10.2±0.3ms           | 10.2±0.2ms          |    1    | load.STASHConstraint.time_stash_constraint((1280, 960, 5), 'PP')                            |
|          | 1.55±0.02s           | 1.55±0.01s          |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'FF')                              |
|          | 1.52±0.01s           | 1.54±0.01s          |    1.01 | load.STASHConstraint.time_stash_constraint((2, 2, 1000), 'PP')                              |
|          | 5.48±0.2ms           | 5.47±0.04ms         |    1    | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'FF')                                 |
|          | 5.23±0.2ms           | 5.26±0.05ms         |    1.01 | load.STASHConstraint.time_stash_constraint((2, 2, 2), 'PP')                                 |
|          | 9.32±0.4ms           | 9.38±0.2ms          |    1.01 | load.StructuredFF.time_structured_load((1280, 960, 5), False)                               |
|          | 5.74±0.2ms           | 5.64±0.2ms          |    0.98 | load.StructuredFF.time_structured_load((1280, 960, 5), True)                                |
|          | 1.52±0.01s           | 1.55±0.02s          |    1.02 | load.StructuredFF.time_structured_load((2, 2, 1000), False)                                 |
|          | 431±6ms              | 439±2ms             |    1.02 | load.StructuredFF.time_structured_load((2, 2, 1000), True)                                  |
|          | 4.42±0.08ms          | 4.56±0.08ms         |    1.03 | load.StructuredFF.time_structured_load((2, 2, 2), False)                                    |
|          | 4.28±0.2ms           | 4.51±0.2ms          |    1.05 | load.StructuredFF.time_structured_load((2, 2, 2), True)                                     |
|          | 158±2ms              | 160±2ms             |    1.02 | load.TimeConstraint.time_time_constraint(20, 'FF')                                          |
|          | 14.8±0.4ms           | 15.1±0.3ms          |    1.02 | load.TimeConstraint.time_time_constraint(20, 'NetCDF')                                      |
|          | 158±1ms              | 157±3ms             |    0.99 | load.TimeConstraint.time_time_constraint(20, 'PP')                                          |
|          | 31.7±0.8ms           | 33.2±0.6ms          |    1.04 | load.TimeConstraint.time_time_constraint(3, 'FF')                                           |
|          | 13.9±0.7ms           | 14.3±0.2ms          |    1.03 | load.TimeConstraint.time_time_constraint(3, 'NetCDF')                                       |
|          | 31.3±0.7ms           | 32.8±0.2ms          |    1.05 | load.TimeConstraint.time_time_constraint(3, 'PP')                                           |
|          | 17.6±0.4ms           | 19.1±0.8ms          |    1.08 | load.ugrid.BasicLoading.time_load_file(1)                                                   |
|          | 48.4±1ms             | 50.7±0.9ms          |    1.05 | load.ugrid.BasicLoading.time_load_file(200000)                                              |
|          | 8.23±0.3ms           | 8.61±0.4ms          |    1.05 | load.ugrid.BasicLoading.time_load_mesh(1)                                                   |
|          | 16.2±0.6ms           | 16.9±0.3ms          |    1.05 | load.ugrid.BasicLoading.time_load_mesh(200000)                                              |
|          | 17.7±0.6ms           | 19.1±0.4ms          |    1.07 | load.ugrid.BasicLoadingTime.time_load_file(1)                                               |
|          | 18.2±0.7ms           | 19.1±0.5ms          |    1.05 | load.ugrid.BasicLoadingTime.time_load_file(200000)                                          |
|          | 7.90±0.3ms           | 8.67±0.5ms          |    1.1  | load.ugrid.BasicLoadingTime.time_load_mesh(1)                                               |
|          | 11.2±0.7ms           | 11.1±0.2ms          |    0.99 | load.ugrid.BasicLoadingTime.time_load_mesh(200000)                                          |
|          | 20.2±0.5ms           | 21.1±0.2ms          |    1.04 | load.ugrid.Callback.time_load_file_callback(1)                                              |
|          | 60.9±2ms             | 62.8±1ms            |    1.03 | load.ugrid.Callback.time_load_file_callback(200000)                                         |
|          | 20.4±0.3ms           | 21.2±0.2ms          |    1.04 | load.ugrid.CallbackTime.time_load_file_callback(1)                                          |
|          | 22.7±0.5ms           | 22.9±0.7ms          |    1.01 | load.ugrid.CallbackTime.time_load_file_callback(200000)                                     |
|          | 3.85±0.2ms           | 3.94±0.09ms         |    1.02 | load.ugrid.DataRealisation.time_realise_data(10000)                                         |
|          | 4.92±0.2ms           | 5.09±0.1ms          |    1.03 | load.ugrid.DataRealisation.time_realise_data(200000)                                        |
|          | 52.4±3ms             | 54.2±3ms            |    1.03 | load.ugrid.DataRealisationTime.time_realise_data(10000)                                     |
|          | 1.05±0.03s           | 1.06±0.03s          |    1.01 | load.ugrid.DataRealisationTime.time_realise_data(200000)                                    |
|          | 1.08±0.02s           | 1.13±0.04s          |    1.05 | merge_concat.Concatenate.time_concatenate(False)                                            |
|          | 446±6ms              | 465±7ms             |    1.04 | merge_concat.Concatenate.time_concatenate(True)                                             |
|          | 2.21±0G              | 2.21±0G             |    1    | merge_concat.Concatenate.tracemalloc_concatenate(False)                                     |
|          | 71.7±4M              | 71.7±2M             |    1    | merge_concat.Concatenate.tracemalloc_concatenate(True)                                      |
|          | 33.3±1ms             | 36.0±1ms            |    1.08 | merge_concat.Merge.time_merge                                                               |
|          | 126±0.03M            | 126±0.03M           |    1    | merge_concat.Merge.tracemalloc_merge                                                        |
|          | 5.68±0.1ms           | 5.90±0.09ms         |    1.04 | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(50)              |
|          | 148±3ms              | 145±0.8ms           |    0.98 | mesh.utils.regions_combine.CombineRegionsComputeRealData.time_compute_data(500)             |
|          | 769±3k               | 768±1k              |    1    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(50)       |
|          | 60.2±0M              | 60.2±0M             |    1    | mesh.utils.regions_combine.CombineRegionsComputeRealData.tracemalloc_compute_data(500)      |
|          | 18.8±0.5ms           | 20.1±0.4ms          |    1.07 | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(50)           |
|          | 22.6±0.4ms           | 22.7±0.6ms          |    1    | mesh.utils.regions_combine.CombineRegionsCreateCube.time_create_combined_cube(500)          |
|          | 24.6±0M              | 25±0M               |    1.02 | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(500)   |
|          | 112±3ms              | 122±4ms             |    1.08 | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(50)         |
|          | 406±8ms              | 414±10ms            |    1.02 | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.time_stream_file2file(500)        |
|          | 1.51±0.03M           | 1.51±0.04M          |    1    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(50)  |
|          | 96.6±0.04M           | 96.5±0.02M          |    1    | mesh.utils.regions_combine.CombineRegionsFileStreamedCalc.tracemalloc_stream_file2file(500) |
|          | 76.7±2ms             | 77.0±4ms            |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(50)                             |
|          | 363±9ms              | 366±3ms             |    1.01 | mesh.utils.regions_combine.CombineRegionsSaveData.time_save(500)                            |
|          | 1.42±0.04M           | 1.39±0.03M          |    0.98 | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(50)                      |
|          | 96.4±0.06M           | 96.5±0.02M          |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.tracemalloc_save(500)                     |
|          | 2.175361             | 2.175361            |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(50)                  |
|          | 216.01536099999998   | 216.01536099999998  |    1    | mesh.utils.regions_combine.CombineRegionsSaveData.track_filesize_saved(500)                 |
|          | 6.41±0.05ms          | 6.58±0.08ms         |    1.03 | plot.AuxSort.time_aux_sort                                                                  |
|          | 55.0±0.5ms           | 55.9±1ms            |    1.02 | regridding.CurvilinearRegridding.time_regrid_pic                                            |
|          | 136±3M               | 136±3M              |    1    | regridding.CurvilinearRegridding.tracemalloc_regrid_pic                                     |
|          | 102±6ms              | 106±7ms             |    1.04 | regridding.HorizontalChunkedRegridding.time_regrid_area_w                                   |
|          | 54.0±1ms             | 54.8±1ms            |    1.02 | regridding.HorizontalChunkedRegridding.time_regrid_area_w_new_grid                          |
|          | 107±0.1M             | 107±0.1M            |    1    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w                            |
|          | 147±0.03M            | 147±0.03M           |    1    | regridding.HorizontalChunkedRegridding.tracemalloc_regrid_area_w_new_grid                   |
|          | 5.13±0.4ms           | 5.25±0.2ms          |    1.02 | save.NetcdfSave.time_netcdf_save_cube(50, False)                                            |
|          | 82.6±1ms             | 83.3±1ms            |    1.01 | save.NetcdfSave.time_netcdf_save_cube(50, True)                                             |
|          | 21.7±1ms             | 21.0±0.7ms          |    0.97 | save.NetcdfSave.time_netcdf_save_cube(600, False)                                           |
|          | 286±3ms              | 297±8ms             |    1.04 | save.NetcdfSave.time_netcdf_save_cube(600, True)                                            |
|          | 62.0±0.6ms           | 63.7±1ms            |    1.03 | save.NetcdfSave.time_netcdf_save_mesh(50, True)                                             |
|          | 252±2ms              | 259±5ms             |    1.02 | save.NetcdfSave.time_netcdf_save_mesh(600, True)                                            |
|          | 34±1k                | 33.8±1k             |    0.99 | save.NetcdfSave.tracemalloc_netcdf_save(50, False)                                          |
|          | 1.8±0.2M             | 1.81±0.2M           |    1    | save.NetcdfSave.tracemalloc_netcdf_save(50, True)                                           |
|          | 34.1±1k              | 33.8±1k             |    0.99 | save.NetcdfSave.tracemalloc_netcdf_save(600, False)                                         |
|          | 225±10M              | 225±30M             |    1    | save.NetcdfSave.tracemalloc_netcdf_save(600, True)                                          |
|          | 41.6±0.3ms           | 41.9±0.7ms          |    1.01 | stats.PearsonR.time_lazy                                                                    |
|          | 10.2±0.08ms          | 10.2±0.2ms          |    1    | stats.PearsonR.time_real                                                                    |
|          | 29.3±0.7M            | 29±0.5M             |    0.99 | stats.PearsonR.tracemalloc_lazy                                                             |
|          | 18.3±0M              | 18.3±0M             |    1    | stats.PearsonR.tracemalloc_real                                                             |
|          | 24.3±0.4ms           | 25.6±0.5ms          |    1.06 | trajectory.TrajectoryInterpolation.time_trajectory_linear                                   |
|          | 82.9±0.5ms           | 83.1±0.2ms          |    1    | trajectory.TrajectoryInterpolation.time_trajectory_nearest                                  |
|          | 17.6±0.01M           | 17.6±0.01M          |    1    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_linear                            |
|          | 7.74±0.01M           | 7.74±0.01M          |    1    | trajectory.TrajectoryInterpolation.tracemalloc_trajectory_nearest                           |

Benchmarks that have got worse:

| Change   | Before [426d7189]    | After [91c9407e]    |   Ratio | Benchmark (Parameter)                                                                    |
|----------|----------------------|---------------------|---------|------------------------------------------------------------------------------------------|
| +        | 820±2k               | 1.23±0M             |    1.5  | mesh.utils.regions_combine.CombineRegionsCreateCube.tracemalloc_create_combined_cube(50) |
| +        | 1.15±0.1μs           | 1.89±0.2μs          |    1.64 | save.NetcdfSave.time_netcdf_save_mesh(50, False)                                         |
| +        | 2.08±0.1μs           | 3.24±0.3μs          |    1.55 | save.NetcdfSave.time_netcdf_save_mesh(600, False)                                        |

Generated by GHA run 39973248966

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Agentic benchmark_this Request that this pull request be benchmarked to check if it introduces performance shifts Type: Feature Branch Highlight this for a feature branch

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants