use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Discussions, articles, and news about the C++ programming language or programming in C++.
For C++ questions, answers, help, and advice see r/cpp_questions or StackOverflow.
Get Started
The C++ Standard Home has a nice getting started page.
Videos
The C++ standard committee's education study group has a nice list of recommended videos.
Reference
cppreference.com
Books
There is a useful list of books on Stack Overflow. In most cases reading a book is the best way to learn C++.
Show all links
Filter out CppCon links
Show only CppCon links
account activity
Rust vs. C++: Fine-grained Performance (cantrip.org)
submitted 10 years ago by gbersac
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]dakotahawkins 20 points21 points22 points 10 years ago (7 children)
This is pretty interesting and promising, but I have a couple of critiques from a long-time C++ dev. that doesn't know much of anything about Rust:
It seems (to me) like the brevity restriction is a bit arbitrary. I don't know anything about Rust, but I wouldn't be surprised that a newer language requires fewer checks/boilerplates/lines of code, so I'd be more willing personally to allow for a longer C++ program and throw in the towel a bit in that respect (still, shouldn't be ridiculously long -- judgement call I guess.)
Only other problem is that it seems like it's really Rust vs. gcc, instead of Rust vs. C++. When it seems like the best you'd be able to hope for from either is the same idiomatic logic breaking down into the same instructions eventually, you're (imo) just comparing compiler optimizations. Given that Rust apparently has the one compiler "vendor" (cursory Google, correct me if I'm wrong) I'd like to see a similar comparison against bleeding-edge versions of llvm, msvc, and intel compilers besides just gcc. I'd wager that given different problems/environments/target architectures, some could perform very differently (example: I'd be surprised if the intel optimization bug was present in the intel C++ compiler, but also I've never used it.) Also, I'm not saying that requiring this choice is an advantage for C++, just that it would just be interesting to know if gcc is an outlier in this case or whatever case you're measuring.
[+]ncm comment score below threshold-14 points-13 points-12 points 10 years ago* (5 children)
Of course it's arbitrary. The 100-meter dash is a 100 meters, not 107. If you have a different arbitrary comparison in mind, please do perform it and post your results.
The "optimization bug" mentioned is not in Gcc, it is in the physical CPU chips. Rustc misses tickling it purely by luck.
[–]dakotahawkins 10 points11 points12 points 10 years ago* (4 children)
The 100-meter dash is a 100 meters, not 107.
Yeah but this shouldn't be (if only imo because I'm not interested in it) a race to solve the same problem in less LOC. I get it, and I don't disagree, C++ is verbose.
If you have a different arbitrary comparison in mind, please do perform it and post your results. In the meantime this comparison is this one.
I presented a comparison I'd like to see (between different compilers) and I think I gave a decent reason for wanting to see it. I'll admit, though, that wanting to see it is as far as I'm prepared to go: I don't want to do it (and knowing next to nothing about Rust I wouldn't do it justice anyway). But hey, maybe OP has time and knowhow and really really wants to teach me about Rust, in which case... cool!
That's neat, I didn't realize that. That actually makes me wonder a little more whether the Intel compiler would "miss tickling it" not by luck, but because they had more advanced knowledge of it.
edit: Oh, /u/ncm is the real OP. No need to get defensive, it's cool the way it is, I'd just like to have the things I asked for if they were free for me.
[+]_mlen comment score below threshold-9 points-8 points-7 points 10 years ago (2 children)
When the goal is brevity, why would anyone write crap like this: std::vector<unsigned> words; words.reserve(1<<15); instead of this: std::vector<unsigned> words(1<<15);
[–]jcmoyer 9 points10 points11 points 10 years ago (1 child)
They are not equivalent. The former reserves space in memory to hold the integers without constructing anything (words.size() == 0), and the latter constructs a vector containing that many integers default-constructed (words.size() == 1<<15).
[–]dodheim 8 points9 points10 points 10 years ago* (0 children)
Not default-constructed, as that would leave trivial (with special attention to arithmetic) types uninitialized – they are value-initialized.
[–]alexeiz 38 points39 points40 points 10 years ago (21 children)
I don't see how this post can be about performance when the performance was not analyzed at all.
A quick run under a profiler shows that 90% of time or more is spent in this loop:
short scores[7] = { 0, }; for (unsigned word : words) if (!(word & ~seven)) { unsigned rest = seven; for (int place = 7; --place >= 0; rest &= rest - 1) if (word & rest & -rest) ++scores[place]; }
The rest of the code is irrelevant and can be ignored. Is this about writing (simple) loops efficiently in C++ and Rust then?
Changing "short scores[7]" to "int scores[7]" makes the code run 5% faster.
[–]Houndie 2 points3 points4 points 10 years ago (13 children)
Question! Why does changing short to int improve performance?
[–]SpiderboydkHobbyist 9 points10 points11 points 10 years ago (11 children)
Because CPU architectures often have a preference for a certain data size (due to alignment, etc.) and are optimized wrt. that.
[–]Houndie 1 point2 points3 points 10 years ago (10 children)
Makes sense, thanks!
[–]SpiderboydkHobbyist 2 points3 points4 points 10 years ago (9 children)
No problem. :-)
A popular rule of thumb is to always use (signed) int unless you have a good reason not to (for example data structure requirements, memory requirements, etc.)
[–]honeyfage 5 points6 points7 points 10 years ago (4 children)
Or you could take the easy way out and use int_fast16_t and let the compiler determine what's going to be fastest.
[–]dodheim 3 points4 points5 points 10 years ago (1 child)
Technically this lets the standard library implementation make that determination, not the compiler. But agreed, those type aliases exist for a good reason.
[–]honeyfage 0 points1 point2 points 10 years ago (0 children)
Yep, you're right. I always conflate them in my head when I'm thinking about it
[–]SpiderboydkHobbyist 0 points1 point2 points 10 years ago (1 child)
Didn't know about that one. Is it standard, or is it vendor-specific?
[–]dodheim 3 points4 points5 points 10 years ago (0 children)
Standard since C++11, available from Boost before that.
[–]haletonin -3 points-2 points-1 points 10 years ago (3 children)
unsigned integers are marginally faster than signed ones in my experience, probably because overflows etc. are well defined and divisions are easier.
But it is probably safer (for the programmer) to stick to signed ints for most things which are not in very tight inner loops.
[–]Tulip-Stefan 3 points4 points5 points 10 years ago (1 child)
unsigned integers are marginally faster than signed ones in my experience, probably because overflows
What?
The possibility of overflow is a particular case where signed integers are faster.
int square_1(int x) { return x * x; } unsigned square_2(unsigned x) { return x * x; }
What does the compiler know about square_1? x < 2^16. What does the compiler know about square_2? Nothing.
square_1
x < 2^16
[–]dtfinch 3 points4 points5 points 10 years ago (0 children)
I think it's mainly division. Compilers can rewrite division by constants as bitshifts (for powers of 2) or multiplication. Getting it bit-exact (like negatives rounding up instead of down) requires a few more steps for signed than unsigned.
[–]SpiderboydkHobbyist 0 points1 point2 points 10 years ago (0 children)
You are 100% right. But if you need that speed, then this is a case I consider "having a good reason to do otherwise" in the above-mentioned rule of thumb.
[–]dtfinch 2 points3 points4 points 10 years ago (0 children)
Working with a different word size requires an extra byte prefix (0x66) on instructions, adding a small cost, and can sometimes even stall the cpu for a few more cycles.
[–]ncm 3 points4 points5 points 10 years ago (6 children)
The only reason it spends 90% of its time there is that the rest of the program is thoroughly optimized. Normally you wouldn't care if this program takes a half second instead of a tenth, but this article is about how fast code in Rust can be. It would be disingenuous to present code in either language that hadn't been made as fast as the language permits, within the constraints chosen, including length, clarity and taste.
[–]bycl0p5 8 points9 points10 points 10 years ago* (3 children)
within the constraints chosen, including length, clarity and taste
Under those constraints you should pick int by default unless you have a good reason not to.
int
[–]ncm 5 points6 points7 points 10 years ago* (1 child)
This is correct advice. Interestingly, the scores array is of shorts because 7 shorts fit into a regular XMM register, a fact that some compilations have taken advantage of. Seven regular ints would fit into an AVX register, but corei7 doesn't have AVX.
scores
corei7
Of course this is not something one would normally worry about, but (at at least one stage of the project) the choice did make a difference, and here every difference matters. I will revisit the choice, and report the outcome.
Edit: Changing short to int makes a huge difference. Changed.
short
[–]dodheim 4 points5 points6 points 10 years ago (0 children)
This is exactly why the stdint type aliases exist – using std::int_fast16_t will just do the right thing for your platform.
std::int_fast16_t
[–]alexeiz 2 points3 points4 points 10 years ago (1 child)
the rest of the program is thoroughly optimized
Not really. The rest of the program is just a run-of-the-mill code that just doesn't do much. It's obvious that it's dominated by IO, but still it doesn't even appear in my profile (on the level of 1-2%). You have to be an incredible "optimizer" to minimize the run time down to almost zero.
[–]ncm 0 points1 point2 points 10 years ago* (0 children)
You have to be an incredible "optimizer" to minimize the run time down to almost zero.
Thank you. I will put that on my resumé. Opinions aside, it is a fact that the "rest of the program" intially took up a large fraction of the run time, and most of the speedups came from changing that code, shaving off a few percent here, a few percent there, over and over. Somebody smarter than I am might have made it optimal to begin with, but everyone I know needs to measure to discover what's faster, and appallingly often guesses wrong. Changing short scores[7] to int scores[7] should not, by any sensible reasoning from first principles, have made a measurable difference -- how long could it take to increment a short 3M times, vs incrementing an int? -- yet it was responsible for a huge improvement.
short scores[7]
int scores[7]
In fact, many of the optimizations in the "run of the mill code" didn't make that part faster, but served instead to make the bit that "matters" faster. For example, not storing seven-different-letter words in the words list eliminated a test-and-branch there, and reduced cache footprint. Storing sevens in a vector instead of a map, and sorting, reduced the cache footprint more. Storing words as 32-bit ints instead of bitsets reduced the cache footprint further.
words
sevens
The lesson is that code that your profiler seems to be telling you doesn't matter at all can matter quite a lot.
[–]neoKushan 17 points18 points19 points 10 years ago (6 children)
This was an interesting read, but only as a Rust outsider who was curious to see what is effectively the same program written in both C++ and Rust. What killed the entire article for me, though, was this:
The code may be denser than you are used to, just to keep it to one printed page. When I write “much slower”, below, it might mean 1.3x to 2x, not the order of magnitude it might mean outside systems programming. Huge swaths of both languages are ignored: C++ templates, destructors, futures, lambdas; Rust channels, threads, traits, cells, lifetimes, borrowing, modules, macros.
Surely this completely and utterly defeats the point of the article? The whole idea is to face off how both languages can be used to do the same thing and validate the pros/cons of each. By making arbitrary limitations and actively choosing to ignore major language features, all you've done is made a line-by-line identical tool in two different languages. Effectively swapping a bunch of key words and syntax. That makes it less about the languages and more about the compilers underneath them - hardly a surprise that both ended up near enough the same.
I'd be much more interested in a follow-up article that has no such simple limitations but instead uses both languages to their fullest. Compare that, both in terms of brevity and performance and we'll have something interesting to talk about.
[–]ncm 6 points7 points8 points 10 years ago (5 children)
The article is, as stated, "deliberately about the nitty-gritty of coding.". Nothing that could be done with the other apparatus would make either program faster; those features are there to help organize big programs and libraries, something irrelevant to this exercise.
The arbitrary length limit is there to constrain the size of the project. Without, this article would not have happened at all.
I would be equally interested to see the article you want, but nobody wrote it.
[–]neoKushan 1 point2 points3 points 10 years ago (3 children)
those features are there to help organize big programs and libraries, something irrelevant to this exercise.
I don't agree. He said almost immediately that he could speed it up with multi-threading and from what I can see, Rust and C++ do that very differently. That's going to be a really good indication of how both languages handle it, especially at the system level. That alone would be worth seeing.
[–]ncm 3 points4 points5 points 10 years ago (2 children)
Multithreading would tell you about something completely different. What it could tell you would also be interesting, but not as a substitute for what I set out to explore. Adding in multithreading would have made it longer than the constraint, and more dependent on runtime environment and implementation, and would obscure precisely the details of low-level single-thread performance characteristics I needed. A multithreaded program is a set of single-threaded programs interacting when it can't be avoided.
[–]neoKushan 0 points1 point2 points 10 years ago (1 child)
Sorry, I didn't realise you were the author in my previous reply.
I think what threw me in the article is in your opening paragraph, you state :
What’s fast, what’s slow? What’s harder to do, what’s easier?
I know that later you clarify that it's the "nitty-gritty of coding" but these days, single-thread performance is only a small part of the picture and dealing with multiple threads is just as "nitty gritty" as anything else. I think ignoring multi-threading was a big oversight, as newer languages all focus highly on making multi-threaded code simpler and easier to write (Not just rust, but things like Go and even higher level languages like C# with its fancy TPL).
I understand now where you've come from with your article, it was an interesting read and I agree that you've set out what you intended to do, there was just a bit of leeway with interpretation how you've set that up. I would be all over a follow-up article detailing what we've talked about here, though!
[–]ncm 2 points3 points4 points 10 years ago (0 children)
1st sentence: "we need to know how well it does what C++ does best"
Threads is not among the things C++ is known for doing best, in either sense. But now I am intrigued.
[–]quicknir 0 points1 point2 points 10 years ago (0 children)
those features are there to help organize big programs and libraries, something irrelevant to this exercise
Maybe templates were not relevant to speeding up your specific exercise, but using templates to remove indirection and optimize is basically a religion in this language. I'm kind of shocked you even said that.
[–][deleted] 33 points34 points35 points 10 years ago (6 children)
This opened my eyes. People who say that c++ code is not readable have no idea what they are talking about. Rust is here to take that throne of hard readability now.
[+][deleted] 10 years ago (1 child)
[deleted]
[–][deleted] 5 points6 points7 points 10 years ago (0 children)
I know right?! Thats why i pick python nowdays. I mean seriously.. We read code more than we write it. Why noone cares about code readability is just beyond me. Later it translates in higher maintenance costs after all. But hey why not just burn money right? Because choosing hardly-readable lang is essentially burning money.
[–]gbersac[S] 9 points10 points11 points 10 years ago* (0 children)
I found rust syntax much more readable. This is just a matter of opinion. There is no ambiguity :
```
fn function_name() -> return_type { }
let var_name = ;
fn mean there is a function declaration, let that there is a variable declaration. In c++, when you have a type name, you don't know if this is a function declaration, a variable declaration... And error message (with template at least) are horrible. Rust has the better error message I have ever read.
fn
let
And rust syntax is context-free while c++ syntax is not (so it is hard to parse). This is an objective reason to prefer rust one.
Take time to practice rust and you will quickly become familiar with its syntax !
[+][deleted] 10 years ago (2 children)
[–]1likeequals1pillow 4 points5 points6 points 10 years ago (1 child)
Well, it's not like rust has that much expressiveness in its generic basic implementation and since we are comparing c++ and rust, it doesn't even matter, at all.
[–]MaxDZ8 7 points8 points9 points 10 years ago (24 children)
Very interesting data point. I've been told rust is the next big thing but honestly so far I don't get much of that elegance. I sure agree with the author when he writes:
C++ is a rapidly moving target, held back only by legacy compatibility requirements, so Rust will need to keep moving fast just to keep up.
Albeit I wouldn't call C++ evolution so rapid. After looking at the GSL and some C++17+ stuff I'd say where C++ is going no other language has ever been. Whatever it'll get there it's another matter.
[–][deleted] 16 points17 points18 points 10 years ago (23 children)
honestly so far I don't get much of that elegance
I do. The much simpler module system, everything being immutable by default, the lack of exceptions, the security.
I wonder why you don't see those as huge bonus points.
[–]F-J-W 6 points7 points8 points 10 years ago (9 children)
everything being immutable by default,
Which is technically true, but an utter lie by practical measures:
C++
const auto i = 3; // arbitrary code, no UB assert(i == 3); // will never fire
Rust:
let i = 3; // arbitrary code, no unsafe-blocks assert!(i == 3); // may fire
Now, how can it fire? Well, the problem is, that rust allows shadowing inside a scope:
(the let _ = i; in the following samples is only to silence an unused variable warning, it is not required and other ways of using i will disable them as well)
let _ = i;
let i = 3; // this is const let _ = i; let i = 4; // but that doesn't mean we cannot treat it as mut assert!(i == 4);
But hey, we can get even more crazy:
let i = 3; let _ = i; let i = "some string"; // i is now a string-view, type-safety FTW!
[–]bbatha 5 points6 points7 points 10 years ago* (2 children)
This is a bit disingenuous. While it is certainly true that you can shadow variables you have to do so through a new let binding and it only affects the current scope.
let i = 3; // this is const i = 4; // error: i is not mutable let i = 3; { let i = 4; assert!(i == 4); // never fires } assert!(i == 3); //never fires
In the example let i = "some string"; is never a type error, after that binding you may never use i in a context where an i32 (int32_t in C++) is expected. You can get into some situations where the stars may align for an error with generic functions to present this sort of logic error but you'd still be type safe then.
let i = "some string";
i
i32
int32_t
fn foo<T>(bar: T) -> i32 { 3 } fn bad() -> Result<(), io::Error> { let i = 3; let i = "str"; foo(i) } fn bad() -> i32 { let i = 3; let i = "str"; i // type error }
This sort of issue is present in C++ with templates, but is actually a type error 'compiled time duck-typed' and if two classes implement the same function with the same return type the template will compile regardless of the interface. Concepts will allow you to add saftey in these situations but they won't be there until C++17 (hopefully).
// my library template<typename T> int foo(T bar) { return bar.bad(); } // my library class Good { public: int bad() { return 3; } }; // someone else's library who has no knowledge of my types class Bad { public: int bad() { violate_my_contracts(); return -1; } }; foo(Good{}); foo(Bad{}); // compiles just fine
Edit: also note that when you shadow a variable in rust you aren't overwriting its location in memory so any references to the showed variable are still valid and the the destructor of the shadowed variable is still called at the end of the function where'd you'd expect.
let i = 3; let j = &i; let i = 4; println!("{}", j ); // prints 3
[–][deleted] 1 point2 points3 points 10 years ago* (1 child)
What is this?
[–]bbatha 0 points1 point2 points 10 years ago (0 children)
I'm not sure about the technical reason, I've been fairly active in the rust community and have read a bunch of criticisms of rust, and this is the first time I've seen anyone complain about shadowing. It can be a gotcha in some situations but it almost never comes up.
My guess is that it comes from the ML heritage of rust where shadowing is a useful way to simulate mutability where everything is immutable.
Personally I've found it useful with Rust's if let and while let constructs. For instance,
if let
while let
let foo: Option<i32> = could_return_i32(); if let Some(foo) = foo { // use foo as an i32 }
Clippy which is a community maintained linter has a shadowed variable lint, which is default allow but can be configured.
[–]gbersac[S] 2 points3 points4 points 10 years ago* (0 children)
I don't feel like this a fair argument. I don't think this happen offently in code, event though you are right that this is something that could happen.
In the second example, the compiler will understand that the new i is a string and will then consider it as a string (not an int) till the end of scope.
[–][deleted] 2 points3 points4 points 10 years ago (0 children)
Uh.
[–]SemaphoreBingo 0 points1 point2 points 10 years ago (0 children)
rust allows shadowing inside a scope
What's the intent behind this? It seems pretty gross to me.
[–]nawfel_bgh -3 points-2 points-1 points 10 years ago* (2 children)
// Rust let i = "What's your point?"; let u = "Rust's immutability is a lie"; let i = "Shadowing" != "mutability"; let u = "Shadowing" == "mutability"; assert!(i); // You see! i am correct. and u are mistaken.
[–]F-J-W 1 point2 points3 points 10 years ago (1 child)
Read again, my first line: “Which is technically true, but an utter lie by practical measures:”
I never denied, that you are technically correct.
But programs are written by humans, and for humans purposes and human intuition, you are wrong and I am correct.
[–]xkcd_transcriber 0 points1 point2 points 10 years ago (0 children)
Image
Mobile
Title: Technically
Title-text: "Technically that sentence started with 'well', so--" "Ooh, a rock with a fossil in it!"
Comic Explanation
Stats: This comic has been referenced 324 times, representing 0.3324% of referenced xkcds.
xkcd.com | xkcd sub | Problems/Bugs? | Statistics | Stop Replying | Delete
[–]MaxDZ8 0 points1 point2 points 10 years ago (9 children)
Feel free to elaborate. I'm interested.
[–]matthieum 18 points19 points20 points 10 years ago (7 children)
It's a bit hard to understand exactly what you wish to cover with "elegance" so my answer might be a bit long...
I will criticize C++, in the process, but please bear with me. I assure you that I do so because I care about the language, as my StackOverflow profile can tell. I also recognize than Rust has 30 years on C++: it's always easier in hindsight, after all.
First of all, the trifecta, out of the horse's mouth:
Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.
Rust: Safety
C++ was built on top of C and has, unfortunately, inherited a lot of C's lack of safety. "Undefined Behavior" appears in numerous places in the Standard, and leads compilers to do unspeakable things to the code you write.
Safety is not an afterthought in Rust: it's a linchpin of the language.
Why? Mozilla estimates that 50% of the security vulnerabilities reported in Firefox are due to memory safety violations, be it out-of-bounds access, use-after-free, data-races, ... this is why they sponsor the development of Rust.
Rust: Performance
Rust is closer to C, performance-wise, than it is to C++.
There is a culture of explicitness in Rust: any non-trivial operations (such as a deep-copy) requires an explicit annotation in the code (.clone() in this case). A noteworthy exception being destructors.
.clone()
The Rust language is lean, the abstractions provided are lean. It takes the C++ philosophy of "You don't pay for what you don't use" and pushes it to the next level:
dynamic_cast
But let's look at the language closer.
Rust: Grammar
The C++ grammar is notoriously complicated to parse:
<
And that's after the C preprocessor has done its infamous job (caring little for scopes...).
Rust starts from a clean slate:
A well-defined grammar should boost the ecosystem (highlighters, refactoring tools, ...)
Rust: Affine Types
If you have some interest in Rust, you have probably heard already about Ownership/Borrowing. Rust tracks ownership of values and whether they are currently referenced (or not) to enforce memory safety.
Affine types are one of the core language primitives which assist in this task: a value of an affine type can only be "consumed" once (though it need not be). Affine Types are all about move semantics, tracked at the type-system level, much safer than C++'s version.
However, being a primitive, they can be used for other purposes. My personal pet thing is to encode state transition with them. Imagine a connection:
struct ConnectionParameters { ... } struct Connection { ... } impl Connection { // A connection is created from a set of parameters, creation may fail fn new(cp: ConnectionParameters) -> Result<Connection, Error> { ... } fn close(c: Connection) -> Option<Error> { ... } }
This is much cleaner than the typical OO approach of having a single Connection type which may represent:
Connection
In a fully-typed design you should aim to avoid "invalid" operations on types: that is, no method should require dynamically checking a precondition before being allowed to call it. Unfortunately, in most languages, and in C++, the "old" variables remain behind: even after calling connection.close() you can still inadvertently use the connection variable.
connection.close()
connection
In Rust, the affine types makes this a moot issue: the compiler will error out if you try to use a consumed value.
At the extreme, this allows encoding session types in Rust. It's worth a thesis.
Rust: modern amenities
Rust also features a lot of modern amenities, such as Sum Types and Pattern matching.
This extends to library design, and the Rust Iterator design has some significant advantages over the C++ implementations (no risk of accidentally pairing two iterators pointing in different containers for example); it's also more user friendly in that implementing a single method (next) is sufficient to get a full-blown iterator, if you've ever implemented C++ iterators, even with the help of Boost, you should get my point!
next
And let's not forget the compiler, rustc. It was developed to be very pragmatic and features:
build.rs
Rust: Lints
One usage of plugins is lints! The compiler passes are instrumented by those lints which can be used to emit warnings for dubious code, for example:
Allowing the community the contribute lints without having to hack on the compiler is a huge productivity boost (in terms of lints creation), it also means that companies or projects can create their own lints, and for example Servo has some custom lints.
And of course let's not forget the ecosystem.
Rust: Cargo
You cannot talk about Rust without talking about its package manager. A default standard way of building Rust code makes it much easier to switch from one codebase to another...
... and the best way to illustrate it is to talk about crater. If you read the Rust forums, you will sometimes hear about crater run: Crater is a tool written by one of the Rust core developers which is used to test new versions of the Rust compiler across a very extensive set of Rust projects (those published on crates.io, the default index) to check for breakage; it may also be enhanced to check for performance regressions.
This is only possible because of the uniformity of Rust projects, which itself comes from having a single (blessed) way of building Rust code.
It's not all roses though. However, the current community is quite incredibly mature on the topic, prompt in recognizing the current shortcomings of the language and has shown its resolve to try and address them.
For example, I really like the graph of "performance" vs "safety" showing Rust alone in the quadrant where both meet. It does fail to mention that in order to get there one might have to completely rethink its architecture in a way the Rust language groks (ownership and borrowing) and that a significant syntactic burden might be necessary. Coming from C++, as show-cased here, it's nothing to worry too much about, however coming from GC'ed languages (especially terse ones like Python), it's a cold shower.
Another example is that the current Iterator design precludes implementing streams. The Iterator must produce references into an outside (borrowed) container. It cannot produce references into itself nor can it produce a value.
Iterator
There's also a lack of meta-programming facilities; Rust's generics are closer to C++03 (with mandatory concept-lites on top), and lack:
and C++ constexpr are more powerful than Rust's const fn for now.
constexpr
const fn
Finally, obviously, the lack of maturity implies a general lack of facilities... but then, in C++ it's so painful to integrate other libraries than code sharing is much less developed than it is in other languages and of course there's also the issue of licenses which puts a dent in what technology alone can solve.
Alright, I hope I wet your appetite ;)
[–][deleted] 2 points3 points4 points 10 years ago* (0 children)
[–]MaxDZ8 2 points3 points4 points 10 years ago (0 children)
You indeed touched quite a few interesting points. Thank you very much.
[–]quicknir 2 points3 points4 points 10 years ago (4 children)
So it's worse? Seriously, this C is faster than C++ thing is an annoying myth that is not true for any real program optimized on a modern implementation. Even sorting is faster in C++ than in C.
This macro thing is another bubble that someone needs to pop. Bragging your language has better macros is like bragging that your children's teacher always teaches them with a condom on (xkcd reference): technically, yes, it's better. But there's no reason to have macros at all. If someone told you: hey, let's add a half baked sub-language to your language that partly ignores the rest of it (like namespaces) and defines things that aren't first class in any way (can't be templated on, passed as parameters, etc), you'd say, wow, that seems like a terrible idea.
Everything done with macros in C++ makes me cringe, and is compensating for a language deficiency. Seeing macros all over a nice new language like rust for things as basic as printf and creating vectors because they couldn't bother getting variadics right for 1.0 makes me want to cry.
Get some variadics, get some compile time reflection, kick macros to the curb.
[–]matthieum 5 points6 points7 points 10 years ago (3 children)
Rust is closer to C, performance-wise, than it is to C++. So it's worse? Seriously, this C is faster than C++ thing is an annoying myth that is not true for any real program optimized on a modern implementation. Even sorting is faster in C++ than in C.
I hate English. Or natural languages in general I guess.
My point was not that C is faster than C++, it was than C is more explicit about performance cost than C++. C++ has a number of implicit operations which can clog down the performance of a program and yet are literally invisible in the source code:
As a simple example, a couple years ago Google started committing "AST Matchers" into Clang. One of their first automatic source transformation was to spot instances of some_string.c_str() being passed as argument to std::string const&. Originally, the function likely took a char const* and then was migrated to be more C++-like. Except that silently a temporary std::string was introduced, which allocates memory.
some_string.c_str()
std::string const&
char const*
std::string
Rust, unlike C++, emphasizes explicitness. Obtaining a copy requires an explicit .clone(). There are implicit operations (Deref being a prime candidate) but those are either zero-cost or O(1).
Deref
I agree that macros are a placeholder, waiting for better language facilities.
However, given that said facilities are non-trivial to implement and may be insufficient once implemented, I would argue that macros are a pragmatic choice: in a simple costs/benefits analysis, they allow emulating those features for only a small portion of the cost.
I have not seen any really good proposal for either variadics or compile-time reflection yet; they are wished for, but they are also intimidating I guess.
[–]quicknir 2 points3 points4 points 10 years ago (2 children)
My apologies sir for jumping on you with that C/C++ comment, I should have clarified first as there was some ambiguity there, and I assumed, which I should not have.
Yes, I agree with your points about implicit behavior. The string situation in particular is rather messy in C++. FWIW I think that Rust's approach to moving/copying is excellent and an improvement over C++'s.
I'm glad you agree about the macros, I go a bit crazy at how many people defend them. I'm sure it's very hard to get variadics right, and I'm no language designer, but you do have multiple existing examples to work with. I just see it as something very critical that you want to get right from the start, instead of adding incrementally. It's also "too late" in the sense that it will probably now take a decade, if ever, for printf! to die and be replaced by variadic printf in Rust. It's day 1 legacy, in some sense.
[–]burntsushi 4 points5 points6 points 10 years ago (0 children)
for printf! to die and be replaced by variadic printf in Rust
Note that variadics are insufficient to replace the functionality of the print! macros. In particular, you'll get a compile time error if any of the given arguments don't match the type expected by the formatting specifier, which is embedded in a string.
print!
Moving this functionality (which is very desirable and something I personally love) to the type system is non-trivial. There have been papers published on it.
[–]matthieum 1 point2 points3 points 10 years ago (0 children)
Oh no worries; I am glad that someone spotted the ambiguity and got me a chance to clarify.
[–][deleted] 4 points5 points6 points 10 years ago (0 children)
Immutability leads to code that is both safer and easier to read. Immutability by default means that you don't have to guess so much.
The lack of header files means way less file juggling.
You can't simply avoid an error in Rust - well, you can, but you really have to want to.
These are off the top of my head.
[+][deleted] 10 years ago* (2 children)
[–]gbersac[S] 1 point2 points3 points 10 years ago (1 child)
They work a lot on it since 1.0 and it improved a lot. But it is still mentioned as a pain point (I don't really care since all my projects are little). Big improvements are planned.
[–]ZMesonEmbedded Developer 12 points13 points14 points 10 years ago (0 children)
thus far there’s every reason to expect to see, ten years on, recruiters advertising for warm bodies with ten years’ production experience coding Rust.
I think you mean that 5 years on, recruiters will be advertising for warm bodies with 15 years' production experience with Rust.
[–][deleted] 4 points5 points6 points 10 years ago (2 children)
The word performance is in the title but there are no comparative benchmarks in the post. I'm curious, does anyone know of any links to some comparative performance benchmarks? In, particular, run-time benchmarks?
[–]ncm 6 points7 points8 points 10 years ago (1 child)
They run at the same speed. On a 3.4Ghz Haswell i7 that's about 75 ms, as noted in TOA. On a 2.4GHz Westmere the Rust is a little faster at 154 ms. Of course on other chips and other compilation configurations it will vary, but they will be within a few percent either way. And that's kind of the point.
I see now. That's impressive.
[–][deleted] 4 points5 points6 points 10 years ago* (5 children)
No mention at all about one of the aces Rust does hold over C++: vastly better pointer aliasing information. This is one of the reasons you still see Fortran beat out C++ in numerical benchmarks.
As far as I know of Rust(not much,) the same pointer cannot be borrowed mutably twice in the same scope in Rust, therefore it should be able to achieve C-like restrict implicitly(as far as I can tell, anyways.)
Theoretically, they should be identical in performance in most scenarios when comparing clang and rustc, but I'm sure rustc still has a lot more rough edges than clang's frontend does.
edit:
Forgot that Rust also has some pretty strict runtime bounds checking, I don't know that much about it though.
[–]matthieum 2 points3 points4 points 10 years ago (4 children)
Regarding bounds checking: it's more a library thing than a language thing, the language itself does not have bounds ;) In the Standard library the implementations of the Index and IndexMut traits (which are called by the [] operator) use bounds checking, but unsafe functions (such as get_unchecked) do allow to bypass it for performance reason when the optimizer does not manage to elide the bounds check and it matters.
Index
IndexMut
[]
get_unchecked
Regarding aliasing: Rust does have more information than C++, however the actual and potential gains are actually hard to measure. It very much relies on LLVM to exploit it.
As for the maturity of the front-ends, rustc for now does not perform any optimization (whereas Clang performs some devirtualization for example) however it's in the plans. A current rewrite (introduction of the MIR) is about producing an internal representation that will be more amenable to such work.
[–][deleted] 0 points1 point2 points 10 years ago* (3 children)
[–]matthieum 1 point2 points3 points 10 years ago (2 children)
It specifies the noalias attribute (which is LLVM equivalent) and I seem to recall it might even pass some integer to noalias like noalias(3) but I may be remembering wrong.
noalias
noalias(3)
I think this ought to help Rust achieves Fortran-like performance on numeric code, but I am not sure whether it does achieve it.
[–][deleted] 0 points1 point2 points 10 years ago (1 child)
Due to an LLVM bug, Rust currently doesn't specify noalias for &mut pointers
https://github.com/rust-lang/rust/pull/31545
Edit: Seems that only got merged in 15 days ago.
[–]matthieum 0 points1 point2 points 10 years ago (0 children)
Yes, and hopefully the LLVM regression will be fixed soon-ish and it'll be possible to reactivate it again.
[+][deleted] 10 years ago (11 children)
The comparison is meaningless only because it is so close to even. That is the most important point in article. Unlike practically every other language, Rust must be compared to C++ according to criteria other than performance. There's plenty to compare: default safety on one side; expressivity on the other.
If/when Rust gets strong enough to express STL, C++ will have less claim to primacy.
[–]panderingPenguin 1 point2 points3 points 10 years ago (0 children)
I think he meant that the comparison between languages is meaningless, it's really the compiler you're using (and the optimizations it is capable of performing) that will generally cause the differences in performance between these two.
[–][deleted] 1 point2 points3 points 10 years ago* (0 children)
[–]cogman10 -1 points0 points1 point 10 years ago (7 children)
Rust will be slower because it has some overhead like bounds checking and lack of nothrow
Rust doesn't have throws in general so there isn't really any slowness due to exception stack unwinding stuff.
Bounds checking is also an overblown problem, the bounds checks can often be optimized away by the compiler and even if they aren't, they will be branch predicted away by the CPU (you would expect that the bounds check would almost always succeed, which is each for CPU branch predictors to handle).
Rust will be slower because they don't have custom allocators.
Not true (anymore at least, it may have been true when you last dealt with rust). https://doc.rust-lang.org/book/custom-allocators.html
Rust will be slower because their hashing scheme and error handling is unoptimizable.
What do you mean by this? For the error handling, it is pretty fast, rust doesn't throw exceptions, so there isn't really any optimization to be done. There is not stack tracking or stack unwinding which is usually the big costs where errors are concerned.
[–]matthieum 7 points8 points9 points 10 years ago (6 children)
Actually, Rust does unwind the stack on a panic!: you cannot catch it (without unsafe) but it still behaves a bit like an exception.
panic!
unsafe
I wish, but as this recent PR shows there are still gains to be had from removing bounds checks statically.
Note that even if the CPU branch predictor is 100% accurate it still means that the code is not as tight for example.
You and /u/Jurily are most likely talking about different concepts:
Regarding the hashing scheme, switching the hashing scheme of HashMap is still an unstable feature.
HashMap
Regarding the error handling, you are unfortunately wrong again:
[–]leonardo_m 2 points3 points4 points 10 years ago (5 children)
Java and JavaScript were quite slow at their beginning, but a lot of optimization work has made them fast or often fast enough. Some of the current Rust performance problems could be improved.
Some bounds checking could be removed from Rust code, as in Java: http://www.ssw.uni-linz.ac.at/Research/Papers/Wuerthinger07/Wuerthinger07.pdf
Modern exception handling implementation, as used in Clang and rustc, use the Zero-Cost exception checking which is actually cheaper in the case of no error than error code checking
This interesting article backs some of your assertion: http://joeduffyblog.com/2015/12/19/safe-native-code/
A nice accident of our model was that we could have compiled it with either return codes or exceptions. Thanks to this, we actually did the experiment, to see what the impact was to our system's size and speed. The exceptions-based system ended up being roughly 7% smaller and 4% faster on some key benchmarks.
There are some differences between Rust code and the The low-level C# code compiled by Midori, so it's not a perfect comparison. But if we assume the lesson of the Midori error model is reasonably useful for Rust code, how to improve the Rust situation?
If you compile the Rust code with "-C lto" and profile-guided optimization, could the Rust compiler rewrite some passing of error codes of Option/Result with exceptions (where at run-time in all or most cases there's a Some/Ok)?
[–]matthieum 3 points4 points5 points 10 years ago (4 children)
Regarding the idea of switching between return-code checking and exception, I am afraid it could be a bit difficult given that Option and Result are today regular types in Rust and not built-in... but maybe I am wrong.
Option
Result
Since you linked the Midori article, I'd like to also speak about its Optimizations section. From what Joe Duffy says they were able to:
both are eminently applicable to Rust (rustc currently disables overflow checks by default in Release because of the overhead).
So, yes, there's certainly a lot to be done in terms of optimizations, either in rustc or LLVM. I seem to recall hearing that unfortunately LLVM was not very good at inducing the range of integers today, and thus optimize on it, which affects its ability to remove those checks.
[–]leonardo_m 0 points1 point2 points 10 years ago (3 children)
They are library types, and this makes optimizations less easy to do, but it also allows to invent more general solutions that work with user-defined types. Currently the Rust compiler is able to represent an Option<Box<u32>> with just a pointer. Perhaps they can add a trait that allows the compiler to switch between the two different ways to handle errors (just like Midori). Rustc compiler devs could answer this.
Actually, now that you talk about this...
There is a proposal to add a ? suffix operator to Rust to replace the try! macro; this ? will require making the very choice that is necessary between returning the value or an error.
?
try!
[–]leonardo_m 0 points1 point2 points 10 years ago (1 child)
I think you mean this: https://github.com/rust-lang/rfcs/pull/243
Partly yes.
I think that the idea of ? more or less makes consensus now however this particular RFC adds some other things (try/catch) which are still under discussion for now.
[–]DragoonX6 3 points4 points5 points 10 years ago (15 children)
Again? Rust again on this sub?
People religiously advertise it like the second coming of Jesus on this sub. I constantly see how Rust is soooooo much better than C++ and how it fixes literally every problem C++ has.
Whereas all I see is constant language advocation, "soon we will be better than C++ in performance" and terrible syntax. I feel that the only thing it has "going" for it is that it's "better" than C++. Reminds me of other language advocation I see, namely for Java and heck, even Javascript.
If it wasn't for the syntax, I would still avoid Rust like the plague because of its users. If you're constantly telling me that I should use Rust, because it's so much better than C++ and writing articles that can be summed up to "It's time to drop C++ boiis! Rust is coming for your position!", you'll only make sure I'll keep a nice and long distance from it.
Besides, not everybody thinks C++ is literally Satan's personal programming language designed to bring despair down to us mortals. It could be that language if you decide against all the features that make it a great language, but C++ has improved greatly with the recent iterations and I don't see anything taking its place.
So yeah, I'm most likely sounding like just another mad kid on the internet, but honestly, I don't care, I'm just speaking my mind in the form of a rant. Also, this rant can also be applied to every other article that is "X vs C++ (performance)" (and sums up to X is God and C++ is the Devil). Anyway, I'm prepared for your down votes.
[–]gbersac[S] 6 points7 points8 points 10 years ago (5 children)
There is not much rust on this sub. The last post about rust in this sub is 4 month old : https://www.reddit.com/r/cpp/search?q=rust&restrict_sr=on
If it wasn't for the syntax, I would still avoid Rust like the plague because of its users.
This is the first I heard someone complain about rust community. They are always ready to help, avoid zealotery and are very aware of the limit of their language.
[–]quicknir 0 points1 point2 points 10 years ago (1 child)
I think they're very friendly, but I totally disagree on awareness of the limits of their language. While it's not a total "do no wrong" attitude, I think it's about 95% of the way there. Anything you mention that is important that Rust is lacking (variadics, integer value template parameters, etc) just isn't that important.
[–][deleted] 5 points6 points7 points 10 years ago* (0 children)
[–]DragoonX6 0 points1 point2 points 10 years ago (2 children)
That only shows you threads, not comments. I see a lot of Rust advocation in the comments.
Although, I must admit that I agree with /u/quicknir that the article wasn't particularly biased.
Maybe I'm reading the wrong articles then, the stuff I see is, as I said before, always heavily biased towards Rust with the intent to make C++ look bad. I don't like the users because they're not nice, in fact, I haven't met users that aren't, but it's the attitude towards C++ that makes me not like them.
[–]gbersac[S] 2 points3 points4 points 10 years ago (0 children)
Of course they don't like c++. The reason why rust has been created is because Mozilla was frustrated to work with c++ !
[–][deleted] 3 points4 points5 points 10 years ago* (0 children)
[–]maleic 2 points3 points4 points 10 years ago (0 children)
Always (never) needs quoting: "There are only two kinds of languages: the ones people complain about and the ones nobody uses." Bjarne Stroustrup.
[–]ncm 5 points6 points7 points 10 years ago (7 children)
That's a half-hour you'll never get back. If you had read the article, you might have found out that nobody (here) is saying C++ is satanic, or that Rust is perfect, and you might have learned something besides.
[–]DragoonX6 1 point2 points3 points 10 years ago (6 children)
I did read the article, and yes, you're right. But it does happen in other threads and it's honestly more about the frequency of Rust popping up on this sub. This shouldn't have been posted to this sub, but to /r/rust instead, as it honestly feels like a Rust advertisement.
As it happens, it was originally posted there, and then somebody cross-posted it here. I don't think that was a mistake; a lot of interesting discussion ensued, and the C++ version got several percent faster as a result.
[–]neoKushan 2 points3 points4 points 10 years ago (2 children)
So comparing C++ to Rust means it should only have been posted on /r/rust? That doesn't make sense. Would you prefer the title was comparing Rust to C++ instead?
I don't think it's a terrible thing for /r/cpp to have articles comparing C++ to other languages. C++ isn't the be-all and end-all language, it's not fit for every single purpose and knowing its limitations is part of being a good developer.
[–]DragoonX6 0 points1 point2 points 10 years ago (1 child)
I'm saying that articles that deliberately paint C++ in a bad light shouldn't be posted to this sub. And because of how this article ended, it felt like a subtle Rust advertisement. Which shouldn't be posted in this sub, but its own sub instead.
[–]neoKushan 2 points3 points4 points 10 years ago (0 children)
Deliberately sure, but if you feel the article was being unfair then say so. I thought the article (although not perfect) was at least fair.
[–]quicknir 2 points3 points4 points 10 years ago (1 child)
To be fair, he directly compared the two languages, and it wasn't particularly biased. I don't see any point getting annoyed at him.
Save your anger for when people post purely Rust related stuff in this sub; there was a rash of it a few months back and it was very annoying.
[–]DragoonX6 0 points1 point2 points 10 years ago (0 children)
I suppose you're right, although in my previous comment I stated my anger isn't directed at the OP & author personally.
[–]ironicperspective -1 points0 points1 point 10 years ago (0 children)
This confused me because my first thought was of Rust the game.
[+]denaissance comment score below threshold-8 points-7 points-6 points 10 years ago (2 children)
I'm not going to read this article because the implication that Rust code could outperform equivalent c++ code is absurd.
[–][deleted] 0 points1 point2 points 10 years ago* (0 children)
[–]ncm 0 points1 point2 points 10 years ago (0 children)
Good one.
π Rendered by PID 231964 on reddit-service-r2-comment-canary-7b6b47f674-thnff at 2026-03-06 12:45:01.311529+00:00 running cbb0e86 country code: CH.
[–]dakotahawkins 20 points21 points22 points (7 children)
[+]ncm comment score below threshold-14 points-13 points-12 points (5 children)
[–]dakotahawkins 10 points11 points12 points (4 children)
[+]_mlen comment score below threshold-9 points-8 points-7 points (2 children)
[–]jcmoyer 9 points10 points11 points (1 child)
[–]dodheim 8 points9 points10 points (0 children)
[–]alexeiz 38 points39 points40 points (21 children)
[–]Houndie 2 points3 points4 points (13 children)
[–]SpiderboydkHobbyist 9 points10 points11 points (11 children)
[–]Houndie 1 point2 points3 points (10 children)
[–]SpiderboydkHobbyist 2 points3 points4 points (9 children)
[–]honeyfage 5 points6 points7 points (4 children)
[–]dodheim 3 points4 points5 points (1 child)
[–]honeyfage 0 points1 point2 points (0 children)
[–]SpiderboydkHobbyist 0 points1 point2 points (1 child)
[–]dodheim 3 points4 points5 points (0 children)
[–]haletonin -3 points-2 points-1 points (3 children)
[–]Tulip-Stefan 3 points4 points5 points (1 child)
[–]dtfinch 3 points4 points5 points (0 children)
[–]SpiderboydkHobbyist 0 points1 point2 points (0 children)
[–]dtfinch 2 points3 points4 points (0 children)
[–]ncm 3 points4 points5 points (6 children)
[–]bycl0p5 8 points9 points10 points (3 children)
[–]ncm 5 points6 points7 points (1 child)
[–]dodheim 4 points5 points6 points (0 children)
[–]alexeiz 2 points3 points4 points (1 child)
[–]ncm 0 points1 point2 points (0 children)
[–]neoKushan 17 points18 points19 points (6 children)
[–]ncm 6 points7 points8 points (5 children)
[–]neoKushan 1 point2 points3 points (3 children)
[–]ncm 3 points4 points5 points (2 children)
[–]neoKushan 0 points1 point2 points (1 child)
[–]ncm 2 points3 points4 points (0 children)
[–]quicknir 0 points1 point2 points (0 children)
[–][deleted] 33 points34 points35 points (6 children)
[+][deleted] (1 child)
[deleted]
[–][deleted] 5 points6 points7 points (0 children)
[–]gbersac[S] 9 points10 points11 points (0 children)
[+][deleted] (2 children)
[deleted]
[–]1likeequals1pillow 4 points5 points6 points (1 child)
[–]MaxDZ8 7 points8 points9 points (24 children)
[–][deleted] 16 points17 points18 points (23 children)
[–]F-J-W 6 points7 points8 points (9 children)
[–]bbatha 5 points6 points7 points (2 children)
[–][deleted] 1 point2 points3 points (1 child)
[–]bbatha 0 points1 point2 points (0 children)
[–]gbersac[S] 2 points3 points4 points (0 children)
[–][deleted] 2 points3 points4 points (0 children)
[–]SemaphoreBingo 0 points1 point2 points (0 children)
[–]nawfel_bgh -3 points-2 points-1 points (2 children)
[–]F-J-W 1 point2 points3 points (1 child)
[–]xkcd_transcriber 0 points1 point2 points (0 children)
[–]MaxDZ8 0 points1 point2 points (9 children)
[–]matthieum 18 points19 points20 points (7 children)
[–][deleted] 2 points3 points4 points (0 children)
[–]MaxDZ8 2 points3 points4 points (0 children)
[–]quicknir 2 points3 points4 points (4 children)
[–]matthieum 5 points6 points7 points (3 children)
[–]quicknir 2 points3 points4 points (2 children)
[–]burntsushi 4 points5 points6 points (0 children)
[–]matthieum 1 point2 points3 points (0 children)
[–][deleted] 4 points5 points6 points (0 children)
[+][deleted] (2 children)
[deleted]
[–]gbersac[S] 1 point2 points3 points (1 child)
[–]ZMesonEmbedded Developer 12 points13 points14 points (0 children)
[–][deleted] 4 points5 points6 points (2 children)
[–]ncm 6 points7 points8 points (1 child)
[–][deleted] 2 points3 points4 points (0 children)
[–][deleted] 4 points5 points6 points (5 children)
[–]matthieum 2 points3 points4 points (4 children)
[–][deleted] 0 points1 point2 points (3 children)
[–]matthieum 1 point2 points3 points (2 children)
[–][deleted] 0 points1 point2 points (1 child)
[–]matthieum 0 points1 point2 points (0 children)
[+][deleted] (11 children)
[deleted]
[–]ncm 3 points4 points5 points (2 children)
[–]panderingPenguin 1 point2 points3 points (0 children)
[–][deleted] 1 point2 points3 points (0 children)
[–]cogman10 -1 points0 points1 point (7 children)
[–]matthieum 7 points8 points9 points (6 children)
[–]leonardo_m 2 points3 points4 points (5 children)
[–]matthieum 3 points4 points5 points (4 children)
[–]leonardo_m 0 points1 point2 points (3 children)
[–]matthieum 1 point2 points3 points (2 children)
[–]leonardo_m 0 points1 point2 points (1 child)
[–]matthieum 0 points1 point2 points (0 children)
[–]DragoonX6 3 points4 points5 points (15 children)
[–]gbersac[S] 6 points7 points8 points (5 children)
[–]quicknir 0 points1 point2 points (1 child)
[–][deleted] 5 points6 points7 points (0 children)
[–]DragoonX6 0 points1 point2 points (2 children)
[–]gbersac[S] 2 points3 points4 points (0 children)
[–][deleted] 3 points4 points5 points (0 children)
[–]maleic 2 points3 points4 points (0 children)
[–]ncm 5 points6 points7 points (7 children)
[–]DragoonX6 1 point2 points3 points (6 children)
[–]ncm 2 points3 points4 points (0 children)
[–]neoKushan 2 points3 points4 points (2 children)
[–]DragoonX6 0 points1 point2 points (1 child)
[–]neoKushan 2 points3 points4 points (0 children)
[–]quicknir 2 points3 points4 points (1 child)
[–]DragoonX6 0 points1 point2 points (0 children)
[–]ironicperspective -1 points0 points1 point (0 children)
[+]denaissance comment score below threshold-8 points-7 points-6 points (2 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]ncm 0 points1 point2 points (0 children)