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

[Rust] Unify types between bindings and pure Rust impl #2616

Merged
merged 17 commits into from
Apr 3, 2019

Conversation

nhynes
Copy link
Member

@nhynes nhynes commented Feb 18, 2019

This PR unifies the types (currently) common to runtime and frontend:

  • share c_runtime_api.rs (removed need for tvm-sys)
  • TVM(Arg|Ret)?Value
  • TVMType, TVMContext

Still TODO (but low priority since they're not shared):

  • PackedFunc
  • TVMContext
  • TVMByteArray

cc @ehsanmok @kazimuth for review

@nhynes nhynes requested review from jroesch and tqchen February 18, 2019 01:40
@ehsanmok
Copy link
Contributor

ehsanmok commented Feb 18, 2019

@nhynes Thanks a lot for PR! I'll start the review as soon as I can.

When the number of changes is a lot, could you rebase all of your commits in one commit to see all the changes at once?

Another thing in here you said for runtime

bindgen doesn't work when the target is exotic (e.g., wasm or sgx)

now you're using it? (that was the reason I kept tvm-sys though)

@nhynes
Copy link
Member Author

nhynes commented Feb 18, 2019

number of changes is a lot, could you rebase all of your commits in one commit

The github PR diff shows all of the changes at once. Squashing commits before merging defeats the purpose of creating logically atomic changesets.

bindgen

Right. The simple solution is to check in c_runtime_api.rs. I'll see if bindgen works yet with non-host targets.

Copy link
Contributor

@ehsanmok ehsanmok left a comment

Choose a reason for hiding this comment

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

Great set of changes contributing to more readability for idiomatic Rust :)

Please add a README to runtime and show some snippets from tests and the overall introductions to runtime. For better documentations of runtime, consider adding a sentence at the module level as well.

/// assert_eq!(String::try_from(t).unwrap(), s);
/// ```
pub struct TVMRetValue {
pub value: TVMValue,
Copy link
Contributor

Choose a reason for hiding this comment

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

You've previously used prim_val: i64 while I used TVMValue and you were confident that I should change it to i64 but now you're back to TVMValue for POD representations! I still think usize should fit.

Copy link
Member Author

Choose a reason for hiding this comment

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

this is temporary. TVMValue is going to become a proper enum with conversions between ffi::TVMValue. The box_value is a hack to fix the unsafety around ffi::TVMByteArray conversions.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why adding temporary here? you mean it's in your todo for this PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

Temporary in terms of the next two rust PRs. I want @kazimuth to be able to merge his PR, but I agree that committing bad code is a cardinal sin. I'd have to ask him whether he's willing to wait for another round of commits which truly bury the TVM ffi types.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's keep things right as you've previously wanted from me (in TVMRetValue POD val frontend), lay the groundwork and later if he's interested he can add the GraphModule on top of it after. We're not in rush.

Copy link
Member Author

Choose a reason for hiding this comment

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

We're not in rush.

🙏 thank you for your understanding

Copy link
Contributor

@ehsanmok ehsanmok Mar 29, 2019

Choose a reason for hiding this comment

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

What's the status of this? it's not resolved yet and you're using TVMValue so no POD discussion!

Copy link
Member Author

@nhynes nhynes Mar 29, 2019

Choose a reason for hiding this comment

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

This will be addressed in a future PR. This one was supposed to unify the types, which it did. Delaying merging this one just blocks further contribution from others.

Copy link
Contributor

Choose a reason for hiding this comment

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

This has exactly happened when you requested me to include it in frontend PR. This sort of double standard is not good.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, I personally have no issue with sitting on this PR for another few months and get things really polished. If you want to add some commits, you're more than welcome to. I'll defer to @tqchen about timelines.

@kazimuth
Copy link
Contributor

I don't know this codebase that well, but I'm happy to look over this.

One question I'm still not sure about is: what's the relationship between NDArrayContainer, ArrayHandle, DLTensor, NDArray, and ndarray::Array? I remember having issues where returning NDArrayContainers from a Function caused panics, is there a test for that somewhere?

Also, do you intend to unify Function and PackedFunc? I'm still not certain if they do the same thing or not.

Please don't worry about accommodating the changes in my PR, I'm happy to rewrite it over whatever the API ends up looking like.

@ehsanmok
Copy link
Contributor

ehsanmok commented Feb 18, 2019

@kazimuth appreciate your upcoming contributions in advance!

Great questions :)

what's the relationship between ...

  1. Everything starts from dlpack's DLTensor as a common tensor/ndarray format for DL frameworks (adopted by Apache MXNet and Pytorch).

  2. TVM uses the type alias TVMArray for DLTensor.

  3. TVMArrayHandle is an opaque pointer to TVMArray.

  4. In frontends (Rust/Python/Java) they define NDArray wrapper (struct/class) around TVMArrayHandle with more info such as is_view.

  5. Rust has the luxury of defining its own ndarray/tensor in runtime.

  6. There's Rust's crate ndarray which we use here to interact with our own Tensor/NDArray. Similar to numpy.ndarray vs. mxnet.NDArray/pytorch.Tensor.

  7. NDArrayContainer is runtime managed NDArray and it's not supported in Rust frontend Function for now. See #1418 for more info.

Also, do you intend to unify Function and PackedFunc? I'm still not certain if they do the same thing or not.

Rust frontend Function (like NDArray) contains a TVMFunctionHandle of C++ PackedFunc. Rust frontend optimum representation of C++ PackedFunc is fn(&[TVMArgValue]) -> Result<TVMRetValue> (which you can use in register_global_func! macro to register Rust's packed function as a callback to C++ backend) because first fn is a (safe) primitive function pointer type constructor and is already Fn + Pointer + Send + Sync + ... and second it captures the Result. Runtime representation is Box<Fn(&[TVMArgValue]) -> TVMRetValue + Send + Sync> as direct translation of std::function<void (TVMArgs args, TVMRetValue* rv)>;. It's in this PR TODO to change it so that function can capture Result.

@jroesch
Copy link
Member

jroesch commented Feb 20, 2019

I'll review tomorrow, been very busy lately.

@nhynes
Copy link
Member Author

nhynes commented Feb 20, 2019

I'll review tomorrow, been very busy lately.

@jroesch no rush. I'll take another pass later this week to iron out those TODOs in the PR body.

Ichange runtime packed function representation to the optimum one.

Just pushed a fix to that (3d10c6b). Note that the fat pointer is required if you want to make a PackedFunc out of a closure. Otherwise, it's probably sufficient to from_raw a C function pointer so to prevent allocation. It shouldn't matter w.r.t. the UI since Box<T> is Deref<T>

@ehsanmok
Copy link
Contributor

ehsanmok commented Feb 20, 2019

Note that the fat pointer is required if you want to make a PackedFunc out of a closure.

Right! Frontend PackedFunc is not a closure and don't need to be. But for runtime, since you're directly translating std::function<void (TVMArgs args, TVMRetValue* rv)>; you can argue that boxing is necessary and so be it.

@ehsanmok
Copy link
Contributor

ehsanmok commented Mar 7, 2019

@nhynes Any update on this PR?

@tqchen
Copy link
Member

tqchen commented Mar 12, 2019

ping @nhynes might be a good idea to push some of this

@nhynes
Copy link
Member Author

nhynes commented Mar 12, 2019

Sorry about that. This was definitely a bit much to bite off for one PR :) I'll get this in by the end of the week.

@nhynes
Copy link
Member Author

nhynes commented Mar 23, 2019

I'll have an update for this PR soon. Right now I'm just working through a bug where a Rust RetVal in a callback is being dropped because TVM CFuncSetReturn doesn't take ownership.

@nhynes
Copy link
Member Author

nhynes commented Mar 23, 2019

ok, once the tests pass, this PR will be ready to go. It accomplishes the goal of unifying the types, but it doesn't do things like

  • making a Rusty TVMPodValue
  • a proc macro for register_global
  • all the other TODOs :)

@nhynes
Copy link
Member Author

nhynes commented Mar 23, 2019

we currently have a bit of a catch-22: the build is failing because we need a new version of rustfmt, but we can't upgrade rustfmt because rust-sgx-sdk depends on a particular nightly version. I'm about to do a rewrite of TVM SGX using the Fortanix EDP, so I'm in favor of breaking the current SGX, merging this, and then dropping in new SGX. Thoughts?

@nhynes
Copy link
Member Author

nhynes commented Mar 29, 2019

@tqchen @jroesch could you please review this PR or suggest reviewers? (pending addressing comments) This PR is ready to go.

/// assert_eq!(String::try_from(t).unwrap(), s);
/// ```
pub struct TVMRetValue {
pub value: TVMValue,
Copy link
Contributor

@ehsanmok ehsanmok Mar 29, 2019

Choose a reason for hiding this comment

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

What's the status of this? it's not resolved yet and you're using TVMValue so no POD discussion!

@@ -85,14 +88,19 @@ fn main() {
.get_function("set_input", false)
.unwrap();

call_packed!(set_input_fn, "data", &input).unwrap();
let data_str = std::ffi::CString::new("data").unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be hidden from the user.

&mut ret_type_code as *mut _
));
}
pub fn invoke(&mut self) -> Result<TVMRetValue, Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you removed FnOnce impl, is there any other place needing unboxed_closure feature? if none, please remove it from frontend.

@tqchen
Copy link
Member

tqchen commented Mar 31, 2019

@ehsanmok @nhynes would be great if you can agree on a plan for moving forward.

From what I see, there is a TVMRetValue debate about what the signature should be which could merit a discussion. If we view this PR as a refactor and the code is strictly better than the old version, we can merge this in. If the TVMRetValue is a new feature and given that the public data structure affects ABI, it is good to polish and make it better.

In this case, I think the main disagreement comes from how to treat the setting. If it takes a while to settle down on the TVMRetValue struct and the code is a strict improvement, I think we can consider merging it. If we can settle down the design of TVMRetValue in a few days, then perhaps it is better to pin that down first.

Either way, let us have a healthy discussion and make suggestions on how to move forward

@nhynes
Copy link
Member Author

nhynes commented Mar 31, 2019

Thanks for weighing in @tqchen.

In my mind, the discussion about TVMRetValue pertains only to its implementation details. @ehsanmok and I are in agreement that thinly wrapping the TVMValue union (taken directly from the FFI) is not a Rusty solution and we'd both like to see it turned into a Rust enum (still a tagged union, but more idiomatic). However, the API provided by TVMRetValue--namely From and Into--is stable. It's more of a question of "do we bundle more changes into this PR or make more PRs." This one accomplishes its original goal of having the frontend and runtime use the same types.

I can emphasize with Ehsan, though. In his original PR, I was fussy about the same issue and asked him to change it to the previous repr (now the proposed repr). I do apologize for this, but unfortunately my precognition isn't what it used to be, and I didn't know what repr would make the types easier to unify. All I can do is promise that I (or someone else) will eventually make a PR (perhaps even this one) that addresses the issue. After all, who doesn't want a better Rust TVM? :)

@ehsanmok if you feel strongly about enum-ifying TVMRetValue, I will gladly incorporate the changes into this PR, but it'll take a while since I also want to turn register_global into a proc macro attribute ;)

@ehsanmok
Copy link
Contributor

ehsanmok commented Apr 2, 2019

@tqchen This is fine for now! I don't have any other comments to make for this PR. @nhynes has explained what each of us is thinking about the final Rusty repr (at least for this PR), but since it's rather fundamental, I'd really love to decide on a solid one as soon as we can after this is merged. Nick, please feel free to propose and update your plans in this regard.

We, as a community, will continue helping each other professionally in any way we can :)

@tqchen
Copy link
Member

tqchen commented Apr 2, 2019

@ehsanmok
Copy link
Contributor

ehsanmok commented Apr 2, 2019

@tqchen Done.

@tqchen tqchen merged commit 4968279 into apache:master Apr 3, 2019
@tqchen
Copy link
Member

tqchen commented Apr 3, 2019

Thanks, @ehsanmok @nhynes , this is now merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants