-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable optional user traits on serializers #282
Enable optional user traits on serializers #282
Conversation
Also removed deprecated Kokkos header includes.
Conflicts: src/checkpoint/container/view_equality.h tests/unit/test_commons.h
Conflicts: src/CMakeLists.txt src/checkpoint/serializers/serializers_headers.h src/checkpoint/serializers/stream_serializer.h
src/checkpoint/traits/user_traits.h
Outdated
|
||
template<typename Trait, template<typename...> typename SerializerT, typename... UserTraits> | ||
auto& withoutTrait(SerializerT<UserTraits...>& obj){ | ||
return reinterpret_cast<typename removeTrait<Trait, SerializerT, UserTraits...>::type&>(obj); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to do this without the reinterpret_cast
. I think this is UB
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's some difficulty since the serializers are expected to be used by the same reference. I've been looking into it, and technically any mechanism we use to refer to the same memory space by the two different types is undefined. The user traits aren't used to change any members, so we should be good on memory positioning, but strict aliasing rules could cause issues w.r.t. compilers keeping member variables in different registers.
I haven't tested, but I think we ensure correct reads/writes by:
- Require all serializers be stored as volatile variables, or all non-reference/non-pointer members of serializers be declared volatile
- Require all serializer types declared with the may_alias trait, which should be available on gcc/clang/icc. I expect this would have more side effects on compiler optimizations.
Any thoughts?
@nmm0 @lifflander I've been considering the issues we discussed a while ago about virtual serialization - could you take a glance at this and let me know if it seems viable to try implementing? The problem was that, for classes with inheritance, we need to call a virtual function to get to the appropriate serialize function. Since virtual functions can't be templated, we have to pre-register each serializer's type and do some shenanigans to cast an untyped object to the right serializer and go from there. The UserTraits aren't known beforehand, so we can't register the types of the traited serializers and then can't virtual serialize with the traits. Based on a fun trick from this blog, I've worked out creating an insertable type list. Types add themselves to the list of inheritors for the type they inherit from, the inherited class does nothing. Then the dispatcher can use that to build templated calls for trying to convert to any base classes.
|
Seeing this revised after a while, this has gone way down a rabbit hole of type system magic. Is there such a pressing concern for performance or compile-time checking that these sorts of traits can't be tracked as some sort of dynamic runtime attribute? E.g. a bitset with indices assigned by position of the attribute in the set of requested attributes? Heck, those attributes could be types in a registry, if you wanted. Then dispatch to the specialized serialization logic could happen based on whether a serializer has a given bit set. |
The main motivation for using type-traiting is to allow non-intrusive 3rd party serialization hooks. For example, KokkosResilience could serialize (or just size) a generic object with a KokkosResilience::ProxyReferences trait and gain fully-typed references to any VT Proxies that object holds as a member. During deserialization for recovery, we could verify that any referenced proxies are available and have the same proxy ID, and if not either recover the other object or replace the proxy with the updated ID. The hook is found by the compiler to be called despite the object's type or the invocation namespace because the serializer has a KokkosResilience namespaced template parameter. |
Superseded by #345 |
UserTraits
What: Serialize/deserialize calls are now also templated on
UserTraits...
, which is passed on to the base serializer. Some helpers for adding/removing and checking for the existence of trait are added as well.Why: User traits allow arbitrary customization of serialization behavior based on user-defined terms. This enables more flexibility in checkpointing than an extra boolean variable, and is more clear in user-space code than a pre-defined variable name (EG:
VT::RestoreProxies
instead ofs.isCheckpointRecovery()
)Serialization overriding
What: When a non-intrusive serializer and an intrusive serializer are both found, call the non-intrusive serializer.
Why: Non-intrusive serializers can follow up with calls to the intrusive copy, meaning non-intrusive serializers can be used as a form of serialization subscriber. Particularly with user traits, we can define non-intrusive serializers which only run when a particular trait is used on a given object, allowing third parties (E.G. KokkosResilience) to perform custom logic before serialization of arbitrary objects. This can also be used to generally attach to serialization of objects for the purposes of tracing, etc.
For examples of these changes in action, see checkpoint_example_user_traits.cc/hpp