Skip to content

Dedup device-mesh serialization in NamedShardingMetadata.to_serialized_string #3494

Description

@anveshjayakrishna

Summary

NamedShardingMetadata.to_serialized_string serializes the device mesh via dataclasses.asdict(self.device_mesh) (sharding.py#L261), and it runs once per array being saved. A checkpoint save typically has many arrays that all share the same mesh, so this re-walks and deep-copies the identical mesh once per array - O(num_arrays) redundant work that grows with mesh size and array count, and can become a significant fraction of save time at scale.

Proposal

Since all arrays in a save share one mesh, the conversion can be memoized so the deep copy happens once per distinct mesh instead of once per array:

# Build one shared DeviceMetadataMesh per distinct mesh
@classmethod
@functools.lru_cache(maxsize=128)
def from_jax_mesh(cls, mesh: jax.sharding.Mesh) -> Optional["DeviceMetadataMesh"]:
    ...
    
# Memoize the asdict deep-copy on that shared instance
@functools.cached_property
def as_serialized_dict(self) -> dict[str, Any]:
    return dataclasses.asdict(self)

# to_serialized_string uses the cached dict instead of asdict per call
sharding_data[_DEVICE_MESH] = self.device_mesh.as_serialized_dict

The two work together: from_jax_mesh returns a single shared instance per distinct mesh, which lets as_serialized_dict memoize the deep copy exactly once — collapsing O(num_arrays) copies to O(num_unique_meshes).

Output is byte-identical to dataclasses.asdict, so existing checkpoints and the restore path are unaffected. The cache is stored on the instance (cached_property in __dict__), leaving dataclasses.asdict / __eq__ semantics unchanged.

Questions for maintainers

  • Is memoizing the mesh serialization this way acceptable in general?
  • lru_cache on from_jax_mesh keyed by jax.sharding.Mesh relies on Mesh hashability/equality — is that a safe assumption across backends?
  • Any concern with the shared-instance semantics or cache lifetime (e.g. long-lived meshes retained by the lru_cache) that we should account for?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions