On 07/31/2017 01:02 PM, Henri Sivonen wrote:
On Tue, Jul 18, 2017 at 7:01 AM, Jim Blandy <jbla...@mozilla.com> wrote:
BTW, speaking of training: Jason's and my book, "Programming Rust" will be
available on paper from O'Reilly on August 29th!

And already available on Safari Books Online (access available via
Service Now request subject to managerial approval).

On Mon, Jul 17, 2017 at 10:43 PM, Ted Mielczarek <t...@mielczarek.org> wrote:
From my perspective Rust is our
single-biggest competitive advantage for shipping Firefox, and every
time we choose C++ over Rust we throw that away.

I agree.

but we are quickly
going to hit a point where "I don't feel like learning Rust" is not
going to cut it anymore.

Indeed.

On Tue, Jul 11, 2017 at 4:37 PM, smaug <sm...@welho.com> wrote:
How is the performance when crossing Rust <-> C++ boundary? We need to make
everything faster, not slower.
If I understood emilio's explanation on IRC correctly having the performance
of an inlined (C++) function requires
handwritten rust bindings to access member variables of some C++ object.
That doesn't sound too good - hard to maintain and possibly easy to forget
to optimize.

I don't claim to understand anything about the current setup, but
has anyone written down what would be needed to have fast and easy to
maintain Rust <-> C++ boundary in
such way that also memory handling is easy to manage (aka, how to deal with
CC/GC).
I think it would be better to sort out this kind of low level issues rather
soon before we have too much
Rust code in tree, or perhaps we won't see much Rust usage before those
issues are sorted out.

(I'm looking this all from DOM point of view, where pretty much all the
objects need to be cycle collectable JS holders, but perhaps Rust would fit
better in code outside DOM)

Rust indeed is, at least at present, a better fit for code outside the
area of cycle-collectable DOM objects.

The performance issue you mention applies if the usage scenario is
that Rust code needs to get or set a lot of fields on a C++ object.
While we do have code that, if implemented in Rust, would have to do
performance-sensitive field access on C++ objects, we also have areas
for which that would not be a concern. For example, in the case of
encoding_rs, the data that crosses the FFI boundary is structurally
simple (mozilla::Span / Rust slices decomposing to pointer to an array
of primitives and a length for FFI crossing) and the amount of work
done on the Rust side is substantial compared to the frequency of
crossing the FFI boundary.

In the absence of the Stylo-like optimization of frequent
performance-sensitive access of fields of a foreign-language object,
the FFI story that one can expect involves three functions/methods per
logical method.

Either (for C++ caller and Rust callee)
 1) C++ method wrapping the C function to hide the unsafety and bad
ergonomics of raw C.
 2) C function declared in C++ and implemented in Rust.
 3) Rust method: the actual callee that does something useful.
Or (for Rust caller and C++ callee)
 1) Rust method wrapping the C function to hide the unsafety and bad
ergonomics of raw C.
 2) C function declared in Rust and implemented in C++.
 3) C++ method: the actual callee that does something useful.

So there's the real callee method, there's a small C function that
wraps that method in a C ABI-compatible way and then there is a
wrapper for the C function that provides the ergonomics that one would
expect in the calling language.

The caller-side wrapper around the C function is trivial to make
inline and as a matter of code size is likely harmless or even
strictly beneficial to make inline.

The compilers don't have visibility across the declaration definition
of the C function, since that's where the cross-language linkage
happens, so currently one needs to assume that the C function always
has the cost of an actual function call.

As for inlining the actual callee method of interest in the language
being called into the implementation of the C function, it may or may
not happen automatically and when it doesn't happen automatically,
forcing it to happen manually might be a problem in terms of code
size.

So when the callee that actually does the work we care about doesn't
get inlined into its C wrapper, one should approximate a call from
Rust to C++ or from C++ to Rust have the cost of two *non-virtual*
function calls instead of one. (It would be interesting to contrast
this to the cost of over-use of virtual calls due to nsIFoo
interfaces.)

- -

Ideally, both the caller-language-side wrapper around the C function
and the C function itself would get inlined so that the cross-language
call would on the machine code level look just like a normal (if not
also inlined!) call to a method of the callee language. For that to
happen, we'd need link-time inlining across object files produced by
different-language compilers.

Naïvely, one would think that it should be possible to do that with
clang producing "object files" holding LLVM IR and rustc producing
"object files" holding LLVM IR and the "link" step involving mashing
those together, running LLVM optimizations again and then producing
machine code from a massive collection of mashed-together LLVM IR.

In London, so over a year ago, I asked people who (unlike me) actually
understand the issues involved how far off this kind of cross-language
inlining would be, and I was told that it was very far off. Most
obviously, it would require us to compile using clang instead of MSVC
on Windows.

Now that it's been over a year and two significant things have
happened, 1) we actually have (traditionally-linked for the FFI
boundary) Rust code in Firefox and 2) clang is ready enough on Windows
that Chrome has switched to it on Windows, I guess it's worthwhile to
ask again:

If we were compiling C++ using clang on all platforms, how far off
would such cross-language inlining be?

If we could have the cross-language inlining benefit from compiling
C++ using clang on all platforms, how far off would we be from being
able to switch to clang on all platforms?

- -

But to go back to Rust and DOM objects:

Even the context of DOM objects, there are two very different
scenarios of relevance:
 1) Rust code participating in DOM mutations
 2) Rust code reading from the DOM when the DOM is guaranteed not to change.

Scenario #2 applies to Stylo, but Stylo isn't the only case where it
could be useful to have Rust code reading from the DOM when the DOM is
guaranteed not to change.

I've been talking about wishing to rewrite our DOM serializers (likely
excluding the one we use for innerHTML in the document is in the HTML
mode) in Rust. I have been assuming that such work could reuse the
code that Stylo of has for viewing the DOM from Rust in a read-only
stop-the-world  fashion.

I haven't actually examined how reusable that Stylo code is for
non-Stylo purposes. Is it usable for non-Stylo purposes?

- -

And on the topic of memory management:

DOM nodes themselves obviously have to be able to deal with multiple
references to them, but otherwise we have a lot of useless use of
refcounting attributable to the 1998/1999 mindset of making everything
an nsIFoo. In cases where mozilla::UniquePtr would suffice and
nsCOMPtr isn't truly needed considering the practical ownership
pattern, making the Rust objects holdable by mozilla::UniquePtr is
rather easy: mozilla::Decoder and mozilla::Encoder are real-world
examples.


Reference counting is needed always if both JS and C++ can have a pointer to 
the object.
And UniquePtr makes it harder to ensure some object stays alive during a method 
call, say, if a member variable is
UniquePtr. With refcounting it is always easy to have local strong reference.
I'd it isn't very common that we could use UniquePtr when we currently use 
nsCOMPtr or RefPtr.



The main thing is implementing operator delete for the C++ stand-in
class that has no fields, no virtual methods, an empty destructor and
deleted constructors and operator=:
https://searchfox.org/mozilla-central/source/intl/Encoding.h#903

For the rest of the boilerplate, see:
https://searchfox.org/mozilla-central/source/intl/Encoding.h#1069
https://searchfox.org/mozilla-central/source/intl/Encoding.h#661
https://searchfox.org/mozilla-central/source/third_party/rust/encoding_c/src/lib.rs#467
https://searchfox.org/mozilla-central/source/third_party/rust/encoding_c/include/encoding_rs.h#350
https://searchfox.org/mozilla-central/source/third_party/rust/encoding_c/src/lib.rs#677

This, of course, involves more boilerplate than scenarios that stay
completely within C++ or that stay completely within Rust, but in the
case of encoding_rs, the work needed to create the boilerplate was
trivial compared to the overall effort of implementing the bulk of the
library itself.


_______________________________________________
dev-platform mailing list
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform

Reply via email to