I think this quote summarizes hackers today: ``[T]ime spent sweating memory is time you're not writing your app. The hard part is developing the experience is that you need to know when you need to care."
Not having to worry about memory is now a luxury we can afford in the age of plenty. While writing an efficient spellchecker would have been a great feat in the 80s, there's no point of that when you can load the whole dictionary file in memory.
In other words, memory management is a #highclassproblem for many developers--you worry about efficiency once you have a lot of adoption. (Within reason; if your app is unbearably slow that isn't good.)
We also live in the age of plenty in regards to how many things we have running at the same time, or how many tabs there are open in a browser. Things can add up pretty quickly.
And why settle for "without optimizing it's slightly faster than things used to be when people had to optimize", when you can have "optimized, it's orders of magnitude faster than things used to be"?
> you worry about efficiency once you have a lot of adoption.
Sometimes things can be tricky to refactor if you didn't move carefully at every step. Initially not worrying about efficiency at all might mean having to rewrite from scratch once you do.
Of course, this can't be generalized. Sometimes it really, really doesn't matter, unless you care about craftsmanship as a value in and of itself -- taking 10-20% more time to write code that makes you happy and proud might very well make you more effective in the long run.
If you can sell it as faster than the alternatives, it makes sense to optimize.
But in the web-focused marketplace, adding 6 months to your launch calendar to get something fast to market may mean someone else's good-enough solution becomes the entrenched, de-facto standard that you are now fighting to prove you're superior enough to that it justifies people's time cost to move their data to your solution.
It's easy to point out the places where you could've done a bit more optimization after the fact. And, yes, sometimes those optimizations don't cost an arm and a leg, but it's very easy to fall into the premature optimization mode that kills the product and the company. Not everybody has tons of funding in the bank. For some startups shipping a day later can put you in the death spiral trajectory. Timing is a huge factor in a startup success.
It's easy to think why not just do it right in the first place. Aside from the timing you, as a startup, don't really know what "it" is or "it" is constantly moving. Committing to perfection at that stage can be a sure way to kill the company. Even if "it" is known and it's not changing you still need to keep in mind the Gall's Law.
That's the problem AirBnb has right now, according to a friend. Using Ruby has scaled AirBnb's business to great heights.
However, it has heaped to performance and code separation of concerns and structure debt. Of course, AirBnb can now afford to move things to Java/Scala and things that are better for multinational companies.
For AirBnb, it's really hard to fix a lot of these things since the product requires good uptime.
> How useful is it to know about C-style memory management when you're a front-end JavaScript Developer?
Try making your own particle engine and a simple game running at 60fps. I guess "avoiding GC" is only a very small subset of memory management, but if you're a noob like me, it surely will open your eyes about some things you would never have guessed could cause problems.
JavaScript is a brutally poor target for that application space though. It'd be better to target Unity and their web plugin; that's designed to be a game engine.
I just don’t understand why there aren’t more languages using automatic reference counting as featured in modern Objective-C and Swift. ARC has very low overhead and predictable performance profile (no GC waking up to choke your app for hundreds of ms). I think it makes almost no sense to consider going back to manual memory management because of performance, not even in games. Unless you are writing for some quite limited embedded hardware.
Because atomic reference counting has much worse throughput than tracing GC. This has been known for a long time.
It makes sense for Swift because of Swift's unique constraints—interoperability with an atomic-reference-counted world (Objective-C) and, less importantly, mobile UIs putting demands on latency. But most applications would be better off with a tracing GC.
> Because atomic reference counting has much worse throughput than tracing GC. This has been known for a long time.
I know this to be true for ubiquitously heap allocated systems, but I'm curious if this has been studied extensively for stack+heap reference counted systems, where only a relatively small number of items are heap allocated? Seems like reference counting would gain in this kind of environment, as the stack effectively gives you many of the advantages generational gcs enjoy from their nursery, and you don't have to worry about write boundaries, tracing and the like.
When we got the wonderful blend of plentiful ram, inexpensive CPU and storage along with enough variety in languages and separation of architecture concerns that not everything you built had to care about memory.
So many were wrong that by Moore's law we will have plentiful ram and inexpensive CPU to run bloated Java or C# programs. In 2016, desktop applications are still 99% C/C++ and fast mobile apps in C/C++/Object-C. Languages like Rust and leaner new languages with JIT that care more about resources like Go, Julia, Lua, Swift, PHP7, Hack or even newer iterations like C++14 are certainly good way to go.
1. I don't see any reason why Lua (presumably you mean LuaJIT?) and PHP 7 would be faster than Java or C#. The compilation techniques are the same, except that Java and C# have more mature JITs.
2. Java and C# JITs perform many more optimizations than Go 6g/8g do.
3. Objective-C message passing is a lot slower than method calls in Java and C#.
Not defending the grandparent's post, but LuaJIT is actually faster than Java or C#. Hell, it's faster than raw C in certain cases because it can optimize better due to being a simpler language. Mike Pall is a god amongst man.
I think it's due to LuaJIT being one of the very few tracing JITs that are still in development, since they are notoriously difficult to write and maintain.
Unfortunately the Benchmarks Game seems to have removed LuaJIT in favor of Lua 5.3, but https://stackoverflow.com/questions/2588071/is-luajit-really... mentions it as being an order of magnitude faster. I've seen it said multiple times that LuaJIT is the gold standard of JITing, but can't find very many concrete benchmarks anymore.
Anything saying 'order of magnitude' can't be taken seriously.
That would imply a horrid baseline in order to get it that fast.
Merely I don't believe a tracing JIT would have a great impact in a real world scenario.
PHP with its shared nothing design makes it very scalable, the 2016 implementations are very fast and consume little memory. LuaJIT see other post. Object-C (there is C underneath, read https://en.wikipedia.org/wiki/Object-C ). Check out Julia, it's great. You can write pretty low level code in modern languages like Go, Rust without loosing everything (don't come with Sing# and Java related misc things, yada yada). Also my point was that these languages consume less memory. I have yet to see a Java or dotNet application that doesn't consume too much memory for my feeling (not to mention the GC idioms). When I read that people put monolithic Java enterprise apps in Docker containers or wonder why the new Startmenu crashes that often, it reminds me why I care about computer resources. After all we are still stuck with 3.8GHz single core performance, same as 2004! (memory bandwith,etc got faster, I know) Give me a 10 GHz single core CPU and we can start talking.
You know, I’m developing Android apps. And you wanna know what eats the most RAM? The UI and the binary itself.
Most of the RAM of my application is used by the app and the goddamn UI and its fancy xxxhdpi and xxhdpi and xhpdi and hpdi and mdpi images, and because Android doesn’t support svg everywhere yet, we have to package tons of ressource twice and thrice and five times.
Run "Process Explorer", Options -> Color Selection -> check ".Net Processes" (Yellow).
In all consumer Windows up to Windows 7 no dotNet processes run (by default) at all. [Longhorn was meant to bring a dotNet shell (explorer, etc) but failed big time.] I have lots of common applications and all are coded in C/C++ with the exception of IDEs (VS/Eclipse/IDEA) which eat memory like no tomorrow. I couldn't care less about the brave Win8/10 world.
Most of the software that comes pre-installed on Windows machines (by the OEM) is .NET. That fact by itself means the amount of software written in .NET is far far far above 1%.
> In Windows world you are very wrong.
So you're saying after the shedloads of money, effort and development Microsoft has pumped into .NET it hasn't even amounted to 1% of applications running it? That's preposterous.
I almost wanted to include the exclaimer of "crap software preloaded by hardware vendors like HP, Lenovo, Dell, etc". Who (in the right mind) doesn't reinstall Windows and install only the "device drivers" but not the crap UI software that is of very little use anyways? The device driver is always in C and sometimes in C++. Nvidia/AMD/Intel drivers work fine without the crap "experience" or "control panel". And even if you install keep all the preloaded software, it's the first action to remove them from autostart, isn't it? Otherwise don't blame your hardware or Windows why your computer is that slow and takes ages to start...
DotNet and Java are very big in enterprise software and server software. Java is also big in open source thanks to Apache foundation (if you need a full text search engine, Lucene is still the only feature complete in town; and all the projects based around Lucene like Hadoop, Solr, etc) There was an C++ port of Lucene (CLucene) that was a lot faster and used little memory and compatible with Lucene file format. Though no native fork of Lucene keep up with the development speed of Apache Java Lucene.
> Who (in the right mind) doesn't reinstall Windows
All non-technical people, so 90% of users. I get where you're coming from (and you have valid points about speed/memory usage) but to say <1% of software on Windows is .NET is frankly wrong. Sure it's not 100% and might not even be 50%, but it's way way way above 1%.
Are there statistics on this? Most of the apps I often use that I can think of are written in C/C++, the ones in C/C++ being (at least mostly, some are partially made with other languages) Firefox, Chrome, Hexchat, Steam, KiTTY, Programmer's Notepad, Unity3D, Skype, GIMP, VLC, iTunes and surely a lot more. A few turned out to be C#/others (Visual Studio, SourceTree, Remote Desktop Connection Manager, ShareX). Maybe it's closer to 90%?
Well seeing as anything Dell (or any other large PC manufacturer) pumps out is in .NET and seeing as most users leave that crapware installed I would say they definitely do not account for 1% of programs. Most of the software you listed is pretty specialized, with the exception of a couple I don't think Joe Bloggs would have even heard of them, let alone use them.
This article has a hint of "C-style memory management is good for you"—implying that you might want to write in something like JS and then go back and write in C++ in order to "tighten the screws". I think that's wrong—by and large, it's been a very good thing that people have stopped caring about memory management in production. That's because correct C-style memory management never† scales up to large software projects in a way that doesn't result in crashes and, worse, vulnerabilities.
So, yes, we've traded performance for automatic memory management. But we've gained reliability, safety, and security. For the vast majority of server-side apps, that's a worthwhile trade.
† Unless you use the processes like NASA and the like use, which for most apps are infeasible from a cost point of view.
It scales, and you don't have to use NASA-like process. The article fairly clearly says that making this work is a learned skill. And it is.
If course it's possible to write stuff using manual memory management in a manner such that it's nearly impossible to do well. Doctor, doctor, it hurts when I do that...
I wrote a 'C' based thing around 2012 that:
- is about 50,000 lines; 50ish by 1000ish lines.
- uses no dynamic memory.
- does very "dynamic memory" things.
Each type/shape/class has its own statically allocated 'C' array of structs that is declared to worst-case, and has a "new". Behind the "new" is a lot of behavior, including pointing to other such things/objects.
A set of "delete" operations would be also relatively easy.
But because it's an array and it is in 'C', this is relatively little code. Yes, there is duplication involved but the object creation code is probably 20% of the total thing and each object creation is darn near provably correct. It's built on RAII principles ( perhaps foolishly, since there's no deletion ). Dependencies between objects are completely explicit in the code. Object creation must be in the correct order to manage dependencies
The magic happens because scripts* are used to create all things, once at startup. it also helps because by its very nature, the system is fully serialized - after a creation script, the state of the tables of objects can be written to a text file.
*scripts are, in this case nothing but sequences of verbs which send text files full of XML to a service at a TCP port. It could have been JSON or attribute-value pairs, but instead it's XML.
I do take extra care to make a very comprehensive test suite or it.
but since the allocation scheme is 1) cheap and 2) fully exposed as state in the system, there's nothing actually opaque about it. I don't have to do "trust falls" with a garbage collector or even the heap. You can't have a malloc() or new() failure - it'll violate a constraint and quit/exit(). You then make the table larger.
From the frame of reference of the other objects, it's abstracted and opaque - after the initial setup. From the "scripting" interface, it's fully transparent.
I've seen this post many times under different names for the authors and projects. Later on, a CVE or bug report shows up with the project's name but a different author. Because the person who created the code just couldn't break their own code like someone else who wasn't mentally attached to it.
What you're likely a victim of is the Dunning-Kruger effect or Schneier's Law depending on which you prefer:
On the other hand, you could've in fact designed something as good as you claimed. In that case, I seriously suggest you publish your methods in full for peer review as they could save millions spent by the likes of NASA, seL4 project, and so on. Any method that might hit full correctness with just basic coding or whatever is a potential game-changer. Very exciting stuff.
Dunning-Kruger is a very distinct possibility :) Your handle includes the word "security"; I don't claim any powers that way beyond "don't allow memory overwrites" and "airgapping good."
I doubt anybody is remotely interested in this - the problem domains are pretty boring and highly constrained. These tend to be realtime embedded ( both soft and hard realtime ). It seems wildly inappropriate for gaming, graphics or Web stuff. Perhaps I'm wrong there; not really all that clued in.
There is much less to this than you might imagine. The problem is that a book is a team effort, and I have no idea who to bother with it. Not asking for a volunteer here.
For an idea of the basic philosophy, it might be worth looking at Bruce Powell Douglass.
"Dunning-Kruger is a very distinct possibility :) Your handle includes the word "security"; I don't claim any powers that way beyond "don't allow memory overwrites" and "airgapping good.""
There's just so many ways that language can get you I have a doubt even a handful of methods would cover them all. It takes Work to get anywhere. Best of luck to you on that.
"For an idea of the basic philosophy, it might be worth looking at Bruce Powell Douglass."
Appreciate the reference. Can't recall if he's in my collection or not. I'll look into his work. :)
Unless you use the processes like NASA and the like use, which for most apps are infeasible from a cost point of view.
LOL. I have to ask: do you "know" this from experience or someone else told you?
Thousands of applications were made not so long ago with manual memory management. Many of them are still the most used desktop apps in the world.
Methods to secure them are not rocket science, really. I would gladly concede it's cheaper to do CRUD apps with GC, but you're going hyperbolic with NASA ;-)
As opposed to the slow and laggy garbage we as an industry should be too embarrassed to ship today.
(of course, vulnerabilities and unreliability in those apps came from more places than just manual memory management, just as today's slowness comes from more places than the GC)
And pretty much all of them are unreliable garbage...
So you have experienced thousands of unreliable garbage apps. I feel really sorry for you. My experience has been the opposite: lots of wonderful and reliable programs. And when I found one that wasn't good, I stopped using it and replaced it with a better one.
I mean as a user. As a programmer I've seen all kind of programs, from "critical mission" to "quick hack" and from excelent to unreliable garbage. And you know what? It's not language features but ludicrous incompetence what makes an app unreliable garbage. There are programmers out there that can make unreliable garbage in any language.
The "secret" to make some app reliable is not reserved to NASA and the like. Follow two simple patterns:
1. For every user action, validate input, try executing the code, catch the occasional exception and show an error message.
2. For every resource, allocate, try{ using it } finally { release it }
Put each of these skeletons in a function or method, so the resource is a local variable (no colateral effects) and it's easy to debug.
That's all. Well, also: don't be too "creative" unless you know what you're doing (most times you don't).
I'm fifty-something, spent a lot of painful time mechanically applying that recipes to rotten codebases, and when I read the kind of criticisms you made I assume they come from people too young to have real world experience on what they're criticizing. The alternative is worse: to be the kind of people that created that rot and then blamed the language.
Just in case: I don't believe that manual memory languages are "better". I love Python and (most of the time) JavaScript.
> I read the kind of criticisms you made I assume they come from people too young to have real world experience on what they're criticizing.
Actually, every single year I've worked on security-critical software I've understood more and more how impossible it is to secure large C and C++ codebases worked on by large teams with just "careful coding". The picture changes a lot when you have people actively searching for vulnerabilities.
Please, reread carefully your initial comment and the one by prodigal_erik (the two comments I've answered to in this thread) and you will see that you've made a gross generalization, while now you're narrowing the field you meant to apply your words.
Do you believe that security software or browsers are better writen in other languages than C/C++? I won't dispute that, even if I'd have some questions about relative merits. Please think of some well known Perl and PHP applications and their security record.
Anyway the unqualified:
That's because correct C-style memory management never† scales up to large software projects in a way that doesn't result in crashes and, worse, vulnerabilities.
... and talking about manual memory management in general...
And pretty much all of them are unreliable garbage that we as an industry should have been too embarrassed to ship.
... are simply not true. Worse, they're insulting for everyone who has worked in this kind of application.
> Do you believe that security software or browsers are better writen in other languages than C/C++?
Yes. And lots and lots of apps (most public network facing things) are security sensitive.
> Please think of some well known Perl and PHP applications and their security record.
Nobody is saying that "memory safe == no vulnerabilities". I do think it's true that being memory unsafe results in many more vulnerabilities. I've measured this precise statistic in our bug tracker.
> That's because correct C-style memory management never† scales up to large software projects in a way that doesn't result in crashes and, worse, vulnerabilities.
It's true. And I work in native code myself!
It's only an insult if you presuppose that introducing vulnerabilities reflects badly on the programmer who introduced them. I don't believe that; most major vulnerabilities I've seen were introduced by very good programmers. It's a problem with the tools they're working with.
I've seen Linux panic. I've seen Emacs and GCC dump core. I've seen Chrome "aw snap" at least monthly. My phone has rebooted for no reason more than once. Damn near every Windows user knows what a BSOD is.
Where did you find reliable software, and what does it do? After twenty years, it all seems to be the shit it always has been.
High assurance niche taught us both the methods you mentioned (eg NASA) and why this comment is correct. For this one, it's because C and C++ codebases often ignore the root cause of so many vulnerabilities in their very design. Too much IT fixes symptoms instead of root cause. That highly assured systems needed immunity to many issues meant the root causes must be prevented. That eliminates use of C and C++ where possible.
Such thinking is why MULTICS used PL/0, why GEMSOS used Pascal, why Wirth used Modula and Oberon for safer OS's, and why Altran-Praxis used Ada/SPARK for a CA. Results were better than C (or predecessor) every time. Deal with root cause, symptoms fade away naturally. Solutions like Eiffel and Rust take that philosophy into modern IT.
Since it is a web server we are talking about here, let's assume it is the backend space :)
I guess the answer is obvious: it is nice, but might not worth the effort. 1.2 million requests/s is pretty good, but in reality, you probably would still need 2-3 other hosts as a failsafe backups, let alone there are other factors, like memory constraint, in which case, less optimized solution might be just acceptable because it is sufficient. So under common usage cases, the need for scalability outweighs the urge to squash the maximum performance from a single machine.
Needing to think memory management turned out be a bit un-expected you might say. There were a few things that went well and few that were a bit unexpected.
Things that went well:
1) RAM got ridiculously cheap: 2x 8GB is now what $60-70? So it would seem, in that regard, yeah you shouldn't care about memory.
2) GC languages and runtimes. Believe or not there was heavy skepticism that GC runtime could be ever be a serious strategy in practice. It was an exotic technology that many thought would left for academia and fringe research area. But most popular frameworks and languages today are GC based.
Some things were a bit un-expected:
1) Memory access speed is fast but it didn't keep up with CPU speed. A cache miss will costs hundreds of nanoseconds. CPU speeds may have been doubling every 2 years or so, but memory access speeds have only been increasing by 10-20% or so.
In the 80s and 90s you just didn't care as much about cache hits and misses. But those are the forefront of performance optimization strategies. "Align your struct members","use structs of arrays instead of arrays of structs","think about cache lines and strides when you loop through your data arrays..." etc.
And because CPU speeds go so much faster, memory access also stands out on performance charts. So GC languages and runtimes can still seems slow and sluggish for applications that don't pay attention to memory.
2) Applications got more concurrent and servers are expected to handle a larger number of clients / connections / requests. That means each one needs a context, so it needs memory. So memory is again at the forefront. Accessing it and its latency, GC pause times are important.
3) Mobile happened. Mobile devices have proliferated so the needs for fast, small footprint applications never went away. Even as there are serves witih terabytes of RAM around, there are even more little arm cores and then everyone talks about IoT. That means langauges/runtimes which compile ahead of time will never be 100% displaced. Heck even on Android, ART (AoT copiling) is replacing JIT.
You can still micro-optimise for memory and cpu performance. That's certainly a thing that is possible to do.
Alternatively, you can buy more cpus and memory.
What has changed in the past few years is that for many workloads, the cost of buying more cpus and memory is significantly lower than the cost of all the development work to optimise away the need. Hardware is cheap compared to developers, for many applications.
There are still some applications where this is not the case, and in those cases people still care about memory management.
I think it comes down to scale. If you are running your application on 5 or 10 or maybe even 100 servers, buying a few more to handle the inefficiency makes some sense. But if you code needs to go on 1000s of servers or more, it's probably worth paying people to optimize it instead of buying more hardware.
On modern CPUs, cache misses take so long and unaligned access is nearly free in comparison, so that padding can become detrimental. Of course ordering members so that they're all aligned is the best option, but if that can't be done, it's better to have misaligned and pack more in the same space than leave wasted padding.
The results of unaligned access can vary greatly, even among the x86 family. You can hit a large performance penalty if one of your fields straddles a cache line boundary, and $deity forbid you cross a page boundary on a Core2.
Under the right circumstances packed structs can improve performance, but they come with a lot of technical debt since they can cause sporadic extreme slowdowns on certain common CPUs, and performance problems, aborts, or even data corruption on non-x86 platforms.
For x86, sure. For other architectures you end up with larger code size for unaligned access, so it also becomes a tradeof between code/instruction and data cache usage.
As I sit here on Sunday afternoon, trying to get a Phaser game to run on an iPad 2; I read this and think "We haven't stopped caring, we care when it's a specified business requirement".
This bites us in the ass, because I can't make Wistia (for example) stop eating all the memory on this thing with my app running next to it.
So, personally, I stopped caring when it was not a priority and didn't, on average, affect user experience. But; it was a mistake and I am watching memory/etc more and more these days. This is for various reasons (old hardware, more than one app on a page, growing amount of bundled media content, etc).
We stopped caring because it didn't matter. Except when it began to matter it was too late. Somethings you have to plan for from the start. You can't go back later and fix it after the fact.
Even switching from "copy and allocate objects everywhere" to non-copying native IO can improve your memory efficiency and usability.
I am working on an Android app, and rewrote the part that handles the connection via a binary protocol over a socket with a remote server. Rewriting that to take advantage of NIO already improved connection times (build up a connection, initialize, etc) from almost 2 minutes to only 8 or 9 seconds. RAM usage of the backend was reduced by 50% – and it is easier readable.
More often than not it’s not that hard to add it, if one carefully thinks about the requirements and future extensions at every step.
If you don’t want to think about memory from the start, at least think about making your code in a way that you can exchange it later on with memory-conscious code.
No warning, no error, mobile safari just kills the tab. It's not fun. Optimize images, optimize audio, stream audio, stream images, reduce text, pack text, pack everything. Repeat, repeat, repeat. Still crashes. How do people work with Apple stuff? I got a macbook and xcode, it lets me view logs/etc, but nothing helpful.
It seems to me that we have been doing this more and more as memory has gotten bigger and bigger, we care more and more about performance, and more and more people are writing more and more code that will be used for shorter and shorter times under tighter and tighter deadlines.
a) The whole quote is "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%"
b) The quote isn't talking about data structuring/memory layout, but rather micro-optimizations of instruction sequences (removing goto statements, in the case of the original article)
.. is 50% chance to die by thousand paper cuts, 25% simply wasted time, AND 25% chance to avoid death by thousand paper cuts.
At least in image processing and robotics, from my experience.
It's very annoying when you have optimized all obvious hotspots 10x only to get 3x speedup because the remaining 30% code consistently abuses « simple ways », e.g. return big objects by value, use std::vector instead of proper Eigen arrays, constantly serialize objects everywhere, etc.
It'd be a cool idea to sell warehouse storage with drone delivery of individual items if needed, that way you dont need to care as much about how much you can fit into your apartment at any one time
What if you could only carry things to/from that closet in a fixed-size box?
Typical L1 cache size for the x86 family is only a few times larger than on the 486. Cache is getting faster, but transistor shrinkage should affect cache speed and instruction execution time in roughly equal proportion.
Of course, the marginal value of spending development time improving performance can vary a great deal; but when performance matters, the key considerations are going to remain location, location, and location for the foreseeable future.
Some didn't stop caring about it. It's all about how much you care about performance, and whether you think no garbage collection is preferable (or not).
> How low should we go? How useful is it to know about C-style memory management when you're a front-end JavaScript Developer?
There are things in between as well (such as RAII). That's about using it. Usefulness of knowing it is another thing. Someone will have to implement things on the lower level either way. I.e. garbage collector itself is going to manage memory after all.
Enterprise applications moved to Java on purpose to lose the issues that memory management causes.
In my last job I first came in contact with people who had never had to do any type of memory management, that was only 8 years ago (that guy probably has his PhD by now and will be Dr Coole...seriously).
If you can stand the speed of GC languages then, there's a whole bunch of savings you get in development time and especially in support.
C-style memory management is important to know about but in C++ land, RAII and C++11 features are here to help. Move semantics, use of containers, references etc. all mean many of these bugs are a thing of the past.
In addition, extra tools and profiling / static code analysis has helped me to write better code.
The language has gotten to a pretty nice place, but the ecosystem is nowhere near caught up. A lot of APIs still haven't even incorporated the idiom of passing const references, almost exclusively taking pointers to non-const and defining their own idiosyncratic ownership schemes. Qt4 is a good example: RAII of widgets is hazardous because windows/containers will delete their "children" (i.e. things that are under them in the widget hierarchy, not members), even if they're on the stack. This means that simply declaring local variables in the wrong order can cause a double delete. I encountered this in tutorial code on the Qt site, which kind of put me off choosing Qt for new projects.
wxWidgets is the same - when a wxWindow gets deleted, it will remove its children in the view hierarchy, which are likely not children of it as data members. I think a lot of this comes from the fact that underneath native pointers are being used (eg handles on Windows) and widgets/controls are not necessarily going to be data members of a parent. It also means you can reparent widgets/controls to other GUI items at runtime. I do this (for example) when switching notebook pages to rehouse a GUI element in order to save RAM; I just assign it new data to display and it means less RAM usage and creation time for each notebook page.
For your own data controls, we can still ensure that they clean themselves up alright and safely, just use pointers for the GUI items. In some sense, the GUI toolkit takes care of deleting its own members, eg. wxWidgets will delete all of a parent wxWindow children at teardown, so it isn't a leak situation.
Since I was looking at it for hobby/educational projects, I basically decided to not do something with a GUI. I think I had also looked at wxWidgets and FLTK, hoping that one of them was cleaner and more modern; FLTK managed the "cleaner" part, at least. The last time I looked at C++ GUI libraries it seemed like JUCE was the most promising, but it also had a bit of weirdness (GPL license without linking exception, heavy bias toward audio applications).
To answer the headline, we stopped caring when a bunch of off-the-shelf solutions became available.
For a lot of problems, once you have this off-the-shelf solution, you're just not going to have a memory issue. It's like cooking without having to manage where the pots, pans, and plates are. Say you hire some guy to clean up for you, and the guy just implements some simple strategy. It's a lot more pleasant, and if you have a large enough kitchen, it won't make a difference to the quality (and timeliness) of the food. In some cases you may have to consider the guy running around, but most of the time you net win anyway.
For things that are truly performance sensitive, you still need to think about cache issues and such, and you definitely still care about memory management.
I can think of two projects in my past five years that really showed performance sensitivity. I knew that at design time so I chose to care about where data sat in memory and how it would behave in multilevel caches.
All other projects, couldn't care less where the kitchen dude stored my pots, and he never lost one.
None of those projects jumped from performance agnostic to performance sensitive overnight.
This article conflates two separate issues: Automatic memory management, and Virtual Machine runtimes. People often make the mistake of thinking that some sort of garbage collection strategy (such as reference counting[1] collection (ARC) or tracing collectors) implies that you must be running in a VM (which typically employ some kind of JIT to achieve reasonable performance). This is not true - see Haskell, OCaml, SML, and Swift (for reference counting collection).
In my experience, many of the issues with debugging performance issues are not related to memory management, but understanding JIT heuristics which are moving targets (especially in the dynamically typed languages).
I think we need to collectively separate the idea of some kind of automated (or semi-automated) collection strategy, from a language level JITed Virtual Machine. You may have one without the other.
I reread the article after seeing your post, and could not find where they made this conflation.
I've not worked on performance critical code running under a JIT, but have worked on performance critical code running under a GC, and having an intimate knowledge of memory can be vital to understanding the performance of your application.
The article divides programming languages into two groups: Those that require you to manage memory manually and those that rely on language level managed memory. However, the examples of the managed memory languages also rely on language level Virtual Machines and/or JITs (which are critical to achieving excellent performance in those respective languages). Then, the article points out that the people using these "garbage collected languages" (which are also JITed (in a VM likely)) tend to have problems debugging performance issues. If he's right, then the examples aren't making the case very convincingly because the realm of languages with garbage collection isn't divided granularly enough in his analysis. The result is attributing the problem to a common language feature that they all share, without acknowledging that they also all share other very important features.
I'd never claim that understanding memory usage/allocation/freeing isn't important. It's important to understand how memory is used whether that be understanding how your collector handles generations of memory in a GC'd language, or whether it be understanding how your programming patterns cause a waterfall of destructor chains in a manually managed language. Often, understanding these things is difficult.
My claim was that, in my experience, the biggest difficulties in debugging performance were in understanding JIT heuristics in language level Virtual Machines. This is because (especially in dynamic languages) the heuristics aren't documented anywhere, they're subject to change, and are likely not consistent between particular runtimes you're hosted within. (Those optimizations you made just for V8, could even hurt perf if you run your library in JSC).
There's also the issue with language level virtual machines in that they usually impose some system wide tax on many operations (a layer of indirection), so I could see the people mentioned in the article having a hard time squeezing out that last portion of performance due to that indirection - again, that might be the fault of there being a language level Virtual Machine - and not necessarily as much related to memory (it depends on your application, and amount you allocate, of course).
In my experience, optimizing collection behavior has been relatively straightforward in comparison and the techniques you learn for one collector seem to translate to others. Perhaps my experience is biased towards JIT/VM/GC in dynamic languages where the JITs are so much more complex in comparison.
I have seen core hot paths in critical client side JS libraries be slowed down 9x because of obscure JIT deopts caused by properties names beginning with a dollar sign. I'm not just talking about a 9x slowdown of field lookups - our entire end to end benchmarks tanked (since fixed). Perhaps the VM architects decided that something with a leading dollar sign would not likely benefit from being made into a hidden JIT class? I've never had anything so obscure happen with any kind of garbage collection, whether that be reference counted ARC, or a tracing collector.
Edit: spelling
Memory management still matters but by how much differs based on your specifications.
Are you programming for a microcontroller? For a low end VPS? For a low-volume high-turnover web app? Do you have a deadline? How many people should be able to come up to speed and contribute to your codebase?
The author's example is interesting. He talks about a webserver that after optimizations can reach 1.2 million requests per second.
In reality though, your gigabit ethernet interface can't handle that much anyway.
I never stopped caring. It just turns out it never really mattered for a large fraction of cases.
There's still plenty of cases where you absolutely do care: low level firmware, kernels, absolute performance, and high reliability. It's more a case of not worrying about it when there's much bigger risks in a project than having loose guarantees about allocation performance or fragmentation.
Developping on smartphone and tablet makes you still caring a lot about memory management if you don't want your app to be killed by the os... I guess the question was asked to server side web developpers...
Software growth is not keeping up with hardware growth.
My parents told me to "Clear your plate." That was because they lived through the depression. I remember writing code in assembler and 64k and feel the say way now. Programs I wrote in that space now take 64m and I think "What a waist".
Having worked in the memory chip business for 10 years: It's cheap!! Hell, for a while, companies were dumping memories below cost.
I remember a Fry's newspaper ad that said something like: "$50 1GB DIMM, $0 after in-store discount. (Limit 2 per customer.)"
Kind of similar to the old (and possibly apocryphal) Sun Microsystems legend: Frustrated that too many domestic programmers were spoiled with ample CPU speeds, they hired a bunch of Russian programmers who dealt with the export restrictions on computing gear and thus had learned to write tight, elegant code.
99 comments
[ 4.1 ms ] story [ 183 ms ] threadNot having to worry about memory is now a luxury we can afford in the age of plenty. While writing an efficient spellchecker would have been a great feat in the 80s, there's no point of that when you can load the whole dictionary file in memory.
In other words, memory management is a #highclassproblem for many developers--you worry about efficiency once you have a lot of adoption. (Within reason; if your app is unbearably slow that isn't good.)
And why settle for "without optimizing it's slightly faster than things used to be when people had to optimize", when you can have "optimized, it's orders of magnitude faster than things used to be"?
> you worry about efficiency once you have a lot of adoption.
Sometimes things can be tricky to refactor if you didn't move carefully at every step. Initially not worrying about efficiency at all might mean having to rewrite from scratch once you do.
Of course, this can't be generalized. Sometimes it really, really doesn't matter, unless you care about craftsmanship as a value in and of itself -- taking 10-20% more time to write code that makes you happy and proud might very well make you more effective in the long run.
But in the web-focused marketplace, adding 6 months to your launch calendar to get something fast to market may mean someone else's good-enough solution becomes the entrenched, de-facto standard that you are now fighting to prove you're superior enough to that it justifies people's time cost to move their data to your solution.
That's the risk tradeoff.
It's easy to think why not just do it right in the first place. Aside from the timing you, as a startup, don't really know what "it" is or "it" is constantly moving. Committing to perfection at that stage can be a sure way to kill the company. Even if "it" is known and it's not changing you still need to keep in mind the Gall's Law.
However, it has heaped to performance and code separation of concerns and structure debt. Of course, AirBnb can now afford to move things to Java/Scala and things that are better for multinational companies.
For AirBnb, it's really hard to fix a lot of these things since the product requires good uptime.
Try making your own particle engine and a simple game running at 60fps. I guess "avoiding GC" is only a very small subset of memory management, but if you're a noob like me, it surely will open your eyes about some things you would never have guessed could cause problems.
CMS GC doesn't choke your all for 100ms
In practice, the GC Apple was using prior to ARC could collect cycles, but it consumed a nontrivial number of CPU cycles compared to ARC.
Apple's tools people seem to be unconvinced that GC is efficient enough for production use on slow CPUs.
It makes sense for Swift because of Swift's unique constraints—interoperability with an atomic-reference-counted world (Objective-C) and, less importantly, mobile UIs putting demands on latency. But most applications would be better off with a tracing GC.
I know this to be true for ubiquitously heap allocated systems, but I'm curious if this has been studied extensively for stack+heap reference counted systems, where only a relatively small number of items are heap allocated? Seems like reference counting would gain in this kind of environment, as the stack effectively gives you many of the advantages generational gcs enjoy from their nursery, and you don't have to worry about write boundaries, tracing and the like.
2. Java and C# JITs perform many more optimizations than Go 6g/8g do.
3. Objective-C message passing is a lot slower than method calls in Java and C#.
I think it's due to LuaJIT being one of the very few tracing JITs that are still in development, since they are notoriously difficult to write and maintain.
If I care about performance I'd check the generated assembly and modify accordingly till I get decent result.
Baring that: do you have any reputable source claiming LuaJIT outperforms C#/java/C?
That would imply a horrid baseline in order to get it that fast. Merely I don't believe a tracing JIT would have a great impact in a real world scenario.
PHP with its shared nothing design makes it very scalable, the 2016 implementations are very fast and consume little memory. LuaJIT see other post. Object-C (there is C underneath, read https://en.wikipedia.org/wiki/Object-C ). Check out Julia, it's great. You can write pretty low level code in modern languages like Go, Rust without loosing everything (don't come with Sing# and Java related misc things, yada yada). Also my point was that these languages consume less memory. I have yet to see a Java or dotNet application that doesn't consume too much memory for my feeling (not to mention the GC idioms). When I read that people put monolithic Java enterprise apps in Docker containers or wonder why the new Startmenu crashes that often, it reminds me why I care about computer resources. After all we are still stuck with 3.8GHz single core performance, same as 2004! (memory bandwith,etc got faster, I know) Give me a 10 GHz single core CPU and we can start talking.
Most of the RAM of my application is used by the app and the goddamn UI and its fancy xxxhdpi and xxhdpi and xhpdi and hpdi and mdpi images, and because Android doesn’t support svg everywhere yet, we have to package tons of ressource twice and thrice and five times.
In the Windows world especially that's not even slightly true.
Run "Process Explorer", Options -> Color Selection -> check ".Net Processes" (Yellow).
In all consumer Windows up to Windows 7 no dotNet processes run (by default) at all. [Longhorn was meant to bring a dotNet shell (explorer, etc) but failed big time.] I have lots of common applications and all are coded in C/C++ with the exception of IDEs (VS/Eclipse/IDEA) which eat memory like no tomorrow. I couldn't care less about the brave Win8/10 world.
Still supports C++ ;)
So XP and Vista then.
Most of the software that comes pre-installed on Windows machines (by the OEM) is .NET. That fact by itself means the amount of software written in .NET is far far far above 1%.
> In Windows world you are very wrong.
So you're saying after the shedloads of money, effort and development Microsoft has pumped into .NET it hasn't even amounted to 1% of applications running it? That's preposterous.
DotNet and Java are very big in enterprise software and server software. Java is also big in open source thanks to Apache foundation (if you need a full text search engine, Lucene is still the only feature complete in town; and all the projects based around Lucene like Hadoop, Solr, etc) There was an C++ port of Lucene (CLucene) that was a lot faster and used little memory and compatible with Lucene file format. Though no native fork of Lucene keep up with the development speed of Apache Java Lucene.
All non-technical people, so 90% of users. I get where you're coming from (and you have valid points about speed/memory usage) but to say <1% of software on Windows is .NET is frankly wrong. Sure it's not 100% and might not even be 50%, but it's way way way above 1%.
So, yes, we've traded performance for automatic memory management. But we've gained reliability, safety, and security. For the vast majority of server-side apps, that's a worthwhile trade.
† Unless you use the processes like NASA and the like use, which for most apps are infeasible from a cost point of view.
If course it's possible to write stuff using manual memory management in a manner such that it's nearly impossible to do well. Doctor, doctor, it hurts when I do that...
I wrote a 'C' based thing around 2012 that:
- is about 50,000 lines; 50ish by 1000ish lines. - uses no dynamic memory. - does very "dynamic memory" things.
Each type/shape/class has its own statically allocated 'C' array of structs that is declared to worst-case, and has a "new". Behind the "new" is a lot of behavior, including pointing to other such things/objects.
A set of "delete" operations would be also relatively easy.
But because it's an array and it is in 'C', this is relatively little code. Yes, there is duplication involved but the object creation code is probably 20% of the total thing and each object creation is darn near provably correct. It's built on RAII principles ( perhaps foolishly, since there's no deletion ). Dependencies between objects are completely explicit in the code. Object creation must be in the correct order to manage dependencies
The magic happens because scripts* are used to create all things, once at startup. it also helps because by its very nature, the system is fully serialized - after a creation script, the state of the tables of objects can be written to a text file.
*scripts are, in this case nothing but sequences of verbs which send text files full of XML to a service at a TCP port. It could have been JSON or attribute-value pairs, but instead it's XML.
I do take extra care to make a very comprehensive test suite or it.
but since the allocation scheme is 1) cheap and 2) fully exposed as state in the system, there's nothing actually opaque about it. I don't have to do "trust falls" with a garbage collector or even the heap. You can't have a malloc() or new() failure - it'll violate a constraint and quit/exit(). You then make the table larger.
From the frame of reference of the other objects, it's abstracted and opaque - after the initial setup. From the "scripting" interface, it's fully transparent.
I've never seen a large C or C++ codebase written by a large team that didn't have serious memory management problems.
I just smell a lot of despair in these threads.
What you're likely a victim of is the Dunning-Kruger effect or Schneier's Law depending on which you prefer:
https://www.schneier.com/blog/archives/2011/04/schneiers_law...
On the other hand, you could've in fact designed something as good as you claimed. In that case, I seriously suggest you publish your methods in full for peer review as they could save millions spent by the likes of NASA, seL4 project, and so on. Any method that might hit full correctness with just basic coding or whatever is a potential game-changer. Very exciting stuff.
I doubt anybody is remotely interested in this - the problem domains are pretty boring and highly constrained. These tend to be realtime embedded ( both soft and hard realtime ). It seems wildly inappropriate for gaming, graphics or Web stuff. Perhaps I'm wrong there; not really all that clued in.
There is much less to this than you might imagine. The problem is that a book is a team effort, and I have no idea who to bother with it. Not asking for a volunteer here.
For an idea of the basic philosophy, it might be worth looking at Bruce Powell Douglass.
There's just so many ways that language can get you I have a doubt even a handful of methods would cover them all. It takes Work to get anywhere. Best of luck to you on that.
"For an idea of the basic philosophy, it might be worth looking at Bruce Powell Douglass."
Appreciate the reference. Can't recall if he's in my collection or not. I'll look into his work. :)
And I appreciate the heads up that I may be repeating myself/others, and that this subject is really for long form stuff.
LOL. I have to ask: do you "know" this from experience or someone else told you?
Thousands of applications were made not so long ago with manual memory management. Many of them are still the most used desktop apps in the world.
Methods to secure them are not rocket science, really. I would gladly concede it's cheaper to do CRUD apps with GC, but you're going hyperbolic with NASA ;-)
And pretty much all of them are unreliable garbage that we as an industry should have been too embarrassed to ship.
(of course, vulnerabilities and unreliability in those apps came from more places than just manual memory management, just as today's slowness comes from more places than the GC)
So you have experienced thousands of unreliable garbage apps. I feel really sorry for you. My experience has been the opposite: lots of wonderful and reliable programs. And when I found one that wasn't good, I stopped using it and replaced it with a better one.
I mean as a user. As a programmer I've seen all kind of programs, from "critical mission" to "quick hack" and from excelent to unreliable garbage. And you know what? It's not language features but ludicrous incompetence what makes an app unreliable garbage. There are programmers out there that can make unreliable garbage in any language.
The "secret" to make some app reliable is not reserved to NASA and the like. Follow two simple patterns:
1. For every user action, validate input, try executing the code, catch the occasional exception and show an error message.
2. For every resource, allocate, try{ using it } finally { release it }
Put each of these skeletons in a function or method, so the resource is a local variable (no colateral effects) and it's easy to debug.
That's all. Well, also: don't be too "creative" unless you know what you're doing (most times you don't).
I'm fifty-something, spent a lot of painful time mechanically applying that recipes to rotten codebases, and when I read the kind of criticisms you made I assume they come from people too young to have real world experience on what they're criticizing. The alternative is worse: to be the kind of people that created that rot and then blamed the language.
Just in case: I don't believe that manual memory languages are "better". I love Python and (most of the time) JavaScript.
Actually, every single year I've worked on security-critical software I've understood more and more how impossible it is to secure large C and C++ codebases worked on by large teams with just "careful coding". The picture changes a lot when you have people actively searching for vulnerabilities.
Do you believe that security software or browsers are better writen in other languages than C/C++? I won't dispute that, even if I'd have some questions about relative merits. Please think of some well known Perl and PHP applications and their security record.
Anyway the unqualified:
That's because correct C-style memory management never† scales up to large software projects in a way that doesn't result in crashes and, worse, vulnerabilities.
... and talking about manual memory management in general...
And pretty much all of them are unreliable garbage that we as an industry should have been too embarrassed to ship.
... are simply not true. Worse, they're insulting for everyone who has worked in this kind of application.
Yes. And lots and lots of apps (most public network facing things) are security sensitive.
> Please think of some well known Perl and PHP applications and their security record.
Nobody is saying that "memory safe == no vulnerabilities". I do think it's true that being memory unsafe results in many more vulnerabilities. I've measured this precise statistic in our bug tracker.
> That's because correct C-style memory management never† scales up to large software projects in a way that doesn't result in crashes and, worse, vulnerabilities.
It's true. And I work in native code myself!
It's only an insult if you presuppose that introducing vulnerabilities reflects badly on the programmer who introduced them. I don't believe that; most major vulnerabilities I've seen were introduced by very good programmers. It's a problem with the tools they're working with.
Where did you find reliable software, and what does it do? After twenty years, it all seems to be the shit it always has been.
Yes, I have worked on C++ apps for years (browsers).
What I am saying is what nearly everyone in the security community will tell you, from what I've seen.
Such thinking is why MULTICS used PL/0, why GEMSOS used Pascal, why Wirth used Modula and Oberon for safer OS's, and why Altran-Praxis used Ada/SPARK for a CA. Results were better than C (or predecessor) every time. Deal with root cause, symptoms fade away naturally. Solutions like Eiffel and Rust take that philosophy into modern IT.
I guess the answer is obvious: it is nice, but might not worth the effort. 1.2 million requests/s is pretty good, but in reality, you probably would still need 2-3 other hosts as a failsafe backups, let alone there are other factors, like memory constraint, in which case, less optimized solution might be just acceptable because it is sufficient. So under common usage cases, the need for scalability outweighs the urge to squash the maximum performance from a single machine.
That kind of scalability difference does still matter.
Things that went well:
1) RAM got ridiculously cheap: 2x 8GB is now what $60-70? So it would seem, in that regard, yeah you shouldn't care about memory.
2) GC languages and runtimes. Believe or not there was heavy skepticism that GC runtime could be ever be a serious strategy in practice. It was an exotic technology that many thought would left for academia and fringe research area. But most popular frameworks and languages today are GC based.
Some things were a bit un-expected:
1) Memory access speed is fast but it didn't keep up with CPU speed. A cache miss will costs hundreds of nanoseconds. CPU speeds may have been doubling every 2 years or so, but memory access speeds have only been increasing by 10-20% or so.
In the 80s and 90s you just didn't care as much about cache hits and misses. But those are the forefront of performance optimization strategies. "Align your struct members","use structs of arrays instead of arrays of structs","think about cache lines and strides when you loop through your data arrays..." etc.
And because CPU speeds go so much faster, memory access also stands out on performance charts. So GC languages and runtimes can still seems slow and sluggish for applications that don't pay attention to memory.
2) Applications got more concurrent and servers are expected to handle a larger number of clients / connections / requests. That means each one needs a context, so it needs memory. So memory is again at the forefront. Accessing it and its latency, GC pause times are important.
3) Mobile happened. Mobile devices have proliferated so the needs for fast, small footprint applications never went away. Even as there are serves witih terabytes of RAM around, there are even more little arm cores and then everyone talks about IoT. That means langauges/runtimes which compile ahead of time will never be 100% displaced. Heck even on Android, ART (AoT copiling) is replacing JIT.
You can still micro-optimise for memory and cpu performance. That's certainly a thing that is possible to do.
Alternatively, you can buy more cpus and memory.
What has changed in the past few years is that for many workloads, the cost of buying more cpus and memory is significantly lower than the cost of all the development work to optimise away the need. Hardware is cheap compared to developers, for many applications.
There are still some applications where this is not the case, and in those cases people still care about memory management.
On modern CPUs, cache misses take so long and unaligned access is nearly free in comparison, so that padding can become detrimental. Of course ordering members so that they're all aligned is the best option, but if that can't be done, it's better to have misaligned and pack more in the same space than leave wasted padding.
Under the right circumstances packed structs can improve performance, but they come with a lot of technical debt since they can cause sporadic extreme slowdowns on certain common CPUs, and performance problems, aborts, or even data corruption on non-x86 platforms.
This bites us in the ass, because I can't make Wistia (for example) stop eating all the memory on this thing with my app running next to it.
So, personally, I stopped caring when it was not a priority and didn't, on average, affect user experience. But; it was a mistake and I am watching memory/etc more and more these days. This is for various reasons (old hardware, more than one app on a page, growing amount of bundled media content, etc).
I am working on an Android app, and rewrote the part that handles the connection via a binary protocol over a socket with a remote server. Rewriting that to take advantage of NIO already improved connection times (build up a connection, initialize, etc) from almost 2 minutes to only 8 or 9 seconds. RAM usage of the backend was reduced by 50% – and it is easier readable.
More often than not it’s not that hard to add it, if one carefully thinks about the requirements and future extensions at every step.
Is that argument to think about memory from the start or to not think about it until absolutely necessary? :)
b) The quote isn't talking about data structuring/memory layout, but rather micro-optimizations of instruction sequences (removing goto statements, in the case of the original article)
At least in image processing and robotics, from my experience.
It's very annoying when you have optimized all obvious hotspots 10x only to get 3x speedup because the remaining 30% code consistently abuses « simple ways », e.g. return big objects by value, use std::vector instead of proper Eigen arrays, constantly serialize objects everywhere, etc.
Typical L1 cache size for the x86 family is only a few times larger than on the 486. Cache is getting faster, but transistor shrinkage should affect cache speed and instruction execution time in roughly equal proportion.
Of course, the marginal value of spending development time improving performance can vary a great deal; but when performance matters, the key considerations are going to remain location, location, and location for the foreseeable future.
> How low should we go? How useful is it to know about C-style memory management when you're a front-end JavaScript Developer?
There are things in between as well (such as RAII). That's about using it. Usefulness of knowing it is another thing. Someone will have to implement things on the lower level either way. I.e. garbage collector itself is going to manage memory after all.
In addition, extra tools and profiling / static code analysis has helped me to write better code.
wxWidgets is the same - when a wxWindow gets deleted, it will remove its children in the view hierarchy, which are likely not children of it as data members. I think a lot of this comes from the fact that underneath native pointers are being used (eg handles on Windows) and widgets/controls are not necessarily going to be data members of a parent. It also means you can reparent widgets/controls to other GUI items at runtime. I do this (for example) when switching notebook pages to rehouse a GUI element in order to save RAM; I just assign it new data to display and it means less RAM usage and creation time for each notebook page.
For your own data controls, we can still ensure that they clean themselves up alright and safely, just use pointers for the GUI items. In some sense, the GUI toolkit takes care of deleting its own members, eg. wxWidgets will delete all of a parent wxWindow children at teardown, so it isn't a leak situation.
Since I was looking at it for hobby/educational projects, I basically decided to not do something with a GUI. I think I had also looked at wxWidgets and FLTK, hoping that one of them was cleaner and more modern; FLTK managed the "cleaner" part, at least. The last time I looked at C++ GUI libraries it seemed like JUCE was the most promising, but it also had a bit of weirdness (GPL license without linking exception, heavy bias toward audio applications).
For a lot of problems, once you have this off-the-shelf solution, you're just not going to have a memory issue. It's like cooking without having to manage where the pots, pans, and plates are. Say you hire some guy to clean up for you, and the guy just implements some simple strategy. It's a lot more pleasant, and if you have a large enough kitchen, it won't make a difference to the quality (and timeliness) of the food. In some cases you may have to consider the guy running around, but most of the time you net win anyway.
For things that are truly performance sensitive, you still need to think about cache issues and such, and you definitely still care about memory management.
I can think of two projects in my past five years that really showed performance sensitivity. I knew that at design time so I chose to care about where data sat in memory and how it would behave in multilevel caches.
All other projects, couldn't care less where the kitchen dude stored my pots, and he never lost one.
None of those projects jumped from performance agnostic to performance sensitive overnight.
In my experience, many of the issues with debugging performance issues are not related to memory management, but understanding JIT heuristics which are moving targets (especially in the dynamically typed languages).
I think we need to collectively separate the idea of some kind of automated (or semi-automated) collection strategy, from a language level JITed Virtual Machine. You may have one without the other.
[1]https://www.cs.virginia.edu/~cs415/reading/bacon-garbage.pdf
I've not worked on performance critical code running under a JIT, but have worked on performance critical code running under a GC, and having an intimate knowledge of memory can be vital to understanding the performance of your application.
I'd never claim that understanding memory usage/allocation/freeing isn't important. It's important to understand how memory is used whether that be understanding how your collector handles generations of memory in a GC'd language, or whether it be understanding how your programming patterns cause a waterfall of destructor chains in a manually managed language. Often, understanding these things is difficult.
My claim was that, in my experience, the biggest difficulties in debugging performance were in understanding JIT heuristics in language level Virtual Machines. This is because (especially in dynamic languages) the heuristics aren't documented anywhere, they're subject to change, and are likely not consistent between particular runtimes you're hosted within. (Those optimizations you made just for V8, could even hurt perf if you run your library in JSC).
There's also the issue with language level virtual machines in that they usually impose some system wide tax on many operations (a layer of indirection), so I could see the people mentioned in the article having a hard time squeezing out that last portion of performance due to that indirection - again, that might be the fault of there being a language level Virtual Machine - and not necessarily as much related to memory (it depends on your application, and amount you allocate, of course).
In my experience, optimizing collection behavior has been relatively straightforward in comparison and the techniques you learn for one collector seem to translate to others. Perhaps my experience is biased towards JIT/VM/GC in dynamic languages where the JITs are so much more complex in comparison.
I have seen core hot paths in critical client side JS libraries be slowed down 9x because of obscure JIT deopts caused by properties names beginning with a dollar sign. I'm not just talking about a 9x slowdown of field lookups - our entire end to end benchmarks tanked (since fixed). Perhaps the VM architects decided that something with a leading dollar sign would not likely benefit from being made into a hidden JIT class? I've never had anything so obscure happen with any kind of garbage collection, whether that be reference counted ARC, or a tracing collector. Edit: spelling
The author's example is interesting. He talks about a webserver that after optimizations can reach 1.2 million requests per second. In reality though, your gigabit ethernet interface can't handle that much anyway.
There's still plenty of cases where you absolutely do care: low level firmware, kernels, absolute performance, and high reliability. It's more a case of not worrying about it when there's much bigger risks in a project than having loose guarantees about allocation performance or fragmentation.
My parents told me to "Clear your plate." That was because they lived through the depression. I remember writing code in assembler and 64k and feel the say way now. Programs I wrote in that space now take 64m and I think "What a waist".
I remember a Fry's newspaper ad that said something like: "$50 1GB DIMM, $0 after in-store discount. (Limit 2 per customer.)"
Kind of similar to the old (and possibly apocryphal) Sun Microsystems legend: Frustrated that too many domestic programmers were spoiled with ample CPU speeds, they hired a bunch of Russian programmers who dealt with the export restrictions on computing gear and thus had learned to write tight, elegant code.