News for hackers. Stuff that matters.
Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @05:08AM (#21471894)
This is a wonderfully written comparison benchmark and it deserves attention even for that reason alone. It knows its target audience, explains what's going on succinctly but completely, and avoids most of the usual benchmark pitfalls that result in comparing apples to oranges. Great job.

The one glaring issue that is ignored is the floating point model used. I understand Rust still doesn't have a usable equivalent to -ffast-math, so I assume it wasn't used for that reason. Some discussion of whether it's permissible in this algorithm (I believe so?) and how much advantage that might give to C++ seems crucial when performance is a priority.

Ironically, reading this has further convinced me that, for all of its disadvantages, I much prefer C++ to Rust for my needs. I'm sure others will draw the opposite conclusion and that's great. Rust is a language that clearly knows what it wants and if your priorities are aligned with that, the performance gap is shrinking and implementation-related reasons to avoid it are rapidly decreasing in number.

Re:Comparing Parallel Rust and C++ by dgellow on Thursday November 07, 2019 @05:17AM (#21471936)
> reading this has further convinced me that, for all of its disadvantages, I much prefer C++ to Rust for my needs.

What are your needs, if I may ask? I'm not asking this to start a thread "rust/C++ is better", I'm just curious regarding situations in which someone decides that one language matches their needs more than another, it's always interesting to see what boundaries people consider for those decisions.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @05:33AM (#21472007)
I mostly use C++ for numerical simulations in physics and associated code (analysis, some visualization, etc.). That means my primary consideration is the ease and convenience of writing high performance code for a narrow set of hardware. I care about the quality of tooling, especially for performance analysis (including things like likwid [1]) and GPGPU computation. I do not care about safety (memory or otherwise) - my code doesn't take arbitrary input, run on shared hardware, do much of anything over networks or have memory safety crashes.

From this rather narrow point of view, Rust does very little to help and quite a lot to hinder me. Rust is very much about memory safety - an issue extremely far down my list of concerns - and to me the borrow checker is an anti-feature I'd love to turn off. None of this is in any way an indictment of Rust - it looks like a very well designed language that knows what it wants to accomplish. It just happens to want the exact opposite things from what I want, and that's fine. I know I'm in a weird minority (most of the people who think like I do seem to be game engine developers).

[1] https://github.com/RRZE-HPC/likwid

Re:Comparing Parallel Rust and C++ by pdpi on Thursday November 07, 2019 @09:18AM (#21473358)
> I do not care about safety (memory or otherwise) - my code doesn't take arbitrary input, run on shared hardware, do much of anything over networks or have memory safety crashes.

This is a common misconception — safety and security are different things. If your simulations make extensive use of parallelism (and I can only assume they do), memory safety also helps you ensure correctness.

I can absolutely appreciate that you might lose other things to achieve that, and that this is not a trivial tradeoff to make (and I'll take you on your word that it is the wrong tradeoff for you!), but it's not as clear cut as "I don't need safety features because I have no security exposure".

Re:Comparing Parallel Rust and C++ by blub on Thursday November 07, 2019 @10:40AM (#21474128)
Memory management errors are just like regular bugs, but they have a disproportionate impact on security.

If the security requirements are permissive, what's the point of investing so much effort in preventing only one category of bug? There's no misconception.

Re:Comparing Parallel Rust and C++ by jerf on Thursday November 07, 2019 @12:48PM (#21475407)
"Memory management errors are just like regular bugs"

I'd have to disagree in this specific context of high-performance parallel numeric simulation. It is terribly easy for memory management errors to produce incorrect simulations that still "work" perfectly, because incorrect handling of floating point numbers will never crash the program, merely corrupt the results in arbitrary, but not necessarily easy-to-detect, ways. Bugs that cause visibly obvious incorrect answers or crash the program are far preferable to bugs that cause subtle errors. It's too easy to "correct" for a bug in a simulation by simply poking it until the buggy simulation seems to return to the behavior you expect, but two bugs don't make correct code.

If you don't really care about accuracy, it may not be a big deal, though... the "shape" of these errors is very difficult to wrap one's head around and I'd still question someone working for or with me fairly hard even so if they think they don't care about accuracy and so it's OK to write code that may or may not be thread-safe.

Re:Comparing Parallel Rust and C++ by affyboi on Thursday November 07, 2019 @01:42PM (#21476001)
To tack onto this point, I actually had a couple of issues where subtle memory management bugs led to slightly incorrect (not terribly incorrect) results when I was working on a graphics paper. The worst part about that was it wasn't immediately obvious that there even was an error, until much later when we realized that the results we were getting weren't consistent with what we expected.

Something glaring would be if we were getting values that were zeroed out or something like that, but when the only difference is that a slope is -1.4 instead of -1.6 or something like that, you don't immediately realize something's up.

Once we realized there was a bug, it took a long time to track down, and after that I was so frustrated I started writing a new library from scratch in Rust for our research and it worked out pretty well.

Anecdotal evidence for sure but it was really nice not having to triple check for memory race issues.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @03:38PM (#21477018)
Can you elaborate a bit on how the bug worked and why Rust would have prevented it? I'm genuinely curious because the only bugs I've ever had with comparable presentation and consequences have been logic bugs which no set of language features could possibly prevent.
Re:Comparing Parallel Rust and C++ by blub on Thursday November 07, 2019 @02:47PM (#21476625)
Why did it take a long time to track down the bug? You had a test case which triggered it, so in theory just running under valgrind would have been enough.

Or did it not happen in that case?

I'm not even gonna comment on the rewrite, that reads like an ad. :-)

Re:Comparing Parallel Rust and C++ by slavik81 on Friday November 08, 2019 @03:36AM (#21480740)
I really appreciate keldaris' posts, as his experience seems to be very similar to mine.

Many simulation algorithms involve manipulating large arrays of data. Rust can check that your indexes are within bounds, but it can't check that your indexes are correct. If you access X+1 to get the element to the left of X, but you should have accessed X-1, then you have introduced the same sort of error as you'd get from memory corruption (and, frankly, one that is much harder to debug. Standard library assertions, Valgrind and ASan are no help at all if you're accessing valid data.)

Nevertheless, I found it helped a lot to compile with `-D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG` during my normal development. Among other things, that adds bounds checks to all container accesses. I leave them off in release mode, as my full-scale simulations already take multiple hours and I need the performance.

Re:Comparing Parallel Rust and C++ by blub on Thursday November 07, 2019 @02:45PM (#21476607)
Any code base which does simulations would likely have some automated testing covering a good chunk of the value domain + extremes, which lends itself very well to ASan and UBSan analysis.

I don't think OP said anything about writing code that's not thread-safe. They specifically said they're carefully designing such code. What they said is that due to the way they handle memory, most related errors don't happen in practice - which is perfectly believable. Floating point errors are separate issue, they could happen in almost any language.

Re:Comparing Parallel Rust and C++ by jerf on Thursday November 07, 2019 @02:55PM (#21476708)
"ASan and UBSan analysis."

This is a personal quirk, but I consider C++ a different language from C++ + a lot of static analysis tools, on the grounds that most of my major beefs with C++ (and C) can largely be addressed by using powerful static analysis tools from day one. So from my personal, idiosyncratic perspective, I see two very different situations if the original poster is "using C++" or "using C++ and a suite of quality static analysis tools".

I prefer languages like Rust that take advantage of our decades of experience with C/C++ and write the most important stuff directly into the language, but in 2019 I still concede that "C(++) + good static analysis" is still a very economically compelling language for a lot of companies with existing C/C++ codebases. "Upgrading" to C+++SA is not cheap, but it's cheaper than any of the other options, as much fun as "rewrite in Rust" may be.

As for their belief that their code is thread safe, if they are using C++ and not C+++SA, I echo mike_hock's comment. It may be threadsafe, but from this distance and without a lot more evidence, my Bayesian priors say I don't believe it.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @04:03PM (#21477218)
> I see two very different situations if the original poster is "using C++" or "using C++ and a suite of quality static analysis tools".

I guess I'm somewhere in between on that spectrum. I use clang-tidy and valgrind constantly, along with an everchanging set of profiling tools that occasionally find a potential bug as well. I've used cppcheck in the past, but didn't find it particularly useful, and I use whatever VS2019's static analysis tool is called now if I'm on Windows. I'm also extremely pedantic about compiler warnings, in that I either have a specific reason for disabling a warning entirely for that codebase or it will get fixed. Exhaustive (often mathematically exhaustive) tests go on top of this.

That being said, I don't use any of the sanitizers in my usual workflow. The reason is simple - most of them don't offer anything I need. The huge set of bugs around excessive dynamic memory allocation simply doesn't arise when you don't do dynamic memory allocation, so ASan/LSan/MSan don't give me much. UBSan might theoretically help, but only with issues that have so far been very trivial to debug (accidentally misaligned pointer in explicit SIMD code and the like, results in instant crash and an immediate fix). The one exception is the ThreadSanitizer and when I happen to finally get a non-trivial deadlock, that'll certainly be my first stop. As it is, most of the multithreading I've done recently has been fairly trivial from a compsci point of view (and for good reason, overly fancy algorithms aren't often particularly performant on current architectures).

Regardless, I'm entirely open to the possibility that I've neglected a tool that might help me further. Strong tooling is one of the major advantages of C++ and I've tried most of the major tools at one point or another. I do require demonstrable benefit in the context I work in to continue using them, however, and I've often found it worthwhile to simplify my workflow by cutting something out that turned out to be mostly superfluous.

Re:Comparing Parallel Rust and C++ by comex on Thursday November 07, 2019 @03:44PM (#21477068)
ASan and UBSan are dynamic analyses.
Re:Comparing Parallel Rust and C++ by mike_hock on Thursday November 07, 2019 @02:28PM (#21476459)
As a rule of thumb, if "I don't care about memory safety and thread safety" is followed immediately by "my programs don't have memory corruption or race conditions," the latter statement is usually false.
Re:Comparing Parallel Rust and C++ by pdpi on Thursday November 07, 2019 @11:03AM (#21474369)
Like I said, it's a matter of safety versus security.

Use-after-free, concurrent read/writes, forgetting to acquire a lock are all problems that can crash an application or cause correctness issues. Preventing all of those falls under the "safety" heading, and is still useful in a security-permissive environment.

Now, what do you mean by "investing so much effort"? At the language level, it's valuable effort that lets us achieve security in many security-conscious applications, and we get the safety for free in less security-critical environments as a knock-on. Score! From an application developer's point of view, though, you're right. It might genuinely not matter, especially if you're not otherwise motivated to learn the language (a lot of that effort is a one-off and is amortised over your lifetime as a developer, if you choose to learn it). Not all applications benefit that much from the safety guarantees, and you might just be better off not even putting in the effort of using the language if something else is a better fit for you.

Re:Comparing Parallel Rust and C++ by thr0w__4w4y on Thursday November 07, 2019 @08:09PM (#21479169)
> This is a common misconception — safety and security are different things.

IMO this could either be read as: - "The misconception that safety and security are different things is common", i.e. they are not different, but the misconception is common. - "The misconception [above, that safety and security are not different things] is common - safety and security /are/ two very different things."

I hope you mean the latter (safety != security). I consult in security (hardware and software), and I came to security by way of high-availability, high-consequence systems.

I've seen many safe, but insecure systems. I don't know if it's possible to have an unsafe, secure system (I doubt it?) but absolutely, safety != security. There is an overlap of sorts, but to say that these 2 "are not different things" is why I have lots of work.

Wow, sorry, I'll get off my soapbox. I'm still not sure if perhaps we're in "violent agreement" as the saying goes.

Re:Comparing Parallel Rust and C++ by pdpi on Friday November 08, 2019 @04:28AM (#21480923)
The last sentence in my post would’ve revealed that yes, we agree they’re different things :).
Re:Comparing Parallel Rust and C++ by filmor on Thursday November 07, 2019 @05:59AM (#21472137)
When I was working in LQCD (lattice quantum chromodynamics), people were fighting quite a bit with corrupted memory, which only resulted in a crash in the best of all cases, usually it led to very or (worse) slightly weird results.
Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @06:13AM (#21472189)
I can't really comment on that case without seeing the code, but maybe you can describe how Rust might prevent those issues - how was the memory corrupted? In the kind of code I write, I find it extremely trivial to avoid the stereotypical memory safety issues. That doesn't mean I don't have bugs, obviously - I make logic errors like everyone else, but no language will ever protect you from that. That's what tests and debugging are for.
Re:Comparing Parallel Rust and C++ by pdpi on Thursday November 07, 2019 @09:24AM (#21473405)
Rust outright forbids a few Bad Things, such as "simultaneous" reads/writes on the same piece of memory, and read-after-free. E.g. reading partially-committed data from non-atomic writes is a really nasty form of data corruption that can easily occur in simulation-style code.

Of course, no language can protect you from all bugs, but some languages can protect you from at least some bugs. Rust can, and does, protect you from all data race-related bugs, and few other classes of issues.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @09:49AM (#21473628)
I partly addressed this in another comment already, but perhaps I can try to convey a bit more of why I'm completely unimpressed by that argument for my use case.

First of all, the "Bad Things" aren't actually uniformly bad. In its zeal, Rust won't let you modify different elements of the same array from different threads (an issue that comes up almost immediately in the OP) even though you know with perfect mathematical certainty that to do so is perfectly safe. In the context of simulation code that's not nitpicking, it's a usecase that comes up all the time, I can't even think of a codebase I've worked on in the last 5 years that didn't have this pattern in it somewhere. I have never, ever encountered a data race bug in any of those cases (even though there are usually tests to cover it just in case), but switching to Rust would immediately force me to waste time (and code quality, in my view) on working around a language restriction. The same goes for read-after-free and most other memory safety features, because most of the reasoning there simply doesn't apply when you're dealing with statically allocated memory that's never freed, with static guarantees on sizes, etc.

Rust is very deliberately designed to address safety and correctness issues that exist in a style of programming that's just completely orthogonal to what I do. There is some inevitably some overlap and it's always possible to construct a contrived case where Rust could potentially prevent a bug I could conceivably make, but the point is that everything I've seen leads me to conclude that any such benefit would be vastly outweighed by the time unnecessarily spent on working around non-issues, dealing with syntax verbosity and semantic baggage (I distinctly do not want the notion of "lifetimes" at the language level at all), all of which means more code and, in all likelihood, more bugs.

All of this sounds very negative, but I really don't mean to be. If you buy into the dynamic allocation heavy style of programming Rust is designed to improve, Rust looks great. It also seems to have an educated and thoughtful user community for the most part, something I actually envy a bit. I just don't think people should fall into the narrow view of thinking that Rust is a universal good - it takes a particular approach, with very stringent limitations and the limitations aren't there because Rust is bad, they are there to make it great... just not for everyone.

Re:Comparing Parallel Rust and C++ by steveklabnik on Thursday November 07, 2019 @11:16AM (#21474502)
> In its zeal, Rust won't let you modify different elements of the same array from different threads (an issue that comes up almost immediately in the OP) even though you know with perfect mathematical certainty that to do so is perfectly safe.

To be clear, Rust will let you do this, as long as you're communicating that you know this to the compiler. split_at_mut and scoped threads are two ways that this is possible without requiring you to write your own unsafe code.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @11:47AM (#21474809)
Right, the OP solved this using Rayon's par_chunks_mut, then enumerate and foreach over that, so I assumed that's the most ergonomic way to work around the issue. I certainly wasn't claiming that there are no workarounds, merely noting that from my - obviously biased - point of view this looks like yet another annoying hoop to jump through. There's obviously the opposite point of view which holds this up as a useful safety measure and determining which is correct requires knowing the context you work in. Given the vanishingly tiny amount of time I've spent on debugging issues this restriction might have prevented Rust just looks very unergonomic to me, but the opposite perspective can be just as valid given the right context.
Re:Comparing Parallel Rust and C++ by pixel_fcker on Thursday November 07, 2019 @01:58PM (#21476161)
I think the key difference here is that in your code you’re saying you “know” that it’s safe to write to these certain sections of the array from different threads. That’s an invariant that‘s held in your memory (or in a comment maybe).

The rust equivalent is doing the exact same thing but requires you to formalise that invariant in your code by using one of the methods defined above.

I can see how initially this looks like an extra hoop to jump through but after using it for a while I’ve found the opposite is true: because those invariants are checked by the compiler it’s one less thing I have to keep in my head and I can concentrate on the real problem.

Re:Comparing Parallel Rust and C++ by safercplusplus on Thursday November 07, 2019 @03:14PM (#21476855)
And also one less thing to transfer to the heads of anyone else that might need/want to understand the code. (Often the author him or herself at some point in the future, right?) Though equivalent facilities are available in C++ [1][2] (and can now/soon be enforced[3]).

[1] https://github.com/duneroadrunner/SaferCPlusPlus/blob/master...

[2] https://github.com/duneroadrunner/SaferCPlusPlus/blob/master...

[3] https://github.com/duneroadrunner/scpptool

Re:Comparing Parallel Rust and C++ by pdpi on Thursday November 07, 2019 @10:27AM (#21473999)
> it's always possible to construct a contrived case where Rust could potentially prevent a bug I could conceivably make, but the point is that everything I've seen leads me to conclude that any such benefit would be vastly outweighed by the time unnecessarily spent on working around non-issues

Yeah, I think at this point we're just agreeing very loudly :)

Rust absolutely has some objective advantages over C++, as does C++ over Rust. The choice of which set of (dis)advantages works best is very much something that can only be determined on a case-by-case basis.

Re:Comparing Parallel Rust and C++ by 87zuhjkas on Thursday November 07, 2019 @09:36AM (#21473525)
I think C++ might be a better choice if you don't care so much about correct results from your computations. I think there are certainly some scenarios where this is the case.
Re:Comparing Parallel Rust and C++ by FpUser on Thursday November 07, 2019 @10:49AM (#21474208)
This does not sound nice at all. What on Earth correctness has to do with avoiding C++?
Re:Comparing Parallel Rust and C++ by 87zuhjkas on Thursday November 07, 2019 @11:07AM (#21474410)
> Rust is very deliberately designed to address safety and correctness issues that exist in a style of programming that's just completely orthogonal to what I do.

My reasoning is: If rust addresses correctness issues that C++ does not, then it is more likely that rust programs produce correct computing results than C++ programs on average. But this is just an assumption and I might be wrong.

> This does not sound nice at all.

Ah that's true, sorry for that, I should have expressed it in a different (nicer) way.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @11:54AM (#21474870)
Since you quoted me, I'd like to clarify my statement. When I said:

> Rust is very deliberately designed to address safety and correctness issues that exist in a style of programming that's just completely orthogonal to what I do.

this doesn't in any way imply that Rust addresses correctness issues that all C++ code is subject to. Rather, Rust addresses correctness issues that are endemic in particular kinds of C++ code, which is qualitatively different from the code I find myself writing (probably much more due to domain specifics than any personal skill). Therefore, in that context, I don't see much reason to believe Rust will on average produce better results than C++ in terms of correctness, even though it might very well do so in a different context (like the context it was actually designed for).

That being said, I obviously do care about correctness. If I had reason to believe that Rust will on average lead to satisfactorily correct code with less effort than it would take me to achieve the same level of correctness in C++, absent any other major contraindications I would switch languages. Personally, I would be rather happy to switch away from C++ - unfortunately, most alternatives so far look considerably worse given the specific context I operate in.

Re:Comparing Parallel Rust and C++ by gameswithgo on Thursday November 07, 2019 @01:46PM (#21476045)
Rust has a lot of correctness features that are useful, and some not related to memory safety, for example 3 that come to mind:

options and results instead of null pointer or using bit flags to indicate invalid states (a recent sudo exploit would not have happened in a language with option types)

everything is an expression so you do not have to create uninitialized variables and then set them later inside a switch or if statement.

much less (no?) undefined behavior

for someone working in a particular C++ niche who has developed strategies to avoid all of these problems already, then switching to Rust certainly may not be worth the cost involved in learning something new, but if you were to start from scratch and pick one of the two languages, there might be good reasons to pick Rust for the same task.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @04:35PM (#21477556)
You can have options and results in C++ if you like (I sometimes use custom result types, and I certainly don't use exceptions), but there's no language-level support for them and that's valuable, I agree. Not sure I understand the second point (I don't have to create uninitialized variables in C++, though I may sometimes want to). As for undefined behavior, I don't personally view that as an issue at all for the most part. I write code for a specific set of compilers running on a specific set of hardware, not an abstract standard. The behavior is what the compiler does (or rather, what I cause it to do) and there's nothing undefined or arbitrary about that.

Anyway, I agree that some aspects of Rust unrelated to memory safety are good for correctness. Unfortunately, I can't pick languages in a vacuum, so I have to weigh that against things like GPGPU support (first rate vs. non-existent), tooling quality (particularly profilers), library support (Eigen alone is worth quite a lot) and other factors. If I could ignore all of those real world issues and just choose the better language, I don't know if I would choose Rust, but it would certainly have a decent shot.

Re:Comparing Parallel Rust and C++ by lmm on Friday November 08, 2019 @05:06AM (#21481053)
> You can have options and results in C++ if you like (I sometimes use custom result types, and I certainly don't use exceptions)

It's not really practical because C++ has no true sum types. You can emulate them with a Java-style visitor pattern but that carries an immense code overhead.

Re:Comparing Parallel Rust and C++ by lenkite on Friday November 08, 2019 @08:32AM (#21481988)
You have std::variant and std::visit. https://www.bfilipek.com/2018/09/visit-variants.html Or you can use a library: https://github.com/mpark/patterns
Re:Comparing Parallel Rust and C++ by lmm on Friday November 08, 2019 @09:28AM (#21482429)
> You have std::variant and std::visit. https://www.bfilipek.com/2018/09/visit-variants.html

Which isn't a true sum type because it doesn't nest properly.

> Or you can use a library: https://github.com/mpark/patterns

Interesting; proper pattern-matching is nice, but the lack of type safety is still a major issue.

Re:Comparing Parallel Rust and C++ by filmor on Friday November 08, 2019 @02:54AM (#21480615)
One thing I remember vividly was an off-by-one error off a colleague in a nested loop. Since we were using periodic boundary conditions, the result looked almost right, but of course the very last read would be garbage. Depending on the lattice size this was not a glaring problem, but sometimes the very last (out-of-bounds) read would trigger a segfault.

Incidentally, I rewrote that piece of code using a self-made `lattice_iterator` in C++ that "linearised" the code just to simplify it and make debugging easier and everything started to work. We found the off-by-one error afterwards by comparing the iteration behaviour of a single run.

This kind of problem can be caught by tests (though it's difficult, you can't tell me that you are really testing each individual iteration behaviour of your code), the advantage of Rust's approach is that this wouldn't even have passed the compiler.

Re:Comparing Parallel Rust and C++ by nwallin on Friday November 08, 2019 @12:16AM (#21480120)
It sounds like you were dealing with code written by physicists. Which probably means they were doing literally everything wrong. And there's a snowball's chance in hell you'd be able to convince them to rewrite it in rust, because "it's too hard". And even if you convinced their boss to force them to rewrite it in rust, everything would be in unsafe blocks. And they'd complain the language is too verbose because every function has to be cluttered with unsafe blocks. They wouldn't complain about the unnecessary indentation though, because all of their code either isn't indented or is nested off the right side of the screen.

Not that I'm bitter. (I'm bitter)

Academic code being shitty can't be fixed by inventing a better language.

Re:Comparing Parallel Rust and C++ by Razengan on Thursday November 07, 2019 @07:40AM (#21472637)
> I know I'm in a weird minority (most of the people who think like I do seem to be game engine developers).

As an amateur game engine developer, I'm probably in a weird minority for abhorring C/C++/C#/Canything. :)

I love Swift, and Rust or Dart would be my next choices if Swift wasn't available.

With first-class support for native APIs which do most of the work anyway, performance has not been a concern for me on Swift. I see only benefits, like making it hard for some types of bugs to creep in and code that is pleasant to read and a joy to write (though you can write beautiful/ugly code in any language, of course.)

Re:Comparing Parallel Rust and C++ by bulldoa on Thursday November 07, 2019 @01:14PM (#21475691)
Not well verse in the subject, do people develop game engine in garbage collected languages? Intuitively I would think game engine requires absolute performance
Re:Comparing Parallel Rust and C++ by gameswithgo on Thursday November 07, 2019 @01:41PM (#21475984)
Sometimes. And sometimes the core engine may be in C++ but the game logic is in C# (Unity).

There are plenty of games whose performance demands are not so high, that a GC language can work fine. There are also many examples of realtime 3d games, that used garbage collection, and were extremely successful (minecraft, subnautica). These were high quality games, though both do suffer from performance downsides, related at least indirectly to using GC languages.

Re:Comparing Parallel Rust and C++ by mratsim on Thursday November 07, 2019 @04:15PM (#21477337)
There is a growing community of game developers using Nim.

But Nim doesn't have a single GC, it has multiple and the default one can be compiled with real-time latency constraints so that you can use to make sure that you don't stop the world for less than 1/200 of a second for example.

Plus the GC is per type, you can mix raw pointers for manually managed memory and references for GC-managed objects. The GC can only be triggerred in code paths with references.

Re:Comparing Parallel Rust and C++ by nwallin on Friday November 08, 2019 @12:35AM (#21480186)
Game engines absolutely not. They're almost all written in C++ with exceptions and RTTI disabled and no STL. Gamdev C++ can better be thought of as C+. It's kinda sorta not the same language.

However, a lot of game engines use GC languages to operate game logic. Lots of them use Lua, unity uses c#. The ubiquity of Lua in gamedev has resulted in luajit being shockingly good if you're pushing around a bunch of floats.

Re:Comparing Parallel Rust and C++ by Razengan on Thursday November 07, 2019 @08:32PM (#21479282)
For things like the latest Doom or pushing 3D hardware to its limits, I'd think no.

I can't say how far you could get in pure Swift for 3D engines, but for 2D games Swift is more than good enough, and you can always interop with C/C++ when you really need to.

Re:Comparing Parallel Rust and C++ by RMarcus on Thursday November 07, 2019 @02:07PM (#21476267)
My experience is entirely different -- writing HPC code for supercomputers at Los Alamos National Lab (on and off for 5 years) made me a true Rust believer.

One of the things I spent the most time on with Fortran / C++ codes was debugging wrong-result bugs. About 90% of the time, the wrong result came from some edge-case where an array was wrongly freed too early, an array was accessed out of scope, or a race condition caused an array member to be updated in a non-deterministic manner. Each of these bugs required hours of debugging and was a huge time sink. Once I started working with Rust, I never encountered any of these bugs. After about a year of fighting the borrow-checker, I feel my overall efficiency has greatly improved.

Now, when I go back and write or read C++ code, patterns that the Rust compiler would yell about jump out at me (multiple unprotected mutable references, cloning unique pointers), and I find these are generally a source of the bug I'm hunting. Like sibling comments point out, a lot (but not all) of the things Rust stops you from doing are just bad practice anyway.

Of course, for GPGPU stuff I have to write CUDA or OpenCL, but those are generally small, compact kernels that are easy to reason about end to end.

I'm not suggesting that you are doing this, but for me, I initially resisted Rust for a long time. Rust seemed extremely complex, and whenever I'd try to use it I would run into a wall. The loud Rust community talking about how great Rust was and how easy it was to use once you "got it" made me feel stupid. Instead of being humble, I became arrogant, and I'd say things like "Rust is too restrictive for the high performance applications I care about" or "I write code that Rust would find unsafe but is actually super well-tuned for this architecture." For me, these were mental excuses I made because I was unable to accept that I was having such a hard time with Rust, and I considered myself a "high performance computing software engineer!"

It took me way longer than most to "get" Rust -- over a year of repeatedly forcing myself to learn and stumble through compiler errors before things started to click. A year after that, and I'm still frequently surprised by certain aspects of the language ("really? I need a & in that match statement?" and "oh god, what does this lifetime and trait bound mean..." are two of the most common). But the parts of Rust that have clicked for me (the borrow checker and associated lifetime mechanics) make Rust very enjoyable to write.

Again, I'm not suggesting that you are falling into the same trap I did, I just wanted to post this to encourage anyone else in the "banging their head against the Rust compiler" stage to power through!

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @04:26PM (#21477473)
Thank you for sharing your experience! I'd be very curious to hear more about your experience in doing GPGPU work in Rust - it was my understanding that there was virtually no tooling, libraries or support for that kind of thing beyond the existence of the C FFI.

On the broader point, I suspect a large part of the reason we've had such contrasting experiences is just a radically different mindset behind the C++ codebases we've dealt with. Wrongly freed or out of scope arrays scream of exactly the kind of C++ code Rust was designed to address, and as far as I can tell it is indeed great at doing that. On the opposite extreme, when you have statically determined sizes and bounds, all allocations happen at startup and nothing ever gets freed, that entire class of issues simply doesn't arise in the first place. The reason why the overwhelming majority of the bugs I debug are either silly typos or plain logic errors isn't because I'm particularly good at this, it's just a different approach to programming that's easy to pull off in simulation code (or embedded systems, or game engines), but probably rather more difficult in other kinds of applications.

Anyway, I'm glad you're enjoying Rust and I hope it'll have more of a scientific / numerics / GPGPU ecosystem in the future. More viable languages can only be a good thing for us computational scientists.

Re:Comparing Parallel Rust and C++ by wyldfire on Thursday November 07, 2019 @07:39AM (#21472633)
> I do not care about safety (memory or otherwise) - my code doesn't take arbitrary input, run on shared hardware, do much of anything over networks or have memory safety crashes

How do you know it's the case that you don't have memory safety defects in your implementation?

It's almost certainly not the case that you don't care about safety. Out of bounds accesses and writes would make the simulation defective. Defective simulations are useless. However it might make sense if you said that you don't find yourself fixing defects like these often.

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @09:29AM (#21473456)
I fix out of bounds errors fairly frequently, but every single one of them is a trivial typo that is caught instantly and takes under 10 seconds to fix and re-test, literally. The reason for that is that in my domain all the memory requirements that matter [1] are known statically down to exact sizes, which are mathematically provable. Making good use of this fact makes most memory safety bugs either impossible (like the usual double frees, use after free, etc.) or trivial to address (like out of bounds accesses). The common approach, which frankly horrifies me, of dynamically allocating different objects all over the place at runtime and trying to care about it to the least possible extent is just utterly alien in this domain (which I think is the point of similarity to game engines). Consequently, things like the borrow checker, that are fundamentally designed to make this common approach safer and less bug prone elicit a "um... why?" sort of reaction from people like me, because they seem to be completely beside the point [2].

[1] Yes, there are scratch buffers for things like log output and other details of convenience. None of them impact the actual simulation and all are easily handled by a trivial bump allocator over a static buffer. Bugs are unlikely and rare, but if they do happen they don't really affect anything of consequence.

[2] The borrow checker is just a random example, I also tend to avoid most of "modern" C++ for the same reasons and write in a fairly orthodox style.

Re:Comparing Parallel Rust and C++ by steev on Thursday November 07, 2019 @08:27AM (#21472940)
Not to get too much into the weeds, but in my experience in HPC for numerical work out-of-bounds read/writes are almost never an issue. I say almost because I'm sure someone somewhere as slipped up, but I've literally never had this problem. A priori you know your bounds and looping within them is trivial. I don't think I've ever encountered any data where you did not know the dimensions at program startup. I have experimented with Rust because I thoroughly enjoy the language (but am not an expert), and immediately got bit by bounds checks during vector indexing.

I also find if you write modern, idiomatic C++ code you rarely, if ever, have to worry about memory safety issues.

Re:Comparing Parallel Rust and C++ by pcwalton on Thursday November 07, 2019 @10:26AM (#21473988)
> I also find if you write modern, idiomatic C++ code you rarely, if ever, have to worry about memory safety issues.

This is empirically not true. Tons of memory safety issues are found (and exploited) all the time in modern idiomatic C++ codebases.

Re:Comparing Parallel Rust and C++ by FpUser on Thursday November 07, 2019 @10:59AM (#21474318)
This is very generic statement. I do write servers for example that run non stop and frankly I've long forgotten when the production version caused memory issues. Basically combination of being diligent, using memory leak detection tools and certain programming style works for me just fine.

For example my latest game server is up more than 2 month already and the only reason it gets restarted is that I am updating it with the new version.

Re:Comparing Parallel Rust and C++ by pcwalton on Thursday November 07, 2019 @11:04AM (#21474377)
You get a very different view when you're looking at actively attacked codebases. Memory safety issues are everywhere.

https://twitter.com/lazyfishbarrel is very much worth reading.

Re:Comparing Parallel Rust and C++ by pnako on Thursday November 07, 2019 @02:18PM (#21476349)
That twitter account mostly reports security bugs in 20-year-old C libraries. Nice, but hardly an argument against "C/C++", and completely irrelevant to people writing numerical code in modern C++.

For years, we've been telling newbies not to use the expression "C/C++", which is incorrect. Now the Rust community disingenuously keeps pushing this outdated meme; I say it's disingenuous because they are informed enough to know that C and C++ are distinct languages, yet they use the well-known flaws of C to attack C++ when it can advance their moral crusade for memory safety.

Re:Comparing Parallel Rust and C++ by pcwalton on Friday November 08, 2019 @02:40PM (#21485923)
Browsers are not "20-year-old C libraries".

These issues are every bit as much of problems in C++. In fact, there is a reasonable argument that modern C++ is less safe than old C++, because of features like lambdas that practically invite use-after-free.

Re:Comparing Parallel Rust and C++ by pnako on Friday November 08, 2019 @05:49PM (#21487738)
Browsers depend on tons of 20-year-old C libraries. At the moment, the top link from the Twitter account you gave above is this one from November 6: https://twitter.com/LazyFishBarrel/status/119228101802954342...

It reports a total of 37 issues in:

  - freetype2 (C lib, 20+ years old)
  - usrsctp (C lib, age unknown)
  - libexif (C lib, age unknown)
  - libxslt (C lib, 20+ years old)
  - imagemagick (C lib, 20+ years old)
  - mruby (C)
  - php (C)
  - openSSL (C, 20+ years old)
  - curl (C lib, 20+ years old)
  - ffmpeg (C lib, 18 years old)
  - ghostscript (C lib, 30 years old)
  - irssi (C, 20 years old)
In that list were also Skia and libsass, two projects actually written in C++.

In Sass, the issue is a nullptr issue: https://github.com/sass/libsass/issues/3001

In Skia the bug was in intrinsics code: https://skia.googlesource.com/skia/+/0f55db539032a23b52897ae...

Of course that's a single data point, but it shows what I think is a reasonable argument: most of the issues indeed happen in (old) C code, for well-known reasons (no standard string, array or collection support, no RAII), but because C++ supports those things by default it largely avoids those issues.

Re:Comparing Parallel Rust and C++ by pnako on Thursday November 07, 2019 @01:56PM (#21476147)
Most of those are issues in old, _C_ code. No one disputes the fact that C is a mine field due to its complete lack of support for things like arrays and strings.

But that's not really an issue in modern C++. It's only really a problem when you want to implement your own data structures with raw pointers, in which case, yes, you have to be careful and write tests and use sanitizers, Valgrind, etc.

Re:Comparing Parallel Rust and C++ by FpUser on Thursday November 07, 2019 @11:42AM (#21474757)
I can not be responsible for however said highly attacked systems were designed and hence can not judge.

In my own cases I use proprietary protocols for client-server communications that more or less ensure that memory bounds are not broken.

Of course attackers might be able to punch holes in lower layers ( UDP for example ) over which I have no direct control but in this case Rust would use the same UDP stack and offer no advantage.

Re:Comparing Parallel Rust and C++ by mcqueenjordan on Thursday November 07, 2019 @12:27PM (#21475167)
I’m glad to hear that your code is unbreakable and without any bugs, but pcwalton’s claim is still absolutely correct.

> This is empirically not true. Tons of memory safety issues are found (and exploited) all the time in modern idiomatic C++ codebases.

Re:Comparing Parallel Rust and C++ by FpUser on Thursday November 07, 2019 @12:36PM (#21475253)
"I’m glad to hear that your code is unbreakable and without any bugs, but pcwalton’s claim is still absolutely correct"

I smell sarcasm here. I do not claim my code to be unbreakable. I do believe it is REASONABLY safe by design. pcwalton's claim is generic claim about generic code that may have no relevance to particular situations. Mine for example

Re:Comparing Parallel Rust and C++ by jcelerier on Thursday November 07, 2019 @12:33PM (#21475224)
let's be serious, chromium and firefox are more 90s style codebases than 2010s. There's thousands of raw malloc calls when I grep in the chromium source tree, and let's not even start talking about firefox where in the same file you've got :

- raw mallocs : https://github.com/mozilla/gecko/blob/central/dom/plugins/ip...

- new / delete : https://github.com/mozilla/gecko/blob/central/dom/plugins/ip...

- "whatever.Allocate<T>" : https://github.com/mozilla/gecko/blob/central/dom/plugins/ip...

and that's not limited to a single file... look at this :

https://github.com/mozilla/gecko/blob/3e6d6e013400af38f85ceb... - some malloc and new, again

- you also get some unique_ptr (because "modern" m'see) : https://github.com/mozilla/gecko/blob/3e6d6e013400af38f85ceb...

- moz_xmalloc because why not ? https://github.com/mozilla/gecko/blob/3e6d6e013400af38f85ceb...

- oh and did you know about our own custom reference counting pointer ? https://github.com/mozilla/gecko/blob/3e6d6e013400af38f85ceb...

etc etc... when you've got 35 different ways to allocate objects used willy-nilly of course things go wrong. Most modern codebases only ever use automatic storage, and unique / shared_ptr.

Re:Comparing Parallel Rust and C++ by pcwalton on Thursday November 07, 2019 @01:52PM (#21476095)
The issues have nothing to do with the differences between STL smart pointers and Mozilla/Chromium smart pointers.
Re:Comparing Parallel Rust and C++ by nwallin on Friday November 08, 2019 @01:21AM (#21480327)
They are '90s codebases, they're not just "like" '90s codebases. Firefox obviously dates back to Netscape, and webkit (which Safari, Chromium, Opera, Vivaldi, and now Edge are based on) is a fork of KHTML.

Mozilla could have achieved 90% of what it wanted from a rust rewrite with a modern C++11 rewrite at a quarter of the cost. Linter rules that say "no new or delete", "either unique_ptr<T> or shared_ptr<const T>", and "only construct unique or shared ptr via make_unique and make_shared" get them like three quarters of the way there.

The thing that makes rust great is that the static analyzer is built into the compiler and has strict defaults. C++ is the same language, but clang-analyze and clang-tidy are shipped as separate packages and have more permissive defaults.

Re:Comparing Parallel Rust and C++ by pcwalton on Friday November 08, 2019 @02:16PM (#21485689)
Those rules are completely insufficient. It's worth looking at the actual vulnerabilities here.

There is a reasonable argument that modern C++ is less safe than old C++, because features like lambdas are very prone to use-after-free.

Re:Comparing Parallel Rust and C++ by jzoch on Thursday November 07, 2019 @02:24PM (#21476412)
>being diligent, using memory leak detection tools and certain programming style

Thats the whole point. You need to do these things in C++, not in rust. In rust you get it for free and dont need to be an expert and use runtime detection tools or even static analyzers besides your compiler (w.r.t to memory safety and some classes of data races. These things can be useful in other domains)

People make the same assertions about dynamically typed languages at scale and how you "only" need to write tests that assert the types or "i wrote the function and know which type is passed duh" or "i write unit tests that would catch this" when a statically typed language tells you at compile time whether or not it will work. No intelligence required.

Re:Comparing Parallel Rust and C++ by FpUser on Thursday November 07, 2019 @04:50PM (#21477684)
You have a valid point. However practically speaking smart pointers in C++ eliminate most of headaches. At least for me personally so I do not really consider it a big nuisance. But yes I agree that for many people choosing Rust could be preferred way.
Re:Comparing Parallel Rust and C++ by MaulingMonkey on Friday November 08, 2019 @08:33PM (#21488901)
> memory leak detection tools

Hopefully you're not only relying on those - valgrind, address sanitizer, fuzzing tools, static analysis, etc. are a must for network-facing C++ (or unsafe Rust) as far as I'm concerned. You're not just looking for leaks, but use after free bugs, single byte overflows, bad casts triggered by bad data, and a whole slew of other potential problems.

Re:Comparing Parallel Rust and C++ by jandrewrogers on Thursday November 07, 2019 @06:59PM (#21478765)
I can't speak for all modern C++ code bases but this assertion is manifestly false in every modern C++ code base I have come in contact with for a long time now. There have been some gross exploits in publicly audited "safe" Rust code in recent months -- does this mean no one should use Rust? Are you going to make a hobby of denouncing Rust in public forums as a consequence?

I don't understand the desperate need to paint all modern C++ code bases as dangerously unsafe. It is demonstrably not true and doesn't reflect well on the motivations of those that would blindly assert it. Modern C++ has many issues and, like all programming languages, is the scene of many bugs. Just not memory safety issues. Furiously asserting that memory safety is an issue does not manufacture fact.

Re:Comparing Parallel Rust and C++ by pcwalton on Friday November 08, 2019 @02:24PM (#21485759)
> I don't understand the desperate need to paint all modern C++ code bases as dangerously unsafe.

Because the idea that security vulnerabilities can be fixed by just "modernizing" C++ codebases is actively harming security, by discouraging investment in memory-safe languages.

> It is demonstrably not true and doesn't reflect well on the motivations of those that would blindly assert it.

It is demonstrably true, as http://twitter.com/lazyfishbarrel shows. Perhaps consider that those of us who work on browsers, which are some of the largest most-attacked pieces of software in the world, would know what we are talking about.

Re:Comparing Parallel Rust and C++ by zarkov99 on Thursday November 07, 2019 @09:22AM (#21473391)
I work in a domain where memory defects do matter, but with moderately disciplined development, that is no naked news/deletes, no naked array access, no locks, etc, we hardly see any memory problems in a high concurrency, high throughput, mission critical environment. At least for my group, Rust's memory safety focus would not move the needle.
Re:Comparing Parallel Rust and C++ by vbarrielle on Thursday November 07, 2019 @08:27AM (#21472934)
The very popular C++ linear algebra library Eigen has bounds checking for matrices by default, but you can turn them off when defining NDEBUG. This means most out of bounds accesses are found during development.
Re:Comparing Parallel Rust and C++ by bluGill on Thursday November 07, 2019 @08:37AM (#21473022)
Memory safety defects tend to manifest themselves in obvious ways. A few unit tests, and some work in memory sanitizes will find them.
Re:Comparing Parallel Rust and C++ by pcwalton on Thursday November 07, 2019 @10:27AM (#21474009)
This is empirically not true. A look through https://twitter.com/lazyfishbarrel confirms this.
Re:Comparing Parallel Rust and C++ by bluGill on Thursday November 07, 2019 @03:02PM (#21476776)
The exceptions tend to escape notice for years. They get a lot of attention (and are often really hard to fix unlike the early ones), but I stand by my statement: most are easily found and fixed - but they are also fixed early in development so you don't hear about them.
Re:Comparing Parallel Rust and C++ by RaleyField on Thursday November 07, 2019 @04:35PM (#21477552)
If it's so obvious then why am I receiving security patches for my Linux desktop almost every day?
Re:Comparing Parallel Rust and C++ by bluGill on Friday November 08, 2019 @08:02AM (#21481799)
Many reasons.

There is a long tail of exceptions to my statement, hard to find things that escape notice for years.

There are a lot of security issues that are not really memory safety as we are talking about here. (many are memory safety in a way that has nothing to do with getting your allocate/free wrong - using uninitialized memory for example). Some of them are subtle new attacks that were just discovered and now need to be mitigated.

Re:Comparing Parallel Rust and C++ by integricho on Thursday November 07, 2019 @08:18AM (#21472871)
Luckily, we have sanitizers now.
Re:Comparing Parallel Rust and C++ by red75prime on Thursday November 07, 2019 @05:47AM (#21472071)
How are you dealing with data races? Manual synchronization (memory barriers, and the like)?
Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @05:58AM (#21472135)
For the vast majority of cases I've found it possible (and very much worthwhile) to just think a bit harder and avoid race conditions by construction (for instance, reformulating an algorithm so that writes only occur to different cache lines in a single pass, etc.). That's obviously not always feasible, but in my experience people give up far too quickly.

For the remaining few cases - yes, manually inserting the minimal necessary amount of synchronization (on x86-64 that's often a single instruction). On GPUs you obviously need memory barriers fairly frequently, but that's much less of an issue than on CPUs. Anyway, I don't have any faith in "smart" compilers or language features (which have large costs elsewhere) that try to free me from the simple necessity to carefully think through what my algorithm actually does before I implement it. If you care about getting close to optimal performance, you'll have to do the thinking anyway.

Re:Comparing Parallel Rust and C++ by FiberBundle on Thursday November 07, 2019 @07:11AM (#21472456)
Thinking through potential race conditions is certainly the right thing to do, but we still make mistakes and concurrency bugs are hard to find and sometimes even to notice. I'd still rather have the borrow checker keep an eye on my code and if its unnecessarily conservative in a situation that you know is thread-safe you can still use unsafe code.
Re:Comparing Parallel Rust and C++ by dgellow on Thursday November 07, 2019 @05:34AM (#21472016)
Thanks for the answer! :)
Re:Comparing Parallel Rust and C++ by mratsim on Thursday November 07, 2019 @05:13AM (#21471917)
ffast-math is not needed when you unroll manually and write your own accumulators.

See my benchmarks [1] with fast-math in the middle and the generated assembly comparison at the bottom.

The base language is Nim but pure C code should generate the same assembly.

[1] https://github.com/numforge/laser/blob/e660eeeb723426e80a7b1....

Re:Comparing Parallel Rust and C++ by keldaris on Thursday November 07, 2019 @05:22AM (#21471956)
That's absolutely right (and thank you for the reference), but this benchmark starts off with fairly naive code where -ffast-math may still help considerably.
Re:Comparing Parallel Rust and C++ by mratsim on Thursday November 07, 2019 @05:29AM (#21471989)
If in-between the start and the end, I went up to 8 accumulators and in files in the same folder I have the same with SSE and AVX instructions.

I actually wanted to know exactly what ffast-math did, down to the codegen (so you have assembly at the bottom).

Re:Comparing Parallel Rust and C++ by nwallin on Friday November 08, 2019 @01:52AM (#21480424)
-ffast-math quickly loses its value once you start dispatching raw intrinsics. -ffast-math tells the compiler "just figure or the math for me, I know what I want but don't know how to get it", but raw intrinsics say "use this instruction, but deal with manual register management for me."

If you take the code `v /= v.length();` and given -ffast-math the compiler can easily compile that as a mul, two fmas, an rsqrt, and three muls.

But if you take the code: (can't format right on mobile) (edit: fixed on desktop)

  __m256 len = _mm256_mul_ps(v.x, v.x);
  len = _mm256_add_ps(len, _mm256_mul_ps(v.y,v.y));
  len = _mm256_add_ps(len, _mm256_mul(v.z,v.z));
  len = _mm256_sqrt_ps(len);
  v.x = _mm256_div_ps(v.x, len);
  v.y = _mm256_div_ps(v.y, len);
  v.z = _mm256_div_ps(v.z, len);
You've told the compiler exactly which instructions to execute. And it will do so faithfully even though it's like two orders of magnitude slower.

I would be very interested to see -Ofast used before SIMD intrinsics start getting thrown around, but it has no value afterwards. The code in the linked article is eventually almost all SIMD intrinsics (v4?) so it won't make a difference.

Re:Comparing Parallel Rust and C++ by mkbosmans on Friday November 08, 2019 @03:50PM (#21486589)
What are you talking about? Intrinsics don't force the compiler to use a specific instruction. It's not even a 1:1 mapping.

I think all compilers will fuse separate add and mul instrinsics to fmadd.

Both GCC and Clang will convert the _mm256_div_ps to a vmulps. GCC calculates the scaling factor by combining vsqrtps and vrcpps. Clang will emit a vrsqrtps instruction, but as that has only 12 bits of guaranteed accuracy, it fixes the result up before using it to scale x, y and z.

https://godbolt.org/z/rG2rH8

Re:Comparing Parallel Rust and C++ by pcwalton on Thursday November 07, 2019 @10:35AM (#21474087)
Honestly, a lot of this comes down to whether you prefer iterators or C-style for loops. I prefer iterators myself, as it's way too easy to make C-style for loops unreadable. Reasonable people can disagree, of course.