5 ms·
> My critique was about list implementation, not about implementing the entire runtime. Linked lists are actually managed by the runtime. Example on a esoteri
by lispm 2y ago
> My critique was about list implementation, not about implementing the entire runtime.
Linked lists are actually managed by the runtime.
Example on a esoteric platform, a Lisp Machine, a real, but outdated, computer. But we still have Virtual Lisp Machines, which have a virtualized implementation (not as in a microcoded CPU, as the real ones were).
If I cons a list via CONS, I get a linked list made of cons cells. But over the lifetime of the list, this may change. At some point in time the runtime may decide to copy the list (for example because it has a copying garbage collector). The effect then is that the list is allocated in contiguous memory. The program itself does not see a difference.
First the list (1 2 3) is allocated as [1 | -> [2 | -> [3 | NIL]]], written in Lisp as (1 . (2 . (3 . NIL))) . At some point in time the runtime changes this transparently to [1][2][3], where the cells are tagged, such that the successor is in the next memory word and where the last element is tagged that it has no successor. That's called cdr-coding.
Now If I copy a list, with COPY-LIST I always get a cdr-coded list as a result. Several list functions return cdr-coded lists, instead of linked lists.
This cdr-coding method is not much used anymore, since there is not much of an advantage: either one uses linked lists (because they are easy to use and have useful properties, like that CONS does not need to copy its arguments) or other available data structures (vectors, structures, ...).
What is still used are locality improving garbage collectors.
> Using garbage collection is not equivalent to using the entire Lisp model. Java, JavaScript, Python, Lua, Ruby, C#, etc. do not make extensive use of linked lists in their standard libraries, instead preferring--you guessed it--vectors.
Lisp also makes use of vectors, where necessary. These languages with managed memory like Java have the same object/reference model as Lisp and the same calling mechanisms. linked lists are only a special case (-> java.util.LinkedList).
Say we have N cities. Each city is a an object of class CITY. Each city will have attributes, for example NAME and PEOPLE-NUMBER. Now we want to keep two "lists" of them one sorted by name and another one sorted by PEOPLE-NUMBER.
We need two vectors and N city objects. In Lisp the city objects are not stored in the vectors. They are stored somewhere in the heap. -> there goes your "contiguous memory" to bust. The vectors are pointers into a heap. Every access to the nth city object through one of the nicely contiguous vectors, references an object which is stored somewhere else.
That model is used in many languages implementations. It's the Lisp model of memory management, introduced with Lisp 1 -> dynamic memory management plus a garbage collector to reclaim unused space.
> And if you dig into how structs are implemented in say, C#, they are implemented as contiguous blocks of memory.
That's also the case in Lisp. But a struct (or list or vector) of structs usually points to those being somewhere on the heap. A struct/list/vector of structs is not a single contiguous block of memory. structs in slots are typically not inlined. Some languages inline data non-primitive structures, Lisp usually does not.
> contiguous blocks of memory
That's can be an illusion. In a virtual memory system, the memory is made from pages of a fixed size. Random pages are cached in RAM, influenced by their usage pattern over time.
> in most languages it does not entail pointer chasing
It does, see above. Just not tail pointers in the list.
> Ironically, higher order functions like map/reduce/etc. push you toward operating on lists as a whole which is exactly the case where vectors most outshine linked lists.
That's why in Common Lisp the functions MAP and REDUCE work over vectors, too.
> Lisp-family languages are fairly unique in this problem.
see Prolog and various functional languages, ... some even have basic strings implemented as linked lists of characters (which Common Lisp does not do).
If we look at data structures, one tries do address quite a bit more than "contiguous memory", for example persistence, runtime updates and runtime lookups, ... see for examples: https://cstheory.stackexchange.com/a/1550 https://cstheory.stackexchange.com/a/1550
- kerkeslager 2y ago> Lisp also makes use of vectors, where necessary. These languages with managed memory like Java have the same object/reference model as Lisp and the same calling mechanisms. linked lists are only a special case (-> java.util.LinkedList). You seem insistent on missing my point. "Where necessary?" is almost always, because vectors almost always outperform linked lists based on cons cells. If your program uses cons cells at all, chances are you made the wrong choice. Lisp doesn't make use of vectors in many cases "where necessary" because "where necessary" is a whole lot more than Lisp's structure, libraries, and community encourage you to do. Simply pointing out that java.util contains a Linked List implementation means nothing. I wrote Java full time professionally for ~5 years and never saw that used once. If Python or C# ship with linked list implementations I'm not aware of them because they're also never used. To be frank, if your program uses linked lists at all, it's probably a mistake from a performance perspective. There simply aren't many cases where a linked list is a better choice than a vector. And this is a mistake that Lisp programs make all the time because cons cells are so baked into the language. > > in most languages it does not entail pointer chasing > It does, see above. Just not tail pointers in the list. It is both dishonest and rude to not even quote a full sentence when you're trying to refute something. Tail pointers on the list being avoided is exactly my point, a fact which you obfuscated by removing context. That is a huge amount of added cache thrashing, even with cache-aware allocation. This is not something you can just pretend is a minor issue. Are you even capable of admitting that Lisp has serious faults, or do you really think it's completely, 100% perfect? This isn't a rhetorical question: I legitimately would like to hear what serious faults you think Lisp has, because if you can't present any problems with Lisp at all, then you simply aren't capable of participating usefully in conversations about Lisp. Lisp is a human invention which includes human mistakes, and if you can't admit that, anything you say about it is going to suffer from crippling bias and can't be trusted. And to be clear, I'm not interested in further whataboutism. If your idea of a problem with Lisp is something where you can end with "but every other language has this problem too", stop--that's just further proving your inability to criticize Lisp. I'm happy to describe serious problems unique to any of the languages I've mentioned in this thread, but that's not the current topic of discussion. Pointer chasing in linked lists is a cause of performance problems in Lisp and it isn't a cause of performance problems in other languages. No, not for retrieving each individual item, I'm talking about moving from one item to the next. Every single thing you've said in this thread has been trying to move the goalposts to make it seem like this isn't a problem or it isn't unique to Lisp. It is a problem. It is unique to Lisp(s).