10 ms·
Every time I've seen similar done in the past, it has come back to hurt the instigator.
by zgs 2y ago
Every time I've seen similar done in the past, it has come back to hurt the instigator.
- pixelesque 2y agoI've used tagged pointers with no issues at all over the years to "smuggle" / store values in...
- speed_spread 2y agoIf you build your whole app with it, yeah. If you use it tactically for a certain data type it can be very nice. Just don't try to dissimulate it's specialness.
- db48x 2y agoEvery lisp system that ever existed uses this technique, and it never hurt them any. Emacs or SBCL or Open Genera; they all work perfectly well.
- _flux 2y agoOCaml uses the lowest bit, which is nice because (aligned) pointers work as-is.
- lmm 2y ago> Every lisp system that ever existed uses this technique, and it never hurt them any. Wasn't it part of the reason they ended up with poor "mechanical sympathy" on regular PCs, and got a bad performance reputation as a result?
- db48x 2y agoNo. Their reputation for poor performance mostly arises because the default data structure is a singly–linked list. Although the language makes these lists extremely convenient for the programmer to use, linked lists invariably result in terrible cache locality. High–performance systems that are written in lisp (and they do exist), achieve that performance by avoiding lists and using vectors and arenas instead. Of course, it must be said that this performance penalty rarely matters in practice. For most programs, the limiting factor is not performance but the cost of implementation and maintenance. Lisp has has many powerful and convenient features that make implementation faster and surer than in any other language, and which usually greatly lower the cost of maintenance as well. Thus by writing a new program in Lisp you can get it off the ground and earning you money faster than you could in almost any other language. You can also add features more cheaply as time goes on, allowing you to keep up with your competitors. It is only when your system is nearing completion, and all necessary features have been invented and implemented, that you should think about rewriting select parts of that system in a systems language where more efficiency is possible. Not only will you only understand the system after it is written, but you can measure the actual performance of the system to figure out which parts you need to rewrite, and which parts have acceptable performance already and thus do not need to be rewritten.
- jxf 2y ago> For most programs, the limiting factor is not performance but the cost of implementation and maintenance. The limiting factor for what? Their commercial success, or something else?
- db48x 2y agoSuccess, yes. It need not be direct commercial success; lots of programs are written purely for internal use within some larger organization. And even when there are known performance requirements, Lisp is often still a good choice because the performance is handled by specialized hardware. Consider games, for example. Time to market and rapid iteration are both paramount, while performance is taken care of not by writing a fast renderer but by sending everything to the GPU to be rendered on specialized hardware.
- pfdietz 2y agoCache locality is improved if the Lisp has a compacting garbage collector, but you still need extra memory for the cdr links. Lisp Machines had cdr-coding that allowed those to be avoided but I don't think that's practical on stock architectures.
- db48x 2y agoTrue, but don’t forget that one of the _other_ causes of performance problems in lisp programs is the creation and subsequent collection of garbage. If you know ahead of time which of your data needs to be accessed or created by the most performance–sensitive parts of your program, then you can put that data into vectors to start with and avoid all of the extra work.
- pfdietz 2y agoLong-lived things end up in older generations and aren't traversed much during GC, which is the moral equivalent of being statically allocated. There's overhead if you mutate old objects in a GCed system due to card marking. Lisp vectors are vectors of pointers, so there's still an overhead of dereferencing through it. Presumably the objects being pointed to end up compacted together, eventually.
- bitwize 2y agoI don't know what you mean. Maybe in the early early 80s that was the case, but PCs were still 16-bit back then, and would've been a poor fit for Lisp anyway. One of the reasons why the Lisp machines died out is that round about the mid-80s or so, compiler technology improved for generic 32-bit processors, and it became possible to run Lisp software on a VAX, 68k, or RISC CPU faster than it ran on the Lisp machines' bespoke architecture. Back during the first AI hypecycle, the makers of Golden Common Lisp introduced the Hummingboard to the market, billed as an inexpensive solution to have a "Lisp machine in a PC". It was just a 386 on an ISA card (predating Compaq's 386 PC by about a year) with gobs of memory on board; a special build of Golden Common Lisp allowed code to be run on that CPU rather than the main one.
- coldtea 2y ago>One of the reasons why the Lisp machines died out is that round about the mid-80s or so, compiler technology improved for generic 32-bit processors, and it became possible to run Lisp software on a VAX, 68k, or RISC CPU faster than it ran on the Lisp machines' bespoke architecture. I'd say Lisp machines died out because Lisp died out (commercially). Other languages got popular, the second AI winter didn't help at all either. If Lisp itself had fared better (even if it was on generic hardware), Lisp machines could have been saved too, they could still use a VAX, 68k, or RISC CPU underneath, and optimize for the developer experience. Or they'd have turned Lisp machines from hardware into a "Lisp machine" OS or IDE/REPL/etc environment for other OSes succeeding. But none of that took off.
- lispm 2y agoNo, the reason for less than stellar performance of Lisp on the 32bit x86 CPUs were things like too few registers. On 64bit Intel CPUs this is much less of a problem and Lisp runs roughly twice as fast in 64bit mode. I would think that this even holds without taking advantage of wider data words. On other architectures this was less of a problem. 32bit CPUs had more registers on a RISC or some other CISC CPUs.
- samatman 2y agoIt's not. Two counterexamples: LuaJIT's interpreter/VM, and Wren's, both use NaN boxing, and are among the fastest VMs out there (we're ignoring JITs as out of scope). It isn't tagging pointers that makes things (some Lisps are 'things', some are not) slow: it's pervasive abstraction and indirection. Doing some masking on a pointer is pipelined, out-of-order, one to three cycles, and is absolutely dwarfed by cache misses and conditional mispredictions, let alone the sort of pointer chasing which is common in idiomatic Lisp (or indeed Python) code.
- rjsw 2y agoMaclisp on PDP-10 and Franz Lisp didn't, they used bibop type checking instead of tagged pointers.
- db48x 2y agoThat’s merely an elaboration on the same theme.
- rjsw 2y agoIt isn't, pointers in Franz Lisp can be used unmodified in C.
- db48x 2y agoBut those pointers still have bits in them that indicate the type of the object that they point to. It is merely the case that the tag bits are chosen at run time so that they correspond to allocated memory regions, rather than being chosen ahead of time.
- rjsw 2y agoNone of the bits in the pointer have been "stolen" though, which is the point of the referenced article, they are all still available to access the full address space of the CPU.
- db48x 2y agoThat doesn't mean that they aren’t still there, and don't tell you the type. It’s still a tagged pointer, and because all objects of the same type must be allocated out of the same page (or collection of pages), it still limits how much memory can be dedicated to objects of that type.
- spacechild1 2y ago> Franz Lisp Off topic, but that's probably the silliest name for a programming language I've seen. I love it!
- gpderetta 2y agoAnd the modern take on this is NaN Boxing!
- junon 2y agoVM writers use this egregiously. I've not heard of it causing issues. It's not that complicated. Lua, Lisps, many others.
- masklinn 2y agoApple's been using that for more than a decade: https://www.mikeash.com/pyblog/friday-qa-2012-07-27-lets-build-tagged-pointers.html https://www.mikeash.com/pyblog/friday-qa-2012-07-27-lets-bui... http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_e... OCaml's also been using tagging to mark unboxed type pretty much from the start, nearly 30 years ago: https://dev.realworldocaml.org/runtime-memory-layout.html#:~:text=OCaml%20values%20don't%20all,the%20block%20word%20is%20zero https://dev.realworldocaml.org/runtime-memory-layout.html#:~....
- duskwuff 2y agoAnd it's not like Apple isn't aware of the dangers, either; they used top-byte tagging on the 68000, and it took them a lot of time and effort to migrate away from that to support 16+ MB systems.
- gpderetta 2y agoIIRC the issue was that 68000 would transparently mask the top bits, so code could get away with avoiding the explicit masking, which of course breaks when you move to a larger address space. More modern cpus enforce specific bit patterns for the MSBs, so broken code would be caught immediately.
- coldtea 2y agoThis "trick" has been used since the dawn of time in major platforms
- titzer 2y agoAll fast JavaScript VMs use some form of pointer tagging. V8 uses (compressed) tagged pointers and both JSC and Firefox used NaN-boxing. WasmGC has a i31ref as part of its reference hierarchy, which is implemented with pointer tagging.
- jstimpfle 2y agoIt's common for libraries to use the 2 lowest bits in tree structures e.g. RB tree. It's not a problem at all since the data structure is controlled by the library. Even if tree nodes are allocated/provided by the user, having a "4-byte aligned" requirement in the API contract isn't a problem at all -- in fact you need to work quite hard to allocate a structure with a pointer that isn't at least 4 byte aligned (i386), or 8 byte aligned (x64).
- a1369209993 2y ago> in fact you need to work quite hard to allocate a structure with a pointer that isn't at least 4 byte aligned (i386), or 8 byte aligned (x64). Well, no, actually; it's: p = malloc(size+1)+1; It's just quite implausible that you'd do that by accident.
- menaerus 2y agoWell no it isn't since malloc returns a void* and incrementing a void* is impossible for a compiler to figure out the offset that it needs to increment address for. For that operation to succeed/compile you need to cast the result of malloc first to p*. This means that the resulting alignment will still be correct. The actual danger is in reinterpreting the pointers, or thereof some arbitrary chunk of memory, to a type that doesn't necessarily satisfy the alignment requirements for that given memory address.
- jstimpfle 2y agogcc will compile this code and advance the pointer address by 1 byte. Yes, it's not standard conformant. My point though is that this is unlikely to happen by accident. The code above is clearly constructed. It's more work to type it than the correct version. The code makes no sense at all. 1 byte is wasted and it's in general a bad idea to allocate unaligned pointers. And when you for example provide your own allocator for nodes, you'll probably do it like this: Treenode treenode_pool[MAX_NODES]; or this: Treenode *treenode_pool = allocate(Treenode, node_count); And the memory will be aligned properly. Even when you use a plain malloc (no type or alignment information given to the allocator) you will get memory that is at least pointer-aligned.
- adql 2y agoAny example ?
- mbrubeck 2y agoThis bitvec I wrote for Servo and Gecko uses this technique, and has been shipping in Firefox releases for over six years with no bugs found: https://docs.rs/smallbitvec/ https://docs.rs/smallbitvec/ I'm pretty sure you can find several more examples in Firefox, as well as other major browsers.