Live data from Hacker News

Speed of Rust vs. C

kornel.ski

271–280 of 546 posts

Re: Speed of Rust vs. C

#271

Earlier quoted context omitted.

It doesn’t say it “works badly” it says the borrow checker can’t protect against external modifications to the file while memory-mapped, which has a host of issues in C as well. You can mmap files in Rust just fine, but it’s generally as dangerous as it is in C.

I don’t get this obsession with “dangerous.” Honestly, what does that even mean? I think a better word is “error-prone.” Danger is more like, “oh my god a crocodile!”

Concurrency bugs can absolutely cause dangerous danger of the deadly variety:

https://en.m.wikipedia.org/wiki/Therac-25

Re: Speed of Rust vs. C

#272

Earlier quoted context omitted.

But that may be of little solace. If you snapshot your entire heap into an mmapped file for fast I/O, then basically the entire advantage of Rust is gone.

> If you snapshot your entire heap into an mmapped file for fast I/O, I've never heard of this trick. And my first reaction is "That would be a nightmare of memory unsafety if I did it in C++" What's it used for? IPC?

I think emacs (used to?) do something awful like this. https://lwn.net/Articles/707615/

Re: Speed of Rust vs. C

#273
post #182

> Both are "portable assemblers" I don't tend to think of Rust as "portable assembly", and this is indeed one of the points where I think it differs the most from C. I think of "portable assembly" as being applicable to C, because it is some version of a "minimal" level of abstraction for a high-level language. Rust is very much a tool for abstraction, and one of the USPs of rust is that the compiler abstracts away t…

With a bit of experience you get the same in Rust.

Rust does not abstract away memory management. For example, it never heap allocates anything implicitly. It inserts destructors, but does so predictably at end of scopes, in a specified order.

Rust heavily uses iterators with closures, but these get aggressively inlined, and you can rely on them optimizing down to a basic loop. For code generation they're not too different from a fancy C macro.

And if in doubt, there's https://rust.godbolt.org/ (don't forget to add -O to flags)

Re: Speed of Rust vs. C

#274
post #93

Earlier quoted context omitted.

If my memory is correct: yes, the root cause was a missing bounds check, but the vulnerability was much worse than it could have been because OpenSSL tended to allocate small blocks of memory and aggressively reuse them — meaning the exploited buffer was very likely to be close in proximity to sensitive information. I don’t have time right now to research the full details, but the Wikipedia article gives a clue: > Th…

That's what happens by using normal malloc/free anyway, no? Implementations of malloc have a strong performance incentive to allocate from the cache hot most recently freed blocks.

Yes, all allocators (except perhaps OpenBSDs from what I see in this thread) do this. It is also why `calloc` exists - because zero-initializing every single allocation is really, really expensive.

Re: Speed of Rust vs. C

#275

> C libraries typically return opaque pointers to their data structures, to hide implementation details and ensure there's only one copy of each instance of the struct. This costs heap allocations and pointer indirections. Rust's built-in privacy, unique ownership rules, and coding conventions let libraries expose their objects by value The primary reason c libraries do this is not for safety, but to maintain ABI com…

> This costs heap allocations and pointer indirections.

Heap allocations, yes; pointer indirections no.

A structure is referenced by pointer no matter what. Remember that the stack is accessed via a stack pointer.

The performance cost is that there are no inline functions for a truly opaque type; everything goes through a function call. Indirect access through functions is the cost, which is worse than a mere pointer indirection.

An API has to be well-designed this regard; it has to anticipate the likely use cases that are going to be performance critical and avoid perpetrating a design in which the application has to make millions of API calls in an inner loop. Opaqueness is more abstract and so it puts designers on their toes to create good abstractions instead of "oh, the user has all the access to everything, so they have all the rope they need".

Opaque structures don't have to cost heap allocations either. An API can provide a way to ask "what is the size of this opaque type" and the client can then provide the memory, e.g. by using alloca on the stack. This is still future-proof against changes in the size, compared to a compile-time size taken from a "sizeof struct" in some header file. Another alternative is to have some worst-case size represented as a type. An example of this is the POSIX struct sockaddr_storage in the sockets API. Though the individual sockaddrs are not opaque, the concept of providing a non-opaque worst-case storage type for an opaque object would work fine.

There can be half-opaque types: part of the structure can be declared (e.g. via some struct type that is documened as "do not use in application code"). Inline functions use that for direct access to some common fields.

Re: Speed of Rust vs. C

#276
post #142

> For example, in C I'd be tempted to reuse a buffer allocated for one purpose for another purpose later (a technique known as HEARTBLEED). You can do that in Java (with byte arrays) or in Common Lisp, so what is the point here? It is not practice in Java, Lisp nor in C and C++. > It's convenient to have fixed-size buffers for variable-size data (e.g. PATH_MAX) to avoid (re)allocation of growing buffers This is becau…

Any book you'd recommend to back up your claims?

Modern C, by Jens Gustedt is one of the best books on C that I have read. That said, I don't think it scratches the surface of backing up the parents claims––though if anyone know of such a text please let me know.

Re: Speed of Rust vs. C

#277
post #186

> computed goto I did a deep dive into this topic lately when exploring whether to add a language feature to zig for this purpose. I found that, although finnicky, LLVM is able to generate the desired machine code if you give it a simple enough while loop continue expression[1]. So I think it's reasonable to not have a computed goto language feature. More details here, with lots of fun godbolt links: https://github.c…

Somewhat off-topic: I just looked into zig, because you mentioned it. > C++, D, and Go have throw/catch exceptions, so foo() might throw an exception, and prevent bar() from being called. (Of course, even in Zig foo() could deadlock and prevent bar() from being called, but that can happen in any Turing-complete language.) Well, you could bite the bullet and carefully make Zig non-Turing complete. (Or at least put Tur…

With respect to deadlocks, there’s little practical difference between an infinite loop and a loop that holds the lock for a very long time.

Languages like Idris and Agda are different because sometimes code isn’t executed at all. A proof may depend on knowing that some code will terminate without running it.

Re: Speed of Rust vs. C

#278

Earlier quoted context omitted.

Until very recently, memory allocators were more than happy to return you the thing you just deallocated if you asked for another allocation of the same size. It makes sense, too: if you're calling malloc/free in a loop, which is pretty common, this is pretty much the best thing you can do for performance. Countless heap exploits later (mostly attacking heap metadata rather than stale data, to be honest) allocators h…

True of the more common ones, but it should be acknowledged that OpenBSD was doing this kind of thing (and many other hardening techniques) before heartbleed, which was the main reason Theo de Raadt was so upset that they decided to circumvent this, because OpenBSD's allocator could have mitigated the impact otherwise.

Even higher-performance mallocs like jemalloc had heap debugging features (poisoning freed memory) before Heartbleed, which -- if enabled -- would catch use-after-frees, so long as libraries and applications didn't circumvent malloc like OpenSSL did (and Python still does AFAIK).

Re: Speed of Rust vs. C

#279

Earlier quoted context omitted.

ripgrep is based on re2, a c library I would guess it contains more c than rust code... But what I love about this article is its lack of hype. It makes clear arguments both ways and all of them I can get behind Hype doesn't help Edit: To all my downvoters; I anticipated you :) With love and best wishes

No it's not. Its regex library is written in Rust, but was inspired by RE2. It shares no code with RE2. (And RE2 is a C++ library, not C.) Off the top of my head, the only C code in ripgrep is optional integration with PCRE2. In addition to whatever libc is being used on POSIX platforms. Everything else is pure Rust.

Ah, thanks burntsushi, I believe you are the ripgrep author even?

Great work btw. Ripgrep is the best

... I will have to restrict my comment to just LLVM being a larger, c++, dependency

... Just angling for more downvotes ;) Thanks for the reply

Re: Speed of Rust vs. C

#280
post #57

I appreciate the article, but it would be really nice if the author could add a timestamp to his blog posts. Without timestamps, it's impossible to know if any issue described in the article body still exists. I didn't read it, because it might present outdated knowledge.

The fact that my perfectly valid comment was down voted like this shows that HN has a pretty dysfunctional community. I think that is my last comment here ;)
Post reply on HN