perf: enable module flatten/unflatten fastpath - #1117
Conversation
|
So Equinox looks to avoid special-casing any module methods. In particular it may be the case that someone already has a method called (The style you have here is actually what we used to have back in the distant early days of Equinox, and moved away from it.) I'm not super what a better alternative is. Perhaps JAX might allow re-registering a PyTree with different flatten/unflatten rules. |
|
What about special-casing equinox-prefixed versions of these methods? |
|
@patrick-kidger if I can't crack #1119 (or even if I can, IMO it'd be nice to be able to customize) would |
|
So I'm really leaning against adding something like that to Supposing #1119 comes good, what would be your use-case? |
|
E.g. not have the |
|
And with #1119, it would be cool to show the user what the (un)flattening code is doing by attaching the generated functions to the classes! class ModuleMeta:
def __new__(...):
cls._eqx_tree_flatten, cls._eqx_tree_unflatten = generate_functions(cls)
jax.tree_util.register_stuff(cls._eqx_tree_flatten, cls._eqx_tree_unflatten)
class MyClass(eqx.Module):
attr1: float
MyClass._eqx_tree_flatten?
>>> def flatten(self):
... return (self.attr1,), () |
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
|
I've rebased this PR on #1119. New approximate timings on that performance notebook:
This PR allows for an ~88% improvement ((12.5 - 8.3) / (12.5 - 7.7)) if a power user wants to write these methods. The default (from #1119) is a ~50% improvement. |
|
@patrick-kidger, the before I rebase this PR and fix conflicts, I wanted to touch base r.e. the general idea. The idea is that users can specify # Allow for classes to define their own (un)flattening procedures.
unflatten = getattr(cls, "_eqx_tree_unflatten", False)
flatten_with_k = getattr(cls, "_eqx_tree_flatten_with_keys", False)
flatten = getattr(cls, "_eqx_tree_flatten", None)
# Using `None` since it's the default value for
# register_pytree_with_keys and has a truthy value of False.
if unflatten and flatten_with_k:
jtu.register_pytree_with_keys(
cls,
flatten_with_keys=flatten_with_k,
flatten_func=flatten,
unflatten_func=unflatten,
)
elif unflatten and flatten:
jtu.register_pytree_node(cls, flatten, unflatten)
else:
# Partial or missing definition: fall back to automatic registration.
if unflatten or flatten_with_k or flatten:
warnings.warn(
"Class contains partial PyTree definition. "
"Falling back to automatic registration.",
stacklevel=2,
)
# Generate optimized flatten/unflatten functions
flatten_func, flatten_with_keys_func, unflatten_func = (
_generate_flatten_functions(cls, fields)
)
jtu.register_pytree_with_keys(
cls,
flatten_with_keys=flatten_with_keys_func,
flatten_func=flatten_func,
unflatten_func=ft.partial(unflatten_func, cls),
) |
|
So I think still hold to the idea from here:
if a user is willing to create custom flatten/unflatten then they should probably be using direct pytree registration, instead of using a Conversely, if a user is using |
I just wish there were a good way to unregister things in JAX. Looking under the hood at the registration functions, it's such a complex, variable set of dicts that I imagine it's hard to do. So offering alt registrations here is much easier. |
|
I think the correct custom-(un)flattening path is to use JAX's custom pytree-registration directly, and not to use |
|
Methods-as-pytrees, the downstream ecosystem that does The 2 ideas I have are:
I'm very happy to close this PR in favour of the other idea, if that'd be better. |
This PR enables power users to write their own flattening/unflattening procedures.
The advantage is that
Modulecan be sped up to be as fast as custom pytree structures and jax arrays.I'm attaching a Jupyter Notebook showing that in this example we achieve a ~50% speedup, which is ~100% of the overhead, making raw arrays, simple pytrees, and
Moduleall equivalently fast.I'm happy to add documentation / tests.
overhead.ipynb.zip