Live data from Hacker News

Being fair about memory safety and performance

thecodedmessage.com

11–20 of 75 posts

Re: Being fair about memory safety and performance

#11
post #8
post #6

There's a presumption here that checked access would cost some nr of nanoseconds per access, but this often isn't the case since predicted, not-taken branches tend to have 0 cycle latency in recent CPUs.

Depends, if the function is vectorizable then the cpu can do more elements at a time if it doesn't do the branch prediction work. It is true for non-vectorizable work.

In autovectorized loops, the generated code typically needs length checks (or static length proofs) to handle tails of vectors. But yes there are still cases where the cost can be measurable.

Re: Being fair about memory safety and performance

#12
I'm not quite sure who this article is aimed at, and who those "C++ apologists" are, but as someone who programs in C++ all day, doesn't like it at all, and yet won't advocate to switch to Rust, these kinds of arguments are unconvincing. I'm not advocating to switch to Rust not because I don't think it's better than C++. I am absolutely, 100% convinced that Rust is technically better than C++ in most possible ways — in some, significantly better — and worse in almost no way. The problem is that, overall, it's not better enough.

Reevaluating a low-level programming language is something that's done in a large organisation or project once every 15-25 years or so. Switching such a programming language incurs a high cost and a high risk, and is a long-term commitment. To make such a switch, the new language obviously has to be better, but that's not enough. It has to be a hell of a lot better (and, if not, at least return the investment with a profit quickly).

For some, Rust is better enough. For me, not nearly so. Even though it offers a fascinating and, I think, ingenious path to better safety, it shares some of C++'s greatest downside for me, which is that they are both extremely complex languages. Maybe Rust is simpler, but not enough. Rust also shares what I think is C++'s original misguided sin, which is the attempt to create a low level language, whose code appears high-level on the page by means of a lot of implicitness. I've become convinced that that's a very, very bad idea.

If there were no other ideas on the horizon or if Rust seemed like a surefire success, it might have been justified to make such a switch, but that's not the case. Rust's low adoption rate in professional settings is not reassuring to "PL-cautious" people like me, and a language like Zig shows that there are other approaches that appeal to me more, and while more revolutionary and ambitious than Rust in its departure from C++'s philosophy, I think it also has the potential to be better enough. Maybe it will make it, and maybe it inspires some other language that will, or maybe other ideas will turn up. Given the risk and commitment, to me it makes sense to wait. I don't like C++; I believe Rust is better. But that's not enough.

Re: Being fair about memory safety and performance

#13
post #10

Read only top half of the article. Would like to add that at least in plain C, doing var[index] doesn't invoke any checked() or unchecked() access function call. It's rather compiled into assembly instructions to calculate the address where the data is expected and load it into memory in one or two lines.

Which is, incidentally, why you can also write `index[var]`. `[]` is really just a convenience for `*(var + index)`. In fact that's exactly how the standard defines it (or at least defined it as of C11, I've not checked more recent versions):

> A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))).

Re: Being fair about memory safety and performance

#14
The substance of the article is overshadowed by its unfortunate tone.

> But wait! The C++ apologists are still talking! What are they saying? How have they not been completely flummoxed?

is just one small sample. I came out of the article liking Rust a little bit less than when I went in (irrational, I know, but true).

The quote from The Big Lebowski comes to mind: you're not wrong, author, ...

Re: Being fair about memory safety and performance

#15
post #6

There's a presumption here that checked access would cost some nr of nanoseconds per access, but this often isn't the case since predicted, not-taken branches tend to have 0 cycle latency in recent CPUs.

> predicted, not-taken branches tend to have 0 cycle latency in recent CPUs.

This is not the case. Due to instruction level parallelism, the throughput could be unaffected, but you will always have latency penalty. The CPU still needs to run the check (access the length and compare it to the index) and this adds latency. On top of that, it also increases code size, which can impact the instruction cache and binary size. It’s a small penalty, but it’s not 0.

Re: Being fair about memory safety and performance

#16
This article has a core point which is good: “in Rust the default is safe, and you have to opt-in to unsafety, but in C++ the default is unsafe, and you have to opt-in to safety”. I think it’s easy to argue for Rust using this construction, because, well, that’s the entire point why Rust was created.

