Live data from Hacker News

Handles Are the Better Pointers (2018)

floooh.github.com

61–70 of 113 posts

Re: Handles Are the Better Pointers (2018)

#61

MacOS 1.0 in 1984 used handles in order to allow the heap to be coalesced more easily (although locked handles and regular pointers often created islands). Given the first Mac had 128K RAM and no MMU, it was highly necessary at the time.

Came here to say this. Referencing objects through handles allowed much better memory management on a primitive OS like System 3.

Re: Handles Are the Better Pointers (2018)

#62
post #55
post #47

Earlier quoted context omitted.

In my Perfect Programming Language That Doesn't Have To Deal With The Problems Of Actually Existing, serialization of data structures into memory is explicitly defined in the language, just as one would define a JSON or Protobuf serialization. One could define a "Point3{x,y,z}", and hypothetically define one array of them in the conventional manner, define another array as a columnar serialization, and potentially ev…

Jonathan Blow's new language, Jai, has some control over layout allowing easy switching between array of structs and struct of arrays without having to change the accessing code.

I thought he pulled that back out of the language, though with the intent of re implementing it with as a library with the metaprogramming facilities of the language for anyone who wanted it?

Re: Handles Are the Better Pointers (2018)

#63

Earlier quoted context omitted.

You are essentially passing an database index key to the getter-function. The only issue I see is that your system has a database integrated into it.

I mean, calling it a database might be technically accurate, but is misleading in the sense the it gives the impression that it's a big heavyweight thing. It's not like there's a SQL implementation in there.

You jest, but SQLite run in-memory is a pretty fast database. I'm currently using it for ECS implementation in a little game I'm writing on the side (albeit in Common Lisp, and not yet very performance-heavy).

The reason I went this way is because when writing my third ECS with various access pattern optimizations, I realized I'm hand-implementing database indices - so I may as well plug SQLite for now, nail the interface, and refactor it into Array-of-Struct implementation with handles later.

Re: Handles Are the Better Pointers (2018)

#64
shared_ptrs, if misused, can cause a lot of memory management issues, because you end up with a web of object lifetime dependencies that is hard to reason about. I think this is a severely underrated problem in C++, particularly for newer developers. shared_ptr is extremely powerful, but it it makes it easy to gloss over issues of object lifetimes and ownership, when in fact those issues are still very important for building robust systems.

In the code bases I've worked on, we have pushed hard to have unique ownership wherever possible and only use shared_ptr where there was a clear need. You can still have a huge object graph kept alive by a single unique_ptr, but it happens less often and it's easier to trace back and fix.

A nice thing about the handle approach is that it makes it a lot harder to build up these object graphs or in general to implement anything without being explicit about object lifetime.

I've seen parts of the handle/object pool approach misapplied and cause more trouble than it's worth, though. It's a good idea for self-contained subsystems where there are a limited number of object types that you would apply this to. I don't think it scales to 100s or 1000s of distinct types of objects because then you're going to have headaches dealing with the sheer number the object pools.

I've also seen object pooling be implemented proactively as an "optimisation" to avoid calling malloc() but then become a bottleneck because of lock contention in the object pool.

Re: Handles Are the Better Pointers (2018)

#65
post #47

Earlier quoted context omitted.

Everything I read these days seems to trend towards columnar stores... I write a mixture of R, Python, C and C++, in that order of prevalence, and mostly for data intensive tasks. After nearly two decades of unlearning loops and implementing instead with FP idioms, vectorization, and matrix multiplies to take advantage of R's strengths, I often have trouble recreating R's performance when I first reimplement in C or…

In my Perfect Programming Language That Doesn't Have To Deal With The Problems Of Actually Existing, serialization of data structures into memory is explicitly defined in the language, just as one would define a JSON or Protobuf serialization. One could define a "Point3{x,y,z}", and hypothetically define one array of them in the conventional manner, define another array as a columnar serialization, and potentially ev…

The idea of "univalence", being pursued in Homotopy Type Theory (AKA HoTT), is that isomorphic types (i.e. we can convert back and forth without information loss) are equal, and hence one can be used in place of the other.

The dream scenario is to write all of our libraries and application code using types that are the most straightforward (e.g. counting in unary, mapping over linked-lists, looking up values from lists of key/value pairs, etc.), then cast these to more efficient types (e.g. machine words, arrays, tries, etc.) for the final program. It's still ongoing, and a naive approach would simply lead to a bunch of conversion functions being scattered around (making the program even slower), so a smart approach would be needed, e.g. where compilers can fuse away these conversions.

Re: Handles Are the Better Pointers (2018)

#66

Earlier quoted context omitted.

