For one, not all software is like Chromium. If you look at something like OpenSSH, the vast majority of their security holes have nothing to do with memory safety and are just logic bugs (often caused by features that somebody added that aren't core to the basic SSH experience, e.g. code that interfaces with X11 or something) or protocol weaknesses. (http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=openssh)
The other effect is that in practice, memory-safe languages can come with security baggage of their own. If you look at the zillions of security holes in something like Rails or Wordpress or Django, a fair portion of them relate to an attacker's ability to invoke sophisticated-but-unintended behaviors that are more likely to be hiding in these managed languages (and their support libraries) than in something like C++. E.g. CVE-2013-0156 or CVE-2013-0277 or apparently any current Python or Ruby program that, even today, calls yaml.load on untrusted input. That kind of "security hole from unwanted latent functionality" is less likely to exist in C++. (I realize this is a contrarian view not shared by the vast majority of PL/security experts, but the ones I hang out with seem to interpret "memory-safe language" to mean "expert-written Haskell or, if you want to slum it, Rust" and are not thinking "random Ruby/Python/JavaScript/enterprise Java".)
Not to mention the countless high-profile security holes that have nothing to do with memory safety, e.g. Shellshock, "goto fail", Lucky Thirteen, BEAST, CRIME, POODLE, FREAK, Logjam, etc. Or bugs that are very relevant to Chromium but aren't really Chromium's fault and probably don't appear on their own list, e.g. Meltdown/Spectre etc.
I mean, supposing that Chromium was rewritten in a safer language, have we any reason to believe that these memory issues would be replaced by a similar number of non-memory-safety issues?
>That kind of "security hole from unwanted latent functionality" is less likely to exist in C++.
Why? That seems like a non-sequitur to me. For me the real difference between the projects you list is that Chromium and OpenSSH are not, as you put it "random Ruby/Python/JavaScript/enterprise Java", they are heavily audited and have a lot of resources allocated into preventing the very issues you mention. Comparing OpenSSH to Wordpress and attributing their respective security track record mainly to the languages they use is fairly absurd IMO. If you're clueless enough not to properly sanitize untrusted input can I really believe that you'd manage to write safe C code?
This thread is oddly reminiscent of discussions around gun control. Because something is not 100% effective doesn't mean that it's not valuable. Although I guess we do need these unsafe languages in case we ever need to overthrow a tyrannical government... no wait, I'm getting confused.
I have heard people say "we rely on exploits to be able to exercise software freedom on locked-down platforms" and "if games were written in Rust, a lot of the glitches speedrunners use would go away and we'd lose our hobby" so... you're not always off.
Also you assert it is absurd to compare WordPress and openssh In the context of this argument why? They are both widely used software with very high stakes on their reliability and security. They show very well the contrast that even though one is memory managed and the other is not, that does not stop both having serious bugs. Actually it shows memory management is a don't care variable. On the other hand if chromium finds their project has much more memory management issues than other issues, then yes: memory management for the functionality of chromium seems to be a relevant facto upon which improvements can be done.
I meant that input sanitization is generally an easier problem to solve than writing safe C. If you can't do the former reliably, I'm certain you wouldn't be able to do the latter.
Input sanitization should generally be handled very close to the interface, once you went through this step you should expect to only deal with safe data. Memory safety covers the entire application, if some non-critical debugging function 10 level deep in the call stack messes up when it computes the size of a message buffer you can have a remote code execution vulnerability.
>Also you assert it is absurd to compare WordPress and openssh In the context of this argument why? They are both widely used software with very high stakes on their reliability and security.
Wordpress is a complicated ecosystem with multiple plugins that each can introduce security problems. Its attack surface is also incredibly large since it's generally public facing and anybody can interact with a lot of the features by default. That coupled with the fact that it's extremely popular make it a very good target for attackers. It's also a relatively fast moving target since the web changes fairly fast and new features have to be implemented regularly.
OpenSSH does mostly one thing and does it well. Its surface of attack is a lot smaller and it's vastly easier to audit and test. Its development is also overseen by the OpenBSD developers, who are famously uncompromising regarding safety. The feature set is very stable and changes very slowly.
>Actually it shows memory management is a don't care variable.
The absence of evidence is not the evidence of absence. How much development effort went into making sure that these issues didn't exist? How many thousands of hours auditing the code and making sure it did what people wanted? In a safer language this time could've been spent auditing other parts of the codebase or implementing other features.
I'd also like to point out that one of the biggest OpenSSH vulnerabilities ever found (in the Debian version of OpenSSH where the maintainer heavily reduced the generated key entropy by mistake) was indirectly caused by a memory issue since the reason for the patch was a false-positive returned by Valgrind regarding use of uninitialized memory.
There are tons of comments, including here on HN on a regular basis, which one could use to conclude that WordPress should be more secure than anything written in C no matter the quality. And yet, it isn't true in that instance. So it's fine to advocate rust or whatever, but let's not make that other, more dangerous conclusion.
In practice what I’ve found is that people prefer to deal in absolutes. A large reduction in a category of bugs isn’t enough, it needs to be eliminated altogether, they say. If it’s still possible in a contrived example, what’s the point of investing in switching?
A thing can happen zero times (never). An example might be 1+1 =0. You could get really unlucky with cosmic rays or something, but really adding two registers, both containing 1, that result isn't going to be zero unless there's some sort of hardware failure.
A thing can happen once. an example might be, you can delete a file once. There are ways to get unlucky, of corse, but once it's unlinked it's gone.
if it happens more than once, you really should probably think about an unbounded number of times. now days, that sorta means 2^64 times. There will be bugs when something overflows int64, but I hope you get the gist.
The parent comment is talking about invariants you can use in an algorithm.
I think you might be worried about python vs C or something along those lines. Really, it should all work with a pencil and paper. Which is obviously going to be slower than pushing around electrons. But if you can find those invariants, 0-1-many, you can make a better algorithm. If you're stuck with a pencil it'll still make that faster.
Risk calculations are far broader than infosec and don't deserve the dismissiveness you seem to be casting towards them. Risk calculations are the core of business. Almost every decision a business makes is a risk calculation; every action has an opportunity cost if it isn't intrinsically risky, and actions with certainty are very rare.
(For the avoidance of doubt, I believe that use of memory-unsafe languages should be avoided if reasonably possible, but there are still plenty of reasonable reasons to use C, C++ etc. instead.)
I feel this thread is getting out of hand. I initially replied to say why, in general, the kind of thinking that makes you unsatisfied with reductions but not eliminations of concerns, is common among programmers - because it's a sound heuristic. Reducing is good, but eliminating is better.
This is where I make the case for having knowledge of asm/C/C++. The JavaScript developers that started on JavaScript or, maybe, Ruby have no clue what the hell is going on in the language they are working in. I see this all the time. From senior and lead developers, too.
They have no mental model for how pointers work. Which means they have no concept of what an object is, on a memory-organization level. They aren't always aware of when mutation will occur, and they are entirely too reliant on operations that are inefficient. In Lisp, this would be the developer than uses CONS everywhere. There is no awareness of the allocations behind the mechanisms. There is premature optimization and then there is not driving into the damn pothole in the first place! One of these is F1 racing and the other is basic driving skills.
I believe this may also explain why so many developers are so bad at using git. Git is entirely based on pointers. There is an elegance and simplicity to git that is lost on many people.
In the old days people usually chose to become a programmer for a reason, today it is like any other job.
Just like you can be a very effective C or C++ developer without knowing anything about css (which in my opinion is far more difficult to understand than when and how stack vs heap allocations are made)
[1] handles could be unboxed to expose a pointer. But unboxing had machinery to detect and trap on bad handles
Edit: that static allocation was essentially a centralized store. Everything repeats itself.
https://floooh.github.io/2018/06/17/handles-vs-pointers.html
You've already been greyed out, but I agree 100% with what you said.
That's my main beef with this kind of bi-weekly discussion where people always put C and C++ in the same bag.
I rarely, if ever, make memory management errors in C. The model can't be more simple. If you don't explicitly allocate, you don't have to care about it (in the general case, it is on the stack and will disappear when exiting a function, anyway you also don't have to care about those implementation details, just do as if it disappeared with scope). If you allocate something (on the heap), then you free it later. Malloc(), free(), end of story. Okay, don't keep pointers to areas you will free or realloc, of course. Any function which returns pointers tomemory chunks are always to be treated as malloc'ed, those objects can never be on the stack and never have a fancy automatic management of any sort.
Now almost each piece of code I wrote in Objective-C contained memory errors/leaks. Be cause 1. I didn't grasp the more complicated, less deterministic (so to speak) models and the jargon well 2. you (well, I) never know right away which type of management was used for the object that some function returned, you have to check the doc and pray it is clearly written 2.b you have to keep that information in mind until the moment when you'd like to release the said object in order to release what should be released and not release what is/will be/has been automatically released now/later/sooner/whoknows.
And I don't even talk about the abomination that is Objective-C++, where I drowned in muddy waters.
Then you are back into the same synchronization issues as in every other language.
Yes, exactly.
https://kkimdev.github.io/posts/2019/04/22/Rust-Compile-Time...
Of course, you can bypass this by using 'unsafe' code, where you are explicitly telling the compiler that you have manually verified Rust's safety guarantees.
This is why a significant portion of Rust's community gets agitated with (possibly unnecessary) use of 'unsafe' in popular libraries.
Considering many of the software problems we face have some connection to thread-safety, Rust is the way to go.
Threading seemed a good idea that went wrong.
I'm trying to learn Android and some libraries give zero indication of what thread your callbacks will be called on.
Maybe there's a convention somewhere that says "On Android, you can't assume anything about callbacks, so always assume you're on some anonymous worker thread and lock everything or dispatch to a thread you own"
But I have not come across it yet.
You cannot assume anything about process or thread lifetimes on Android, as the simple act of rotating the screen will restart your application, and it can be killed at any moment and restarted later, due to memory pressure, or because the use has switched applications.
So whatever was the state of your application can be completely different when the callback is supposed to be invoked later on.
As for having more time to write a fast program... that's funny. If you want a fast program on something JVM based you're pretty much going to be spending the majority of your time writing things in a way where the GC plays as little role as possible.
Sorry, this is not hand wavy nonsense. And what you are providing is called annectodal evidence.
Also, your universe seems to consist only of the JVM as a memory-safe alternative to C++. Yes, there are a lot of bloated, badly performing programs implemented in Java. However this isn't a given. Yes, some design decisions for Java introduce the risk of bloat, but you can avoid them at much less effort (and risk) than memory corruptions and new features like the value classes are reducing the bloat quite a bit. But still, the JVM is extremely high performance, so for speed in surprisingly many cases, it often beats C++. Virtual method calls are just better optimizable at run time, the Java JIT creates excellent code and Java has some of the best garbage collectors, so at really dynamic memory loads, it beats any manual management by a wide marging.
And of course, there is a whole world beyond Java as alternatives. Rust has been explicitly designed to excel at tasks C++ traditionally shines for, while giving your full safety.
Rust is certainly interesting and it's on my radar. I wonder though, when it comes to having it in use in anger if its guarantees turn out to be over sold, just like the JVM's safety claims were. Time will tell.
Edit: I tend to focus on comapring against the JVM because pretty much any framework you use on The Cloud is JVM based. I'm of the opinion that there are cost savings to be had if these were ported to more appropriate languages, hence the Cassandra vs Scylla comparison. The money saved was 'noticeable'.
[1] https://github.com/LMAX-Exchange/disruptor [2] http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot...
Java 15 just accepted the JEP for native memory management, yet another stepping stone for having value types support.
If the cadence continues, Java will eventually have all the features that it should have had in 1996, had Sun properly taken into consideration languages like Modula-3 and Eiffel.
Which you can get today in a language like Swift, C#, Nim or D, productivity of GC, type safe, while having the language features to do C++ like resource management.
You'll never actually see anyone tout the benefits of using GC for this, though, because the performance characteristics of persistent data structures are so horrendous compared to mutable ones, no one actually uses them in C++.
Stack is also very hot in cache. Memory that GC is handing allocations from is not.
I recently ported some of Cassandra code from Java to stack-only Rust and I got ~25x performance improvement, most from avoiding heap allocations and GC.
Of course, repeatedly allocating and freeing is poor for performance. Cache/pre-allocate when you can. This goes for managed languages too.
Both Java and C# may be somewhat slower, but the maintainability and freedom from memory management issues more than makes up for this.
Any engineer worth his salt will take this into account.
If you unlucky and given GC is not well optimized for given workload, then memory usage overhead can be huge. Just a few days ago had such problem with Go (HeapIdle grew until OOM).
Go GC is less mature than Java GC, but Java GC is not free too - sometimes you need to spend a lot of time time to tune GC settings or to optimize code to avoid GC problems.
In my case case it would be faster to use malloc/free than to spend time fighting with GC (looking for a workarounds).
On average GC saves development time and allows to avoid memory management bugs, but in some cases overhead is big and developers have to spend more time, not less.
https://www.ptc.com/en/products/developer-tools/perc
https://www.aicas.com/cms/en/JamaicaVM
One of the Java vendors acquired by PTC, Aonix, used to sell real time Java implementations for military deployments, including weapons controls and targeting systems.
You don't want a couple of ms pause when playing with such "toys".
And ?
C (even Fortran) had threads and was used to create high performance program with high degree of parallelism before any "concurrent" modern language was even born.
You can not agree if you want. But fact are there, 98% of programs running on the biggest "parallel" machines nowadays (supercomputers) are C, C++ or Fortran.
You don't need to be "designed" concurrent to be efficient at it. The same way you do not need to be designed "Cloud-native (bullshit)" to run on a virtual machine.
We have to give programmers, with different backgrounds & training, tools to write high performant code in their everyday job. Many of the tools we use today are not designed for that. We are stuck in a mental model 50 years old that is no longer true.
Here are some interesting stackoverflow answers. You are of course free to dismiss these answers as anecdotal.
If you mean the `synchronized` keyword, then correct, they don't have that. Most languages do not have that concept. C++ does have mutexes and has had them since C++11 (nearly a decade). C++ also has as part of the language spec the concept of threads, again, there since C++11.
If you meant co-routines, then C++ just added them with C++20.
Or do you mean something different like green threads (ala go)?
C++ certainly has concurrency constructs and has been expanding them since C++11.
The entire design of C++ is to enable efficient libraries for these kinds of things to be built. And it does, and has, and will continue to for decades more.
So much that in the 90's there were vendors that could make a business out of selling specialized memory allocators.
https://inf.ethz.ch/personal/wirth/ProjectOberon/Sources/Ker...
The malloc() exposed in a ISO C standard library implementation isn't necessarily related to whatever means the OS does memory allocation.
What C and C++ have going for them is 40 years of investment in optimizing compilers, to detriment of other languages, and abuse of UB in optimizers.
Now with shared compiler backends becoming a mainstream thing across most OSes, that advantage is getting thinner.
Does that actually exist? (I mean in the general case, not just a few carefully chosen benchmarks).
Some things, namely array indexing and RefCell borrowing, have unavoidable extra runtime overhead in the default safe usage, but it's unlikely that it is significant (often array bounds checks are either essential and thus needed in C++ as well or optimized out by LLVM in Rust, and RefCell usage is generally rare), and you can use unsafe unchecked operations as well.
I find this very hard to believe.
I don't claim that Java is always faster than C++, that would be silly and there are plenty of Java programs in the wild which proves that it isn't. But there are quite some tasks at which Java is indeed faster.
However, in my experience, using virtual dispatch is a relatively rare ocurrence in C++ (compared to the vast majority of method calls). On the other hand, on Java, most of _everything else_ is indirect and has overhead: All objects are allocated on the heap, primitives (int) are often objects (Integer) where they ought not to be, all objects have 16 bytes of overhead, etc.
But the JVM will convert those heap allocations to stack allocations! And it will realize those Integers are used as int and remove the overhead! And it will realize you're not using the information on the header of every object!
Perhaps in synthetic benchmarks, but in real programs, where there are an almost infinite amount of code paths, dumb 'data transfer' objects are common and things need to be modularized, the JVM is forced to assume the worst case can happen (even if you as an human can prove that it won't happen) and inhibit those optimizations. And now you have indirect accesses everywhere, memory overhead (=cache trashing) everywhere, the runtime can't vectorize that tight due to Integers, etc.
In fact, I can't think of any domain where there is heavy competition and where high performance is a determining factor where Java has won to C/C++. In browsers, it certainly has not.
The number of instructions is much less important than what they are doing. In the case of virtual dispatch, it's doing a memory lookup. If that memory is in cache it could be relatively inexpensive but not guaranteed. However, if you have to hit main memory then things are much slower.
> But the JVM will convert those heap allocations to stack allocations!
I was surprised recently to learn this is not the case. (at least, not with hotspot) The JVM will try and "scalarize" things (pull the fields out of the object which may push them onto the stack) but it won't actually allocate a full object on the stack (OpenJ9 will, but I don't see people using that very often).
It is also somewhat bad at doing the Integer to int conversion. That is mainly because the Integer::valueOf method will break things (EA has a hard time realizing this is a non-escaped value). Simple code like
Integer a = 1;
a++;
can screw up the current analysis and end up in heap allocations.There is current work to try and make these things better, but it hasn't landed yet (AFAIK).
> In fact, I can't think of any domain where there is heavy competition and where high performance is a determining factor where Java has won to C/C++. In browsers, it certainly has not.
I think the realm where the JVM can potentially beat C++ is, funnily, work that requires a lot of memory. The thing that the JVM memory model has going for it is that heap allocations are relatively cheap compared to heap allocations in C++. If you have a ton of tiny short lived object allocations then the JVM will do a great job at managing them for you.
Except C++ also has many other options besides malloc() each of these individual objects.
Of course you normally try to avoid writing C++ like that.
For example, if you use `std::vector` or `std::unique_ptr` you would be deallocating memory when those go out of scope. The JVM might actually never do that, e.g., if the program terminates before sufficient memory pressure arises.
Writing Rust code that leaks a `std::vector` is trivial, but doing the same in C++ actually requires some skill.
auto* v = new std::vector<char>(); vector<int> foo {...};
and heap allocate a vector on the heap with `new` (without using a smart pointer), and then avoiding your linters warnings about this (e.g. clang-tidy).If this is common in your place of work, I truly pity you.
Also, trading a call to `free` for a second call to malloc, and a second pointer indirection to the vector elements, isn't a very effective way of improving performance to beat the JVM. The whole idea behind leaking memory is doing less operations, not more :D
In Rust, leaking "the right way" is trivial (mem::forget is safe), but in C++, leaking the stack allocated vector probably requires putting it behind an union or aligned_storage or similar.
Maybe type systems could, in some cases, prevent this type of problem. You could construct a system where performing a password authentication returns an object of type Authorized, and performing operations requires passing in objects of type Authorized. I don't know how often this is useful in practice though.
So we would have to make sure that type safety is ultimately backed by a hardware based encryption module, but that's just not where we are today.
You have a type for UnauthenticatedUser and one for AuthenicatedUser. You have an authenticate() method to convert from one to the other.
I'll wait.
The type system is a tool to reduce the space of invalid inputs.
Here's an example: create a type, such as "RoleAuthorization". Make it a required parameter for access-controlled methods, either through the constructor of the parent object or explicitly in the method.
In order to create a RoleAuthorization, you must first pass the user's Role object in an authorization method.
If a method requires authorization, the compiler will complain if you don't first check the current user's role through the authorization method.
> I'll wait.
If you're sincerely interested in discussing something, antagonizing someone this way (and implying that you are infallible) really doesn't help.
If you just wanted to make what you believe is a statement of fact, just make it.
If you don't want to discuss the issue at all and would instead prefer to be unchallenged on this topic, why write about it in a comments section?
authenticate() : role1 | role2
I'm not sure this is a sensible way to compare - Heartbleed is a memory safety bug and is a bigger bug than the rest of the ones you've listed combined. The various terrifying nameless iOS exploits that have had actual real-world, documented usage are not clever crypto protocol bugs with catchy names, either.
This is why we have to abandon C, not only is the language unsafe, it has also driven hardware manufacturing to a bad state with a reinforcing feedback loop, the more C we write the more hardware manufacturers want to make you believe that we still are working on PDP-11.
But here is a great article
I'm not sure which point you are trying to make, but yes, 100%-70% = 30%, i.e., there are many security vulnerabilities in Firefox, Chrome and Windows that are not attributed to memory safety by these projects, and preventing memory unsafety wouldn't remove all of them (at most "only" 70% of them).
Nobody is claiming here that fixing memory unsafety in software fixes hardware bugs, nor anything about OpenSSH or other projects, and your anecdotes do not show how many security vulnerabilities are caused in those projects due to memory unsafety.
It's a case of "When in Rome, do as the Romans do".
Real dynamic linking on that (dlopen-style) can be a real nightmare, specially between compiler versions.
You can also add that Rust do not have a runtime, which is in many cases an advantage, but also tend to make libraries bigger.
Though the average one is probably harder, yes. I'd wager that it's mostly lack of care or motivation though, made a bit worse by common language features.
C++ has a stable ABI. Fragile, but stable. Or doing things like updating libQt on your Linux distribution without recompiling half of the world would be close to impossible :)
I'm not aware of any plan to not stabilize Rust's ABI, it just hasn't happened yet. It's completely fair to label that a deal-breaker for using Rust, but trying to draw a hypothetical box around it with the label "its ABI will be difficult to create and/or use" seems a bit unwarranted.
If you're using C or C++ "for performance" and not using a profiler in day-to-day development, you don't need to be using C or C++ and should be using a memory-safe language instead.
The adage is named after Niklaus Wirth, who discussed it in his 1995 article "A Plea for Lean Software".[1][2]"
We could do better languages. But it just takes insane resources to compete with the others ecosystem. IDE's, the Pandas, the format on save.
I've been a professional C++-developer in the past and one of the great things is the tooling and the just sheer knowledge of my past C++ colleagues. They know what happens in the kernel, they know the performance optimisation tricks, they know a lot, because the knowledge they gather just have a longer lifespan.
Ask a JS-developer and they will tell you all the web frameworks that came before React and their quirks...
C++ developers may be smart people because you have to be smart to do anything in C++ - imagine if the same brainpower that's being spent tracking memory usage and pointer/reference distinctions could be put into your actual application logic instead.
If performance is on your mind constantly, why wouldn't you choose the one with the least restrictions on what you can achieve?
It's not like those people would find joy in being locked into the JVM instruction-set.
However: I'd argue that accidentally quadratic algorithms are easier to hide in a concise language. Writing out a quadratic loop explicitly takes space, and that space alone might make people pay more attention than some subtle implicit language construct. Either way, the most common source of unintended quadratic (or higher) behavior is helper functions and library calls.
The other thing to keep in mind when it comes to algorithms is that cache behavior and therefore memory layout matters a lot for performance on modern hardware. Managed languages really stand in the way of optimizing memory layout, which can be a systematic performance disadvantage compared to C++.
I do hope we get some more innovation in the design space occupied by Rust, where you get fairly explicit control over memory layout, but still have statically checked memory safety guarantees.
I disagree. When every loop is full of cruft around setting up the iterators, it's easy to drift past what's actually happening. In a language where looping over a list takes a single syntactic token, it's a lot more obvious when you've nested several such loops.
> The other thing to keep in mind when it comes to algorithms is that cache behavior and therefore memory layout matters a lot for performance on modern hardware. Managed languages really stand in the way of optimizing memory layout, which can be a systematic performance disadvantage compared to C++.
C++ doesn't really make cache behaviour clear either though. I agree that we need better tooling for handling those aspects of high-performance code, but they actually need to come from somewhere lower-level than C++.
The real problems tend to come from where the quadratic behaviour doesn't come from nested loops, but from library calls. The canonical example of this is building up a string with successive string concatenation in C.
As for cache behaviour, C++ allows you to control memory layout, which is really what's required there, while most managed languages don't give you that control at all.
We live in a fallen world. In a large enterprise codebase there will almost certainly be parts that aren't indented correctly. And even if everything is indented perfectly, the sheer amount of stuff in a C++ codebase makes everything far, far less obvious.
Correct me if I'm wrong, but I doubt you've ever written a program in modern C++?
It's not easy to see what algorithms you're using in a C++ codebase, because most of the lines of code are taken up micromanaging details that are broadly irrelevant. Yes, C++ makes it easy to tell whether you're using 8 bytes or 16 in this one datastructure. But you drown in a sea of those details and lose track of whether you're creating 10 or 10,000 instances of it.
As for algorithms, I honestly don't know what you mean. They're all documented online with respective big-O running times[1]. If you're talking about making unintended copies of things, then yes, C++ does expect you to know what's going on... it's the whole point of the language. If that's too much for you then don't use it, but that doesn't make it a bad language (I'm not denying it has some hair-pulling moments) Use std::move() when appropriate.
[1] https://en.cppreference.com/w/cpp/algorithm/sort (see 'Complexity' section.
Then yes, I work on modern C++ codebases.
> As for algorithms, I honestly don't know what you mean. They're all documented online with respective big-O running times[1].
Nontrivial programming tends to involve implementing, or even inventing, some algorithms yourself.
Yet fairly modest requirements like 4000 concurrenct connections or a 16gb cache can be hard to achieve.
We almost exclusivly write for the JVM. I see systems with 20 - 30 - 40 jvms with huge CPU and RAM demands, to meet modest perf requirements.
Sometimes I do wonder if we shouldn't write a little more carefully written C and a lot less memory safe Java.
I would lay money that the language isn't your real bottleneck. Switching languages might save you a factor of 2, using better algorithms or datastructures can save you a factor of 1000 or more. How much profiling do you do?
That's in my mind wrong for at least two reasons:
- If you are making building blocks (libraries) for other languages, you have to use C or C++ (maybe Rust soon). They are currently the only languages that can be bridge to the rest of the world (Python, Ruby, JS) without loosing your mind.
The main reason is that they are without GC, meaning deterministic destruction of object, meaning easy to interface with language with GC.
- When you aim for high performance you don't profile day 1, that's a complete waste of time. You will never transform every of your function in a critical kernel. You profile when you reach a performance bottleneck and you need it.
Sure (though there are often other ways to achieve the thing you actually want to do). But in that case you're not doing it "for performance".
> When you aim for high performance you don't profile day 1, that's a complete waste of time. You will never transform every of your function in a critical kernel. You profile when you reach a performance bottleneck and you need it.
In that case your performance requirements are not extreme enough to justify using C/C++ for your whole application. Write it in a safer language, when you hit performance issues profile and optimise, and maybe drop into C/C++ for those few "critical kernels" in the unlikely event that it turns out you actually need to.
Again : no. That is in my experience both wrong and over-idealist.
For many applications, the overhead in term of dev time of writing bindings for every of your compute kernels + the pleasure of debugging the problems associated with them and heterogeneous build chains is generally several order of magnitude higher in term of man-hour-cost than just doing your program entirely in C/C++/rust.
There is an other aspects generally ignored:
- Theory say that often 80% of the compute time is consumed in 20% of the code. That's often wrong, and many HPC simulators do not have any kernel taking more than 3-7% of total run time. Consequently, everything might one day need to be optimised.
- Many performance critical algorithm are state of art and evolve. Meaning your innocent little function in "memory managed" language might become tomorrow a new bottleneck. And you do not want to have to rewrite that all the time.
A lot of new devs are wrongly scared of manual memory management where it became a non-problem with RAII in C++[1-2]x or the borrow checker in Rust.
And generally the ones that are scared are the one that do not use it.
The mental overhead with memory in C++ does not come so much with the lifetime of object, it comes mainly with HOW to use efficiently YOUR memory: aligned object in memory, cache effect, indirection, cost of polymorphism, allocation, etc, etc.
All these aspects, you do not think about them in memory managed language because you can not: You have no control over it.
And that's also why they bite you in the face in term of performance, generally much more than the 2x you quoted before. Just a remember, a cache miss and it's ~200 cycle you loose
> - Many performance critical algorithm are state of art and evolve. Meaning your innocent little function in "memory managed" language might become tomorrow a new bottleneck. And you do not want to have to rewrite that all the time.
You can't have it both ways. If it's really common for everything to become a performance bottleneck, it's worth profiling from the start so that you avoid having major pitfalls anywhere. If it's rare and exceptional, FFI for those cases is fine.
> The mental overhead with memory in C++ does not come so much with the lifetime of object, it comes mainly with HOW to use efficiently YOUR memory: aligned object in memory, cache effect, indirection, cost of polymorphism, allocation, etc, etc.
> All these aspects, you do not think about them in memory managed language because you can not: You have no control over it.
> And that's also why they bite you in the face in term of performance, generally much more than the 2x you quoted before. Just a remember, a cache miss and it's ~200 cycle you loose
On the contrary. Plenty of people do those kind of things in, say, Java. They require knowing about compiler internals and using unsupported hints, or even bypassing parts of the compiler. But so does controlling these things in in C++.
You can have it both way, code evolve. It is pretty common in performance critical code that a minor, almost never call function in one scenario, become a performance critical bottleneck in an other. If you already played with large scaled simulation software, this happened almost every week depending of your inputs on what you are interested to simulate.
> On the contrary. Plenty of people do those kind of things in, say, Java. They require knowing about compiler internals and using unsupported hints, or even bypassing parts of the compiler
No it's not again. I have been developing in C++ for 15 years, including in the HPC world and I (close to) never had to touch a compiler internal. The language gives you what you need for performances, you do not need to play with that.
At the opposite JIT compilers like V8 or Java are monster of complexity very sensible to side effect [1] and controlling things like "Does this data fit in my L2 cache ?" in them is close to impossible because even things like "Where are my data and what are there size ?" is an hard question.
Once again, there is theory and there is practice.
Theory is what you say. Practice is that 98% of performance critical software in HPC, game industry, physics and High Frequency Trading is in C++/C (maybe Rust soon). And this is why.
In which case you're in the "worth profiling from day 1" world. It's much easier to work on the performance of code when you're already working on it and have it in your head - particularly in a verbose language like C++ where it takes a relatively long time to comprehend existing code - so if there's a decent chance that the performance of this code is going to be important in the future, profiling as you write saves you time overall.
> No it's not again. I have been developing in C++ for 15 years, including in the HPC world and I (close to) never had to touch a compiler internal. The language gives you what you need for performances, you do not need to play with that.
I said be aware of, not touch. If you weren't doing things like memory alignment pragmas then I guess your performance requirements were never so stringent. Fact is that a Java program that's fitting its data into L2 or avoiding cache line aliasing will blow a C++ program that isn't out of the water.
> Theory is what you say. Practice is that 98% of performance critical software in HPC, game industry, physics and High Frequency Trading is in C++/C (maybe Rust soon). And this is why.
HPC/physics follow questionable development practices in a lot of areas, and the games industry follows questionable everything practices. HFT uses a lot of Java and even higher level languages. C++ survives because people are rewarded for being seen to put a lot of effort into performance, and are not rewarded for avoiding bugs.
That's pure bullshit.
C++ survives because, even in 2020, it does the job.
Most people criticizing C++ are still stuck in there mind with C++98 and its quirks.
C++ evolved and modern C++ is at least as productive as Java or C# when used correctly.
That's why he is still actively uses and continue to grow.
This message just translate at best, your feelings (as a Java/scala developers ?). And you allow yourself to insult both the HPC Industry and the game industry without even providing metrics based on your "feelings".
You're claiming this in a thread about how a flagship project from one of the biggest names in the industry found that 70% of their security bugs were things that wouldn't have happened in Java or C#. Your statement may be true, but only for a kind of "correctly" that doesn't actually exist in practice.
> That's why he is still actively uses and continue to grow.
Where are you getting those stats?
> This message just translate at best, your feelings (as a Java/scala developers ?). And you allow yourself to insult both the HPC Industry and the game industry without even providing metrics based on your "feelings".
Would you defend either of those industries as a haven of good coding practices? Do you believe that they have fewer bugs, make better use of up-to-date tools, make more data-driven decisions, than other parts of the industry? I'm repeating a reputation rather than a specific metric, sure, but does anyone actually dispute that reputation?
Which is a project which is born in 2008, and still ship codes from the 90's. That also include one of the most optimized (meaning complex) piece of code world wide: V8.
You have nothing that come even close to the complexity, usability and popularity of Chrome in both Java and C# world. Ironically, even Microsoft uses a C++-core in its software, including MS Office and Edge. Maybe you should reflect on that.
> Where are you getting those stats?
https://tjpalmer.github.io/languish/#y=stars&names=java%2Cc%...
You're welcome.
>Would you defend either of those industries as a haven of good coding practices?
Every industry has domain driven standards in term of coding practice. They all have their reasons based on deadlines, usages, iteration cycle, developer backgrounds, safety.
Pretending that one culture is superior to the other is both pretentious and let appear a bad misunderstanding of the world we are in.
Now this is my last comment on this thread. I do not think you are open to any discussion.
Back in 2008 C++ advocates were saying the same thing: all those errors are only in old codebases, modern C++ doesn't have those problems. At what point should we stop believing it?
> You have nothing that come even close to the complexity, usability and popularity of Chrome in both Java and C# world.
Nonsense. There are dozens of more complex, more usable, and more popular systems written in Java and C#.
> Ironically, even Microsoft uses a C++-core in its software, including MS Office and Edge.
In the older projects that they're most conservative about, yes. Large companies change slowly. Doesn't mean what they're doing today is wise.
> Every industry has domain driven standards in term of coding practice. They all have their reasons based on deadlines, usages, iteration cycle, developer backgrounds, safety.
Which is to say that good development practices will be a lesser or greater priority level in different industries.
And game programming isn't normally "performance critical" so much as OS-less and embedded. Sometimes there's real-time constraints, but very few parts of a AAA game are the inner rendering loop that has real time requirements. Mort of it if boring stuff that could be (and often is!) written in python or more often Lua.
There is certainly a need for the industry (and hobbyists) to take stock of both what is necessary and what is just desired. I just don’t want to see the bar for using a language that doesn’t handle all aspects of memory usage and performance to be restricted to soft real-time applications and infrastructure projects. Digging into low level programming can be fun and rewarding.
My problem is essentially that even if I am not absolutely concerned with maximum performance across memory usage, binary size, CPU efficiency, bandwidth, etc. I still truly enjoy the options for control (or the illusion of it) that is provided by C. I enjoy memory layout design, allocator design, cache-efficiency considerations and the like; while at the same time, I don’t have a huge love of malloc and free or having to track down segfaults. I think ‘performance-by-default’ is a viable language design goal and want more of it in my tools.
I keep posting comments mentioning my pet language project, mostly in hopes that when I see it on my threads display I can continue to shame myself into getting it released on time, and this is one of those. These kinds of concerns motivated me to design a language for personal use that gives me what I want, but doesn’t require (but can) allow me to deal with other things. I enjoy using Coq, Haskell, Idris, Lisp, ATS, and Clean. But those language deprive me of things I really do enjoy. So I’m going for a low level language (in the Perlis sense) that has an experimental type theory. This will certainly allow for a memory safe subset and is the kind of thing I want to see more of from others.
Both of those methods still allow leaks, which is why Rust encourages RAII. [1] Are there other structured lifetimes we can get compilers to enforce for us, like they enforce certain invariants about flow control using control structures?
[1] https://users.rust-lang.org/t/memory-leaks-in-rust/18187/2
http://huonw.github.io/blog/2016/04/memory-leaks-are-memory-...
The distinction between unsafe and managed languages does not make any sense. You can for example have a safe C implementation. No need to move to another language.
Does the amount of unsafe implementations matter?
And when talking about C++, since 2011 there are smart pointers to help developers managing memory kind of automatically.
I believe most of the problems are due the people typing on those keyboards such bad code, especially when it comes to "smart" code.
In 2020 we have plenty of tools supporting the developer job (memory sanitizers, static analysis tools, linting, profilers, etc.): The big problem is failing to / ignoring to use such tools.
C is syntactically C++ being a subset of C++ and I've seen many programs which claim to be C++ but are actually programmed using C methodologies.
You are directly rewarded for "new" features (i.e. plagiarizing or rewriting old ones and selling them as new).
So I don't think the engineers are particularly talented, and the incentives are not the same as for SQLite.
The reason SQLite can test so thoroughly is that their requirements never change. I have tried to browse the web with SQLite but so far no luck.
Why do we see the version of Chrome reaching 100 at a sustained pace, instead of seeing something like Chrome 8.X or 9.Y?
The pressure is all about introducing new features (and thus, more bugs!) while fixing the existing bugs slowly.
And we should stop thinking that all the brilliant minds are working at FAANG, because it isn't true.
Interestingly enough Microsoft arrived at the same number. I think it really stresses how hard it is to reason about memory manually. I'm still surprised how much stuff is being written in non-safe languages even if people could get away with a managed language.
One thing that I just recently noticed when scrolling through some github repos is how much new software in the linux ecosystem is still written in C despite it being probably avoidable, like flatpak.