27 ms·
It's an idea I've had for a long time for my applications where (almost) all dynamically are global arrays. 64-bit application have a huge address space and I c
by jstimpfle 8y ago
It's an idea I've had for a long time for my applications where (almost) all dynamically are global arrays. 64-bit application have a huge address space and I could simply place each array in a huge subsequence of this address apce. When an array needs to grow I simply mmap() more memory at the end, so I never have to move memory, and all pointers stay valid.
I try to avoid pointers to pointers because of the redirection. They are not really solving a problem but only delaying it, at the cost of performance.
But first and foremost the biggest problem with pointer is the identity thing. Using a pointer to access various kinds of information either requires that all this information is clumped together (loss of modularity) or at least that we maintain additional lookup structures that bind the disparate information together through indirection (loss of performance, more memory use, more code, and still some loss of modularity).
- shawnz 8y ago> I try to avoid pointers to pointers because of the redirection. They are not really solving a problem but only delaying it, at the cost of performance. Passing an array index and doing an array lookup is also a form of indirection. How is that any different than passing a pointer and doing a pointer dereference? Either way you are not passing the value itself, you are passing a value that represents some kind of memory offset where the real data is stored. It's just that with the array approach, it is a relative offset rather than an absolute one. > Using a pointer to access various kinds of information either requires that all this information is clumped together (loss of modularity) Why does putting all the information together imply a loss of modularity? You could have structs inside structs, defined in separate files, and there would still be no need to dereference twice. Their values will just be stored as part of the enclosing struct's values automatically. You could even reference the specific sub-structs with pointers if you want to keep your modules unaware of the enclosing struct type. Example: https://onlinegdb.com/BJLxqWhuV https://onlinegdb.com/BJLxqWhuV
- jstimpfle 8y agoJust plain global arrays are technically an indirection, but I suppose the performance impact is hard to measure in practice. You have a small number of global pointers (most project will have < 100) that can be expected to be in the L1 cache. The compiler can easily optimize the code by not loading the value of the pointer on each access to an element of the array. Pointers to pointers are one level more of indirection, both the properties I just described are probably not achievable in practice. > Why does putting all the information together imply a loss of modularity? You could have structs inside structs, defined in separate files, Because there is a place that must know them all, bind them all together. And all the client code that wants to use any of the things in there now depends on EACH of them (for a liberal but very real definition of "depends").
- shawnz 8y agoUnless the objects you're working with happen to be contiguously arranged in the arrays then it might be 100 cache lines for those 100 pointers. That's another reason why the "all the data together" approach would probably win performance wise - you'd get 100 whole objects for those 100 cache lines > Because there is a place that must know them all, bind them all together. [...] True, that could be a problem in some circumstances which might warrant a map-like structure like you describe. But I don't think it's the most obvious pattern otherwise
- jstimpfle 8y ago> it might be 100 cache lines for those 100 pointers No, the usual recommendation is that you should split your structures into smaller structures such that each contains only fields that are likely to be accessed together. In other words, you should err on the side of SOA (parallel arrays) rather than AOS. That's because the unused field apply pressure on the cache.
- jstimpfle 8y agoOops, yeah, I misread your post, and while my sibling comment is technically correct, it wasn't really a reply to your comment. Yes, the way I write it is absolutely that my global buffers are all allocated sequentially, so it's very cache friendly. And I partly agree that this is kind of "all the data together" in this instance. But note that there is not a struct that binds them together, and so there's no code that depends on them being layed out sequentially in memory. Actually I can achieve the effect without losing modularity at all, by having each module specify its global data categorized as types of data (one category of which would be "global buffer pointers"). I'll then just have the build go over all my modules and put them sequentially in the built image. This is exactly what ECS do, by the way. What you want is aspect-oriented program performance-wise (and maintainability-wise in general), but sometimes you still want to maintain parts as optional "packages". So what ECS do is they weave each aspect of each package into the final product. By the way, one very simple way to achieve this in C is to declare the data like BUFFERDATA int *myIntArray; in the header file of each module. And I define BUFFERDATA as "extern" for all modules except one data module, which "implements" all data declared BUFFERDATA by defining BUFFERDATA to an empty string and including all of the project's header files.