Everything I read these days seems to trend towards columnar stores... I write a mixture of R, Python, C and C++, in that order of prevalence, and mostly for data intensive tasks. After nearly two decades of unlearning loops and implementing instead with FP idioms, vectorization, and matrix multiplies to take advantage of R's strengths, I often have trouble recreating R's performance when I first reimplement in C or…

Typically functional programming isn’t considered to be CPU friendly. Specifically typical functional programming allocates memory all over the heap, especially given the prevalence of things like linked lists and so on. This obviously makes for crumby cache performance and memory pressure. I’m not sure in what sense FP lends itself to columnar data structures, but those would obviously have still better cache and ve…

The difference is that if you're doing functional programming with vectors as the fundamental data type (rather than scalars in linked lists), then the implementation could be hyper optimized underneath the programmer to maximize computational throughout. Whereas with imperative for loops, the implementation is somewhat fixed because python, C, and C++ force you to choose row (array of struct), or columnar (struct of arrays). R prefers struct of arrays, and makes arrays of struct a very difficult and horribly unacceptable slow. But if you are willing to use higher order functions, R makes struct of arrays suuuuuper fast. Python/C/C++ make arrays of structs the "default", by programmer culture if not by language/runtime capabilities.

Re: Handles Are the Better Pointers (2018)

#67

Moving away from pointers and using a shared pool of handles with integer indexes will introduce a whole new array of issues. A plain int carries no information about the validity of the object behind that handle because they are carried around as copies, not references. I remember debugging a regression with UNIX network sockets where valid connections were being killed, and the bug was only triggered under heavy lo…

>A plain int carries no information about the validity of the object

So, just like a pointer then?

Re: Handles Are the Better Pointers (2018)

#68
post #35

Earlier quoted context omitted.

Been there, done that. I was writing an interpreter using shared ptrs all over the place. Performance was fine until I linked pthreads, at which point it took a nosedive forcing me to refactor the entire project. No one really tells you this. People will say don't use shared ptrs because they're slow, which is far from the whole truth. They're pretty darn fast as long as you don't enable threading.

>I was writing an interpreter using shared ptrs all over the place. I am doing that :/ In Object Pascal it is the standard memory management way. All strings and arrays are reference counted, and so I use it for all my own data structures as well I have not even added threading and I doubt I will, but the reference counting is already the slowest part Not sure what the alternative is

You can avoid reference counting overhead by using handles as described in this article. Each object in a slab would have just one reference, and you handles would be indexes into an array of them. Now, after you do that you will be on be hook for making sure you don't accidentally deallocate any of the objects that is currently in use. And it's not guaranteed to be faster. But it is one way to get around this issue.

Re: Handles Are the Better Pointers (2018)

#69
post #47

Earlier quoted context omitted.

In my Perfect Programming Language That Doesn't Have To Deal With The Problems Of Actually Existing, serialization of data structures into memory is explicitly defined in the language, just as one would define a JSON or Protobuf serialization. One could define a "Point3{x,y,z}", and hypothetically define one array of them in the conventional manner, define another array as a columnar serialization, and potentially ev…

The idea of "univalence", being pursued in Homotopy Type Theory (AKA HoTT), is that isomorphic types (i.e. we can convert back and forth without information loss) are equal , and hence one can be used in place of the other. The dream scenario is to write all of our libraries and application code using types that are the most straightforward (e.g. counting in unary, mapping over linked-lists, looking up values from li…

HoTT changes what equal means. In order for univalence to work you must be comfortable with arbitrary code running to perform the necessary substitutions. Isomorphism isn't for free, you can do some fancy cast-conversions (where cast is an identity function) that has been explored in generic zero-cost reuse [1] but this is not HoTT and uses essentially an UIP equality (which is inconsistent with HoTT).

Your usage of the word "cast" in the second paragraph is a misnomer, because no systems programmer is going to want to perform an expensive transporation in HoTT!

[1] https://arxiv.org/abs/1803.08150

Re: Handles Are the Better Pointers (2018)

#70

MacOS 1.0 in 1984 used handles in order to allow the heap to be coalesced more easily (although locked handles and regular pointers often created islands). Given the first Mac had 128K RAM and no MMU, it was highly necessary at the time.

Those were not the same kind of handles AFAIK. This particular article is about pooling objects of the same time AND using indices (which the articles calls handles) to reference them. They aren't handles by any definition I've heard of in the past. AFAIK the definition of a handle is an opaque reference to some object, when you want to use the object you ask the handle for the object's actual address being aware that the address is only useful for some defined period (like having to call ptr = acquire(handle) ... do work with p ... release(handle) (p is now not valid).

Handles by that definition have the usage the can be moved in memory to clear up fragmentation but they have none of the other benefits this particular post is talking about. Those benefits only come from their specific implementation, not from the idea of "handles".

Post reply on HN