3 ms·
"When are linked lists actually good?" Your question is more accurate than you realize! Because the answer is... the past. Up until about the 486/50MHz era, C
by jerf 4y ago
"When are linked lists actually good?"
Your question is more accurate than you realize! Because the answer is... the past.
Up until about the 486/50MHz era, CPUs and memory were attached to each other; one CPU cycle was approximately equal to one memory access. I don't mean that you could reach out to RAM in exactly one cycle and get a value, there were still some CPU caches and other considerations, but it was much closer to that ideal than on modern systems. And if you go back in time even farther, that actually was the case (Commodore 64, for instance).
(I don't know if there was ever a "true" 486/66 system, but I remember that as the CPU/clock speed where the CPU finally and definitively detached from RAM speed because that was generally a double-clocked 486/33 from the RAM's perspective. I can't quite remember the marketing term that was used. It's a dead term now because everything works that way.)
In those circumstances, traversing a linked list was not necessarily that much more expensive than a vector, and you could win on the other things a linked list can do faster than a vector, like insert in the middle, especially an insert in the middle when you were traversing the list anyhow. You could also win on a linked list containing a relatively large value organized just by pointers; a sort on the linked list manipulating just the pointers could win versus a sort that was trying to move around larger values constantly during the sort. And so on.
When memory accesses aren't hundreds and hundreds of CPU cycles, when your hardware doesn't have prefecting implemented, when your pointers aren't 64bits wide, when you aren't on modern systems essentially designed to make vector-based access go zoom, linked lists make a lot more sense.
This is how they got embedded in curricula so hard that they are taught to this day. They used to be a very important data structure. Now they're an antipattern. It happened gradually, though, and it doesn't help that linked lists are just about the easiest non-trivial data structure to teach and I'm not sure they could get removed from the standard curriculum for that reason alone.
"One case that seems to make sense is any time you want to do constant-time pops/appends… maybe?"
Another problem linked lists have is that if you know that's what you're going to do, you can build vector-based solutions to that problem that work just fine. Vector-based stacks, for instance, are trivial, and O(1)-amortized for push and pop, which is good enough in practice. (A bit more care is needed than an only-growing vector but IIRC it can be done.) You need not just something linked lists are better at, but some bizarre cocktail of all the things they're just barely better at, and you still need to construct a win out of the combo. It's nearly impossible. Not quite impossible. I assume without looking that the Linux kernel still has some linked lists for good reason, as I'm sure if they could win on performance by removing them they would. But very hard.
- kevin_thibedeau 4y agoThe majority of microprocessors today are <200MHz, single core, single issue, with no cache. The world doesn't run on x64 and GPUs.
- LorenPechtel 4y agoI would think a queue would be best served by a linked list. Every pop moves the whole list unless you get fancy an implement it as an array with head and tail pointers and grow logic. If you keep a tail pointer inserts are likewise O(1). You rarely traverse them.
- tobiasSoftware 4y agoCircular buffers (your fancy implementation) are far superior for queues. Pretty much any modern vector like data structure is already going to have grow logic so adding head and tail pointers isn't too hard. However, as this article mentions, keeping memory compact is pretty important to speed, and circular buffers are able to do that while linked lists aren't.
- jerf 4y agoBy data structure standards, the requisite amount of "fanciness" to implement a queue in a vector is table stakes, hardly even worth 10 minutes of class time at the sophomore level, if not simply given out as homework. It is no problem at all to put a queue in a vector. You'd need a doubly-linked list for this, too, and as the article mentions, your linked-list queue is likely to lose just on malloc & free (or local equivalents) versus the vector. (The best optimization you could do in such a case would be to allocate multiple slots per pointer, to amortize the malloc/free time. Then you'd want to run benchmarks to tell what the optimal amount of slots would be. If a vector is optimal, as I strongly expect it would be, the answer would come out to be, "all of them".) Modern processors move chunks of RAM around really quickly. They're really optimized for it. It is one of the major things I'm referring to when I say our systems have been optimized for C. I often wonder about what an architecture designed in a world where linked lists were dominant would look like. However, it is certainly not this world.
- jodrellblank 4y ago