Skip to content
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

Core type system #697

Merged
merged 24 commits into from
May 4, 2023
Merged

Conversation

magnatelee
Copy link
Contributor

This PR adds a common type system to Legate core. The type system supports primitive types, fixed size array types, and struct types. Struct types can optionally be aligned, which is often what the C++ counterpart expects. As a part of the refactoring, the ugly handling of tuples in legate::Scalar has been improved with the new fixed-size array type so everything is under the same type system.

@magnatelee magnatelee added the category:improvement PR introduces an improvement and will be classified as such in release notes label Apr 21, 2023
@magnatelee
Copy link
Contributor Author

@manopapad @jjwilke @bryevdv this PR is fairly big, so I thought it'd be helpful to have more than one pair of eyes on the code.

Copy link
Contributor

@bryevdv bryevdv left a comment

Choose a reason for hiding this comment

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

Great improvement

src/core/legate_c.h Outdated Show resolved Hide resolved
legate/core/runtime.py Outdated Show resolved Hide resolved
src/core/utilities/deserializer.inl Outdated Show resolved Hide resolved
src/core/type/type_info.h Outdated Show resolved Hide resolved
src/core/runtime/runtime.cc Outdated Show resolved Hide resolved
legate/core/types.pyi Outdated Show resolved Hide resolved
@magnatelee magnatelee requested a review from bryevdv April 22, 2023 22:58
Copy link

@jjwilke jjwilke left a comment

Choose a reason for hiding this comment

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

LGTM. A couple nits. Main question is on Store lifetimes related to reference_wrapper and std::unique_ptr

@@ -1,5 +1,5 @@
legate_root=`python -c 'import legate.install_info as i; from pathlib import Path; print(Path(i.libpath).parent.resolve())'`
echo "Using Legate at $legate_root"
cmake -S . -B build -D legate_core_ROOT=$legate_root
cmake --build build
cmake --build build -j 8
Copy link

Choose a reason for hiding this comment

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

I would call --parallel instead of -j 8

Copy link
Contributor Author

Choose a reason for hiding this comment

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

src/core/utilities/deserializer.inl Show resolved Hide resolved
@@ -220,7 +220,7 @@ void StoreMapping::populate_layout_constraints(
{
StoreMapping mapping{};
mapping.policy = InstanceMappingPolicy::default_policy(target, exact);
mapping.stores.push_back(store);
mapping.stores.emplace_back(store);
Copy link

Choose a reason for hiding this comment

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

I'm a bit uncomfortable with the change here. Store has changed from a lightweight, copyable handle to a move-only class. The reference_wrapper stuff here has potential lifetime issues. I'm sure the current implementation is correct, but I don't love creating a vector of references that returns from a function.

Can these type objects just be statics? They are going to get reused quite a bit, yeah? Would it be better to move from unique_ptr<Type> to Type* where Type is an object with static lifetime?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that we don't want to pass around pointers in the distributed setting, so Type* needs to be deserialized somehow in the mapper. We can certainly make a singleton type object for each distinct type, but then we need a centralized registry of Type* objects for the deserializer (so it can recover a Type* from its serialized form) , which would then introduce synchronization between the control code registering types and every deserialization attempt. I don't actually want to introduce this. Though I don't like how it's set up, I feel it's reasonable because store mappings cannot exist without stores nor can they outlive them.

Legion::Runtime::perform_registration_callback(
detail::invoke_legate_registration_callback<CALLBACK>, true /*global*/);
auto runtime = Runtime::get_runtime();
if (runtime->is_in_callback())
Copy link

Choose a reason for hiding this comment

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

What would cause multiple callbacks to get invoked recursively? Also - can we guarantee that perform_registration_callback will actually invoke the callback? Doesn't it sometimes defer the callback?

If Legion defers the callback until later, it might invoke the callback when runtime->is_in_callback is false?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Callbacks don't get deferred with Legion::Runtime::perform_registration_callback; it's add_registration_callback that defers them.

@magnatelee magnatelee requested a review from jjwilke May 3, 2023 20:39
Copy link

@jjwilke jjwilke left a comment

Choose a reason for hiding this comment

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

LGTM

@magnatelee magnatelee merged commit 149fa50 into nv-legate:branch-23.05 May 4, 2023
@magnatelee magnatelee deleted the core-type-system branch May 4, 2023 00:12
) except+


cdef Dtype from_ptr(Type* ty):
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be possible to use forwarding references here, similar to https://github.com/cython/cython/blob/master/tests/run/cpp_forwarding_ref.pyx, to avoid having to release() then reset() the unique_ptrs?

def serialize(self, buf) -> None:
buf.pack_32bit_int(self.code)
buf.pack_32bit_int(self.uid)
buf.pack_32bit_int(self.num_elements())
Copy link
Contributor

Choose a reason for hiding this comment

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

Technically buf.pack_32bit_uint, but I doubt it matters.

buf.pack_32bit_int(self.code)
num_fields = self.num_fields()
buf.pack_32bit_int(self.uid)
buf.pack_32bit_int(num_fields)
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly technically buf.pack_32bit_uint

@@ -49,11 +49,10 @@ class Scalar {
* @brief Creates a shared `Scalar` with an existing allocation. The caller is responsible
* for passing in a sufficiently big allocation.
*
* @param tuple If true, the allocation contains a tuple of scalars.
* @param code Type code of the scalar(s)
Copy link
Contributor

Choose a reason for hiding this comment

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

Update argument doc

*/
bool is_tuple() const { return tuple_; }
const Type* type() const { return type_.get(); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Could probably return a const Type& here, like Store.type() does, to avoid exposing the pointer.

madsbk added a commit to madsbk/kvikio that referenced this pull request May 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category:improvement PR introduces an improvement and will be classified as such in release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants