Live data from Hacker News

Why is calling my asm function from Rust slower than calling it from C?

ohadravid.github.io

31–40 of 43 posts

Re: Why is calling my asm function from Rust slower than calling it from C?

#31
post #5
post #4

Earlier quoted context omitted.

The point of Rust is ostensibly to provide a safer version of C++-like semantics, not necessarily to avoid the same level of complexity. Especially if you're directly using unsafe code (which is necessary in some cases, like FFI), it's not really clear to me that Rust was "meant" to be doing something wildly different here. The large majority of the code not needing to use unsafe will still be better off even if this…

This is all well and good, but "zero cost abstractions" implies, well, that you're crawling _up_ the abstraction pyramid, not lost in twisty little side passages.

From this thread it's pretty clear:

1. You've come up with your own unique definition of "zero cost" which isn't what the term means, at least as popularized by C++ where the concept of "zero cost abstractions" comes from. It means zero runtime cost, not zero cognitive cost.

2. Rust generally has more "zero cost abstractions" than C++ in terms of shifting traditional runtime checks into compile-time checks (e.g. while unique_ptr exists in Rust as Box, it's far more rarely used because ownership is easier to transfer).

3. Rust generally has faster defaults in the standard library so the abstractions are lower-cost than equivalents in C++ (e.g. C++ took forever to add a hash table via unordered_map and then proceeded to really fuck up the definition to inhibit high performance designs despite this being raised during standardization & haven't since revisited the issue).

4. Rust offers far more opportunities for "zero cost abstractions" than C++. Notably it's ownership rules prevent aliasing which allows the compiler to do more aggressive optimizations that can't generally be done in C++. It provides abstractions like NonNullPointer and NonZero so that unused bit patterns can be leveraged for enums (e.g. Option is the size of a raw pointer but in C++ it's harder to pull off). Functional style and iterators are implemented to be aggressively inlined and overheads like bounds checking elided by the compiler, whereas C++ can't elide as easily and various bounds checks have to be inserted. Could go on and on.

Anyway, Rust as the base language vs C++ has simpler cognitive abstractions, it has more zero cost abstractions, and it has more efficient abstractions overall, both in the language and the standard library.

Re: Why is calling my asm function from Rust slower than calling it from C?

#33
post #15
post #12

Earlier quoted context omitted.

> But the point is that the exact same external library code has a _lower_ cost if called from the non-abstract horror of a language C, and the (partial) fix for that is really ugly looking low-level Rust crap that that took a _long_ time to figure out and is the exact opposite of an abstraction, and the full fix is not even known yet. No, it's not the exact same external library. There's an additional Rust library i…

> No, it's not the exact same external library. It uses the exact same assembly language. > There's an additional Rust library in between that they used, which provides the `FFISafe` type, and that has overhead. Look, I wrote " But the point is that the exact same external library code has a _lower_ cost if called [from C]" and that remains a true statement. It's pretty obvious that I was referring to the shared code…

> It uses the exact same assembly language.

> The profiler showed that the identical assembly language itself was taking more cycles when called from C.

No, again, it showed that using a certain wrapper type around the same assembly had more overhead. My argument is that there's no reason to assume that this single type is representative of the entire ecosystem. I don't understand what you could possibly mean if that's not what you're saying.

> The point is that they did the absolute normal expected thing in Rust, and it slowed down the external assembly language library, and after a _lot_ of digging and debugging, they changed the Rust code to be a lot less flexible and more convoluted, and now the assembly language is almost as fast as when it is called from C.

> Look, the people doing this _obviously_ know Rust, and it took them a _long_ time, and some _really_ ugly concrete low-level concrete code, to take external code and make it perform almost as well as if it had been called from C++.

Sure, and you'd probably need a lot of similar ugliness to do the same in reverse to call a random Rust library that wasn't explicitly designed to be called from C/C++. You seem to be inferring that Rust on the whole can't be performant because the boundary between C/C++ isn't as easy as staying on the same side of the boundary, but that works both ways. If your argument is "I only want to use a language that lets me call all of these C/C++ libraries I want", that's totally reasonable. That's not the same as "Rust doesn't have zero cost abstractions" though, unless you think "calling a C/C++ library directly" is what abstraction means.

> Look, the people doing this _obviously_ know Rust, and it took them a _long_ time, and some _really_ ugly concrete low-level concrete code, to take external code and make it perform almost as well as if it had been called from C++.

> To me, that looks like a high-cost non-abstraction.

I don't understand this argument at all. You don't think that a type called `WithOffset` that wraps a pointer to provide an offset is an abstraction? From what I can tell, they were using an abstraction that was implemented inefficiently, and they switched to one written efficiently. I guess we just have completely incompatible definitions of what "abstraction" means, given that from my perspective, you genuinely seem to be arguing that the presence of this one abstraction that someone wrote being inefficient happens to be representative of the entire language and ecosystem.

Re: Why is calling my asm function from Rust slower than calling it from C?

#34
post #3

Not related to the topic, but seeing this tidbit in the article took me by surprise; > PSA: if you don’t see syntax highlighting, disable the 1Password extension. This linked to the following discussion that's been ongoing for over a week (with acknowledgement from 1password that they're looking into it but as far as can tell no ETA on the fix or explanation for why it's happening in the first place): https://www.1pa…

From link:

  It looks [like the 1password extension] is using prism.js to try to add syntax highlighting to  blocks on the entire page.
Jesus that comment contains so many red flags for a extension managing passwords!

Re: Why is calling my asm function from Rust slower than calling it from C?

#35
post #19

Earlier quoted context omitted.

Natural language. It's a language interpreted by LLMs. It is really high level.

Even that has lots and lots of abstractions which add complexity. For example, figures of speech such as "shooting fish in a barrel".

If anything, the fact that it's hard to come up with an example of a language like this is even more evidence that this probably isn't what anyone has ever meant by that term.

Re: Why is calling my asm function from Rust slower than calling it from C?

#36
post #14
post #13

Earlier quoted context omitted.

> > someone writing a library with a costly abstraction > That's not what happened here. Yes it is. Where do you think the `FFISafe` type that they used came from? It's not anything inherent to how Rust does FFI; it's a type someone wrote in an attempt to try to provide an abstraction, and that abstraction happened to have a cost. There's absolutely no reason anyone has to use it in order to do FFI in Rust.

The extra time was being taken up in the identical assembly language function. Fixing it took wizardry.

The time was taken by the code around the assembly language. It was an abstraction someone wrote in a way that happened not to be efficient. The ability of someone to write a costly abstraction doesn't take away from Rust any more than the possibility of doing that in C++ would from whether C++ has them.

Re: Why is calling my asm function from Rust slower than calling it from C?

#37
post #36
post #14

Earlier quoted context omitted.

The extra time was being taken up in the identical assembly language function. Fixing it took wizardry.

The time was taken by the code around the assembly language. It was an abstraction someone wrote in a way that happened not to be efficient. The ability of someone to write a costly abstraction doesn't take away from Rust any more than the possibility of doing that in C++ would from whether C++ has them.

It was caused by the code around it, but the actual CPU cycles stalled out in the assembly language, making it much harder to find the problem.

Is how I understood the post.

Anyway there's noting wrong with rust FFI. The overhead was because this function wanted to support two options and didn't implement that in the best way.

Re: Why is calling my asm function from Rust slower than calling it from C?

#38
post #5

Earlier quoted context omitted.

This is all well and good, but "zero cost abstractions" implies, well, that you're crawling _up_ the abstraction pyramid, not lost in twisty little side passages.

"Zero cost abstractions" refers to some features of the language that provide functionalities with no runtime cost, e.g. (safe) iterators, not to a presumed simplicity of the whole language. Therefore, this is not mutually exclusive with the fact that certain concepts in Rust require more complexity than their counterpart in other languages (after all, the complexities of the borrow checker don't exist in C). In gene…

> "Zero cost abstractions" refers to some features of the language that provide functionalities with no runtime cost...

In that case, all languages provide zero cost abstractions.

> programming with a high level of control over the implementation is complex, > (after all, the complexities of the borrow checker don't exist in C).

The point of an abstraction is to _hide_ complexity.

> This article explains the concept

The article is only half-right. People complain both about the semantics and the syntax, because Rust's implementation of both presents (in some cases) a huge cognitive load that simply doesn't exist in other languages.

Re: Why is calling my asm function from Rust slower than calling it from C?

#39
post #18

Interesting deep dive. Can't say I understand the nuances really, and some of the Rust syntax looks incredibly arcane to me as an outsider.

(Author here) thanks! Even as an experienced Rust developer, this is a lot of syntax which is part of what makes it archaic I think.

As usual with Rust most of it is due to inherent complexity: for example, when you deal with raw pointers, you need to specify if they are const or mut, and you must have a syntax that’s not &, and not too wordy: so * is a good choice (might be changed to &raw in the future!). And if you want to say that something is generic and the generic parameter is a pointer… you just end up with a lot of syntax.

Re: Why is calling my asm function from Rust slower than calling it from C?

#40
Title reminded me of something completely unrelated:

FFI overhead is not to be neglected sometimes. I've seen cases where replacing a python or js lib with an in isolation much faster native Rust or C lib with bindings had the end result being a decrease in real-world performance due to it being in a hot path or loop and the overhead being more significant than the savings were. There is no substitute for real-world benchmarks.

Post reply on HN