But, it really doesn’t take a very long post to talk about this. The remainder goes off the rails, talking about “C++ apologists” (hint: if you’re being “fair”, pick words that are unlikely to cause people to be preemptively upset. This is not one of those words) and their stupid opinions. And the author just trashes them as being complete idiots, but it’s obvious that the arguments come from inexperience or strawmen, which just makes the overall thing not particularly convincing. Saying that the various UB finding tools were useless because you tried using them and didn’t get good results is stupid. Being smug about “people who use modern C++ clearly can’t do HFT, which is the thing that you said you were using C++ to do” is also insipid, just because you spotted the use of a shared_ptr somewhere and read how it’s not zero-cost. Modern C++ has other things in it, you know, many of which are zero-cost and significantly (but not entirely) safer; picking one thing and misrepresenting it does not make for a good refutation.

Anyways, coming from someone who writes a lot of C++ and would also like a lot of code to be migrated to Rust for good reasons, it’s a good idea to approach the tradeoffs honestly and without disdain for those who aren’t convinced yet. The core argument I mentioned above and the closing part of the article does do this…but there’s a lot in the middle that doesn’t, and it drags down the usefulness of the post.

Re: Being fair about memory safety and performance

#17
> I learned that the issue was in framework code – code written by my boss’s boss. The code was untested, and written extremely poorly, and had rotted, so that it didn’t work at all.

One unaddressed issue in Rust is that this could easily happen with a crate, and how hard diagnosing it could be, especially due to implicit behavior via procedural and attribute macros. Also, just because your code is safe, there could still be an unsafe block at the end of any long safe call chain. I haven't been able to reconcile to myself how this isn't just an illusion of overall safety.

Re: Being fair about memory safety and performance

#18
post #10

Read only top half of the article. Would like to add that at least in plain C, doing var[index] doesn't invoke any checked() or unchecked() access function call. It's rather compiled into assembly instructions to calculate the address where the data is expected and load it into memory in one or two lines.

You’re taking C extremely literally, and compilers are not required to do this (for example, if the array is being iterated sequentially, multiple loads may be done at once). Similarly, in Rust a function call is not necessarily going to actually compile to a function call: it is expected that the access compiles down to something very close to what C would do. The syntax is an abstraction, rather than a normative designation to the compiler for how it should generate code.

Re: Being fair about memory safety and performance

#19

> I learned that the issue was in framework code – code written by my boss’s boss. The code was untested, and written extremely poorly, and had rotted, so that it didn’t work at all. One unaddressed issue in Rust is that this could easily happen with a crate, and how hard diagnosing it could be, especially due to implicit behavior via procedural and attribute macros. Also, just because your code is safe, there could…

I don’t think it’s so much an illusion as it is just not quite as absolute as one might initially think. If you get a segfault in a rust program you know where to look.

Re: Being fair about memory safety and performance

#20

This article has a core point which is good: “in Rust the default is safe, and you have to opt-in to unsafety, but in C++ the default is unsafe, and you have to opt-in to safety”. I think it’s easy to argue for Rust using this construction, because, well, that’s the entire point why Rust was created. But, it really doesn’t take a very long post to talk about this. The remainder goes off the rails, talking about “C++…

> This article has a core point which is good: “in Rust the default is safe, and you have to opt-in to unsafety, but in C++ the default is unsafe, and you have to opt-in to safety”.

On the subject of safe defaults, just to correct that Rust does not in fact have as much default memory safety with regards to buffer bleeds (e.g. variants of OpenSSL's Heartbleed) as it could [1], because it has unchecked arithmetic (integer wraparound) as the default for performance, with checked arithmetic only as an opt-in for safety.

In other words, if an attacker can get some bounds merely to underflow (as opposed to overflow) then they can still read the sensitive memory of a Rust program, even without a UAF or buffer overflow.

Bleed vulnerabilities like these are also low-hanging fruit and significantly easier to exploit.

In other words, bounds checking only ensures you are within the buffer, but checked arithmetic is still needed to ensure that your index was correctly calculated in the first place.

I believe that Rust would be much safer against memory bleeds, if it had checked arithmetic enabled by default for safety, with an opt-out at the block scope level for performance, like Zig has.

[1] https://news.ycombinator.com/item?id=29991439

Post reply on HN