|
|
Subscribe / Log in / New account

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Oct 31, 2023 18:30 UTC (Tue) by tialaramex (subscriber, #21167)
In reply to: Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack) by linuxrocks123
Parent article: Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

You're not going to be able to "outperform Rust by default" even in the status quo. You have to get down into the nitty gritty with algorithmic choices and library features and even CPU models and so it's much too messy for such categorical claims even without incurring any cost at all. One Monday you write a Benchmark Game winner in C++ that cuts a second off the record time then by next Monday some Rust fanatic wrote a Rust solution that shaves a further second off that, it goes back and forth until one of you gets bored by which time both codebases are likely unmaintainable nightmares of micro-optimisations.

The biggest performance loss C++ has to take is lack of aliasing optimisations. On a good day C++ gets some simple cases where it can use its type rules to rule out aliasing, on a bad day the programmers turned that off because it broke their awful software. But for Rust every day is a good day in this respect, it's fundamentally unsound in Rust to have any aliasing and mutability, so all those optimisations are available. The biggest problem is bugs in LLVM (but don't fret, these bugs often bite C++ too, it's just easier to prove they're bugs with Rust).

And no, C++ for at least the last several years and arguably going back decades is very willing to sacrifice performance, but not to get safety. What C++ trades away performance for is endless back compatibility. Why are these data structures so needlessly huge? So that code written in 1998 still works. Why is this obvious code so slow? Because if it did it the quick way it would mess with assumptions people made in 2005. Why isn't this faster way to do things enabled in C++ without vendor extensions? Because that would break some source code written last century.


to post comments

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 1, 2023 0:39 UTC (Wed) by linuxrocks123 (subscriber, #34648) [Link] (10 responses)

So, there's too much wrong stuff here to correct all of it, so I'll just start and end my reply with "Yes, C++ allows aliasing optimizations. The keyword is 'restrict'."

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 1, 2023 6:52 UTC (Wed) by roc (subscriber, #30627) [Link]

In practice "restrict" is not used much because it is very risky: the consequences of getting it wrong are horrendous (unpredictable UB) and there aren't even good tools for dynamically checking "restrict" usage.

The C++ big projects I'm familiar with even treat TBAA as too risky and turn it off. No way they're going to sprinkle "restrict" around their code.

In Rust however you get the equivalent of pervasive "restrict", TBAA, and more, by default, with almost no risk.

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 1, 2023 8:40 UTC (Wed) by tialaramex (subscriber, #21167) [Link] (8 responses)

> "Yes, C++ allows aliasing optimizations. The keyword is 'restrict'."

That's a C keyword. Immediately you aren't even writing standard C++ but some vendor flavour with no clearly defined semantics.

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 1, 2023 22:53 UTC (Wed) by linuxrocks123 (subscriber, #34648) [Link] (7 responses)

Oh my god! So terrifying! What will become of my code!?!?

Oh, wait, GCC, Clang, and MSVC all support it with the same semantics. But you're right you do have to do:

#ifdef _WIN32
#define __restrict restrict
#else
#define __restrict__ restrict
#endif

To make it work on every compiler anyone cares about. Terrible cost, those five lines of boilerplate.

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 2, 2023 4:21 UTC (Thu) by linuxrocks123 (subscriber, #34648) [Link]

Probably use _MSC_VER instead of _WIN32. I don't do Windows much :)

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 2, 2023 6:01 UTC (Thu) by marcH (subscriber, #57642) [Link] (2 responses)

If it's so simple, why is it still not part of the C++ standard 24 years after it was formally defined in C?

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 2, 2023 22:25 UTC (Thu) by linuxrocks123 (subscriber, #34648) [Link] (1 responses)

If it's so complicated, why did every compiler that matters implement it decades ago?

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 4, 2023 14:19 UTC (Sat) by foom (subscriber, #14868) [Link]

When Rust attempted to use the LLVM IR implementation underlying this feature ("noalias"), it took multiple years of work fixing LLVM, before it no longer caused miscompilations. Most of those errors were also expressible in C code -- it was not simply that Rust is a different language. How can that be, given the age of this feature? Because it's so rarely used in C or C++ that those edge cases were not discovered. But then Rust came along and used it _everywhere_.

The current LLVM implementation doesn't even represent many forms of "restrict" -- often, the information is just dropped. There's been ongoing work in LLVM since 2019 (search for "full restrict") to enhance the representation. Not yet landed.

It's also unclear what the interaction should be, or is, between restrict and C++ operations like creating a reference from a pointer. E.g. given `int* restrict y` does `int& x = *y;` count as an access to the object that could violate restrict, even if you never read `x`? Or, how about calling a non-virtual member function, where the function body doesn't reference any member variables. Does that access the "this" object in a way that would violate restrict?

You might claim that the answer is obviously "no" for both, because there's no "actual" memory accesses. But that's only true under the "C++ as portable assembly" world. Per the specification, these do nominally access the object, and the compiler takes advantage of that to reorder loads for better performance. So, maybe it is/should be a violation of restrict? I dunno, who can really say, since restrict isn't specified for C++!

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 2, 2023 22:19 UTC (Thu) by bartoc (guest, #124262) [Link]

MSVC's __restrict has different semantics than C restrict.

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 3, 2023 8:23 UTC (Fri) by smurf (subscriber, #17840) [Link] (1 responses)

> #define __restrict__ restrict

That should be the other way 'round.

Sure, you can sprinkle "restrict" onto everything, but given a nontrivial program you're going to hit a case where that is wrong (or, worse, will be wrong in the future, when the compiler gains another class of optimizations), and thus introduce a bug that's almost impossible to find.

On the other hand, (safe) Rust implicitly tags everything as "restrict" and guarantees that that won't have adverse effects.

Bjarne Stroustrup’s Plan for Bringing Safety to C++ (The New Stack)

Posted Nov 6, 2023 2:29 UTC (Mon) by linuxrocks123 (subscriber, #34648) [Link]

Yeah, you're right. It should probably be:

#ifdef _MSC_VER
#define restrict __restrict
#else
#define restrict __restrict__
#endif

Of course you don't sprinkle restrict on everything. You only put it on parameters where the invariant actually holds.

But, yes, you do actually have to use restrict to get the benefits of restrict. However, the "alias analysis is easier in Rust, therefore we will beat C" argument strikes me as very similar to the "profiling is easier in the JVM, therefore we will beat C" argument. Like, okay, yes, for idiomatic code in both languages, you do have that one minor performance advantage, but you also have a lot of performance disadvantages, and you will still lose overall.

The reason you will still lose overall is because C/C++ still has a way to compile with profiling when it's important and still has a way to specify non-aliasing memory access when it's important. So, in any case where your advantage really matters, someone will do the small amount of optimization work necessary to negate your advantage.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds