Some time ago I used something like this in a project for a microcontroller, but instead of the PoolObject struct that you used in the video, I used an union of a pointer to my PoolObject, and the data struct intself, and then used a single-link list to store the pool using that pointer to link each memory block. Since the pointer is needed only while the block is in the list, but its value doesn't matter when it is borrowed, an union is perfect. Getting an object was as simple as returning the first element in the pool, and returning it was also as simple as adding it to the top of the pool. Both operations used constant time, no matter how many elements I had borrowed. Also, I sorted the elements in my structure based on their size (first, 64 bit integers and doubles; then 32 bit ints and floats; then pointers; then 16 bit ints; then chars and 8 bit ints and bools). That ensured that the struct was as compact as possible and allowed me to take maximum advantage of the available memory.
@josephbaker993215 күн бұрын
I have done that before with creating a union to a block of memory, then referencing it by the union member that matches up with the code that is trying to access it. Works well.
@Cambeast12315 күн бұрын
Interesting, could you provide a brief example so I can better understand/visualize what you’re saying? Thanks.
@rastersoft14 күн бұрын
@@Cambeast123 I tried to put a pastebin, but youtube removed the comment. It probably think is spam 😞 I'll copy the code here... #include typedef struct { int x, y, z; } OBJECT; typedef union { OBJECT object; void *pointer; } OBJECTPOOL; #define N_OBJECTS_POOL 10 OBJECTPOOL pool_block[N_OBJECTS_POOL] = {0}; OBJECTPOOL *pool = pool_block; void init_pool(void) { for(int i=0; ipointer; return obj; } void return_object(OBJECT *obj) { OBJECTPOOL *p = (OBJECTPOOL*) obj; p->pointer = pool; pool = p; } int main() { init_pool(); printf("%p ", pool); OBJECT *obj = borrow_object(); printf("%p %p ", pool, obj); OBJECT *obj2 = borrow_object(); printf("%p %p ", pool, obj2); return_object(obj); printf("%p ", pool); return_object(obj2); printf("%p ", pool); return 0; }
@rastersoft14 күн бұрын
(I know that the initialization isn't very elegant... I just preferred to make the code easier to understand what is doing)
@darshanrajpattanaik215412 күн бұрын
@@rastersoftOne better thing I think you can do is use just integers to reference the index of the object pool, and instead of doing an O(n) initialisation, you can just keep an n = next_index... If you remove from pool, put the value of n in the union, so you can retrieve it back. You know it is better to use 32bit numbers instead of 64bit pointers ;)
@Oddbob615 күн бұрын
There is a slight oversight in the second version of the return-function: if the user tries to return a pointer that lies outside the pool, "i" might become negative or larger than the pool and the write access to "allocated" would possibly corrupt other memory. You should check the bounds for "i" to be 0...(NUM_OBJECTS-1).
@JacobSorber15 күн бұрын
Good point. Thanks!
@rustycherkas822915 күн бұрын
The variable 'i' is declared 'unsigned', so negative is not an issue. It's also an 'unsigned int' which means there might be truncation of a wider 'pointer' datatype happening. That would open the door to multiple (wrong) collision possibilities of the calculated value if the pointer (address) provided by the caller is garbled or wildly wrong. If memory is tightly constrained, expending a 'bool' (16bits??) as a simple "yes/no" flag for each object is not conservation... Perhaps the 'opaque' librarian (object pool driver) could be allowed to use a reserved "sentinel value" inside the data record to mark "available" vs "occupied"?? When dispensing objects, the data region should be zeroed-out to ensure buggy application code can reproduce its own bugs for finding and squelching.
@nickeldan15 күн бұрын
You could also do `PoolObject *pool_obj = (PoolObject*)((unsigned char*)v - offsetof(PoolObject,obj));` in the return function.
@jakedeschamps445413 күн бұрын
If you rearranged your PoolObject Strict so that Vector3 obj is first, then the address of obj would be the same as the address of the entire struct. Using this, you could cast the Vector3 pointer in ReturnVector3 to a PoolObject pointer, and be able to modify the 'allocated' member of the PoolObject struct without all the extra pointer math.
@revealingfacts4all12 күн бұрын
Agree and his math is wrong but works due to float to int implicit rounding.
@BlitterObject15 күн бұрын
An added bonus of using object pools when used by applications running on modern -- let's say at least a decade old -- CPUs (in non-embedded platforms) is the notion of data locality, which, on those CPUs, equates to being more friendly with the CPU cache. This yields a more efficient (faster) application when applied correctly. Look into Data-Oriented Design for more details about effective cache use.
@doodocina15 күн бұрын
there is no bonus. there in no data locality. fastest place in memory where you can allocate smth is stack, slowest - global memory. read about memory paging
@jlewwis19956 күн бұрын
@@doodocinawhat, the stack is in ram just like the heap 🤨 The only reason the stack would be faster is it's probably more likely to be in the cpus data cache more often, well that and the most recent variable on the stack can be obtained with a pointerless assembly instruction which is going to be smaller and slightly faster to execute than one involving a pointer
@doodocina6 күн бұрын
@@jlewwis1995 learn some basics. cache is 10000 faster than ram. it's not "most recent variable" it's several kbs/mbs which is a lot. also we have stackframe so even not yet allocated varaibles are likely to be in cache. the most valued optimizations in programming come from resolving cache misses. video for lamers - lamers in comments, nothing new...
@jlewwis19956 күн бұрын
@doodocina bruh the cache is loaded with values from ram, when you try to load a variable from memory, whether that be on the stack or the heap, it'll look in the cache first to see if it's in there already, BUT, you literally can't use the cache directly, you use an instruction to load a variable from memory and the CPU will load the cached copy if that area of memory has been read from recently and is in the cache already, like you said, it's way faster to do that, I already know that... You said "the stack is faster" but that's not true because the stack is in ram just like everything else so just like everything else it's only actually faster if it's in the cache already, it just so happens that because every function call you make USES the stack (at least, if you aren't doing weird inline assembly things) that part of memory is likely to be in the cache very often because its being used very often which makes it *seem* inherently faster when in reality the stack isn't really much faster than any other part of memory, at least not TECHNICALLY, though in practice you are more likely to be able to avoid cache misses if you store variables on the stack due to the reasons I just said.
@jlewwis19956 күн бұрын
@@doodocina Also are we talking about allocation and deallocation or just data manipulation in general? OBVIOUSLY allocating and deallocating stack space is going to be way faster than heap space because you don't have to ask the os to do that, you can just change the stack pointer
@fusca14tube15 күн бұрын
Very nice! I think part 2 could explore more about ObjPool because it's very useful! Tks.
@d97x1715 күн бұрын
Thank you for the interesting video! I would love to see how an object pool can be implemented more efficiently. And I would definitely be interested to see a different underlying data structure being used for the pool, including its advantages and drawbacks
@DeLuxMusicChannel15 күн бұрын
Hey, great video as usual :) Just a feedback from an audio engineers perspective: Go easy on the de-reverb or noise supression. Also reduce some compression. Its a bit exhausting for the ears to listen to this unnnatural processing. Keep it up!
@kahnzo15 күн бұрын
Would it help to get his mic closer or just make sure that the mic is a cardiod so that it's not picking up outside noise? Just a touch of de-essing might help.
@Pi7on15 күн бұрын
Here I was wondering if my headphones were tweaking out lol
@JacobSorber15 күн бұрын
Thanks. I was trying to address an annoying echo, but I definitely don't consider myself an audio engineer. I'll play around with it for next week's video. Please let me know if that one sounds any better to you.
@DeLuxMusicChannel15 күн бұрын
@@JacobSorber will do! Moving the mic closer is the best way to counteract annoying room acoustics. Maybe consider investing in a lavalier mic. Otherwise just dial down the de-reverb and compression a bit. A bit of reverb sounds way more natural to our ears than the heavily processed audio.
@kadealicious13 күн бұрын
@@JacobSorberAnother way to mitigate the echo could be as simple as strategically placing a towel near your microphone. If that goes well, you might not need the extra vocal processing at all :)
@gatty.15 күн бұрын
2 minutes in -- yes please, keen to see some videos on those topics/things!
@pyajudeme924514 күн бұрын
Nice one! I would like to see a video about other allocators!
@SpooderDev15 күн бұрын
As someone new to C and currently learning the language, your videos are really useful in helping me become a better programmer 👍 C is definitely my favorite programming language because it gives you some basic tools which is all you need to literally make anything
@rian0xFFF15 күн бұрын
I wish a video about safe code in C, like common mistakes
@christopherpetersen34215 күн бұрын
Nicely put. It reminds me of code I wrote 30 years ago for processing HL7 messages at line speed. I think I called that a "free list with pre-allocation" rather than a "pool", but the concept is useful in lots of areas as you said. From an "object" perspective, this could be a good thing to hide behind a Factory pattern. Cheers!
@samjohnson504414 күн бұрын
Excellent, both conceptual explanation and implementation. Thanks for the tip about uint_ptr.
@rishithaminol1515 күн бұрын
This video is really helpful Jacob ❤
@greg436715 күн бұрын
Happy New Year, Jacob.
@JacobSorber15 күн бұрын
Happy New Year to you too!
@alexanderzikal72448 күн бұрын
Thank You, that's phantastic stuff😀👍
@azizkavas699315 күн бұрын
It would be really nice if you can also mention how other mallocs can decrease memory fragmantation
@emeraldmonarch35929 күн бұрын
12:55 I would say to implement the "Objectpool" as an arena, and keep track on the top most free, and if you don't want to clear the pool every single time, you can use a stack, as alternatives to make a faster pool
@heavymetalmixer9115 күн бұрын
Other advantage of pools/arenas: They make debugging easier, as the code has way less points where the memory bugs could come from, and the same goes for errors.
@DDSTrainers15 күн бұрын
Object pools are the way to go. Thanks for sharing.
@rustycherkas82299 күн бұрын
Just to say that pointer differencing would have been cleaner as: unsigned int index = v - &objectpool[ 0 ].obj; The compiler sees that both pointers (addresses) are addresses of the same datatype (Vector3) and happily emits the correct code (without any programmer fiddling.)
@josephbaker993215 күн бұрын
Another way to do this is to create the array of objects, then create an array of pointers to the objects. Then manage the array of pointers as if it was a stack. Or said another way, create a stack of pointers to the array of objects. An advantage of this is that you can have bool 'empty' and 'full' flags, which saves you having to do pointer arithmetic. It also saves you having to have the 'allocated' flag. If the pointer to the object is on the stack, it is not allocated. If it is not on the stack, it is 'allocated' The care that needs to happen here is if an ISR can push or pop while you are updating the 'push' & 'pop' pointers in the stack.
@user-sl6gn1ss8p15 күн бұрын
How would you go about taking care of that case?
@josephbaker993215 күн бұрын
@@user-sl6gn1ss8p I never figured out a really good way handle concurrency. Turning off interrupts globally can work, but the risk is that you hold off an interrupt that needs to be serviced quickly. Sometimes I would use queues to pass data from an ISR to a process running in my while(1){} loop. But you still run into the problem of handling concurrency when modifying the 'empty' flag on each queue. (Use a queue to pass data from (eg a UART Rx ISR) and to (eq a UART Tx ISR).) One way I tried that seemed to work was for both the ISR & the main process have counters that are initialized at the same time. When the ISR increments its copy of the counter, the main process sees that the main process' counter no longer matches the ISR, which means it needs to get something out of the queue. Going from the main process to the ISR is easier: just have main process sending data trigger the ISR by setting a flag. That is particularly easy in an architecture that has a single cycle bit set instruction. Handling concurrency would be a great topic for a video?
@chorew15 күн бұрын
Great explanation.
@rustycherkas822915 күн бұрын
Okay. Might have been a good idea to emphasize for beginners that their application won't be able to get an array of adjacent objects this way... "But, malloc() can do an entire array all at once!!??" Understanding memory layout and use is a hurdle to be cleared before further progress can be made. That "pointer arithmetic" gives me shivers. CrowdStrike taught us to ALWAYS test for NULL before a function can tentatively trust a pointer... Might be a good idea for `release()` to zero-out the data bytes, too. Consistent buggy behaviour is easier to hunt-down than random buggy behaviour...
@qwertyplm13does5115 күн бұрын
Nice video.I feel motivated to do some c
@MyriadColorsCM11 күн бұрын
I managed to make the memory pool to be type agnostic, I can create pools of any kind of object, but they need to be separate pools, I need a pool for, say, vector2, vector3, bullets and so on. WOuld a 'generic' object pool even work? Anyway, thanks for the video! Coudl you make a video on memory arenas?
@bartlomiejodachowski14 күн бұрын
returnvector function could take in double ptr to vector so that we could null out the user ptr
@carolinemathieson14 күн бұрын
In any hard real time system you often have severe time constraints to meet. Using any code that takes a variable amount of time to execute will likely prevent meeting those real time constraints in every circumstance. It isn't just malloc() and free() though but even simple things like printf() where you can never be sure how long it will take with a given format string. Just running on any multitasking OS is going to prevent guaranteed execution times as well. Which is why code that need to run in a fixed amount of time often gets implemented in FPGA form. But even then there are issues because it takes time to feed the data in and then extract the processed output.
@JacobSorber7 күн бұрын
Good point. Thanks.
@liendatt834715 күн бұрын
Great video as always Jacob. Isn't this fixed size linear allocator?
@catcatcatcatcatcatcatcatcatca8 күн бұрын
I always expected manual memory management to be maybe not very complex, but at least very clumsy. But I guess you can get pretty far with very minimal structure, at least if we can work with set sized blocks.
@meni18181810 күн бұрын
thanks sir. just pointing out that *v might be null so your assert is necessary.
@rdubb7715 күн бұрын
Is this good for real time applications like flight control or audio processing?
@matiasm.312415 күн бұрын
Very nice video.. thanks 🙏
@tommcboatface190813 күн бұрын
Great video! Could you make a video on Obstacks?
@josephbaker993215 күн бұрын
Could you do a video on handling concurrency in c, particularly in 8-bit or 16-bit micros that are passing information / data between an ISR and a background process?
@deepakr826115 күн бұрын
Thanks Dr Sorber for the great video. Could you also do one on intrusive linked list at some point. With reference to your video, I was think if we had an intrusive linked list inside Pool object to point to Free objects we could make the Borrow function faster as well.
@JacobSorber15 күн бұрын
Next week. 😀
15 күн бұрын
Happy new year! In the second implementation of the ReturnVector3 you are computing the I (index) with the actual address of the given pointer of *v. Yet the object pool consists of PoolObject which means we need to calculate the offset with this in mind I guess. unsigned int I = ((uintptr_t)v - sizeof(bool) - (uintptr_t)object_pool) / sizeof(PoolObject); right?
@rustycherkas822913 күн бұрын
Happy New Year to you, too! Presuming the parameter pointer 'v' is unaltered from what has be "handed out", the "integer division" (truncation) deals with the "slight offset" caused by that bool variable. Listen closely. This is mentioned in the video (@20:58). FWIW: If you did want to include this bit, sizeof( v.allocated ) would be more robust. Imagine changing the datatype from bool to long for some reason, but overlooking that sizeof(bool) operator in that long formula. Venturing into "pointer arithmetic" like this is fraught with hazards. 'bool' is probably still okay, but 'char' may be followed by padding byte(s) to maintain alignment of the next struct member... There are 'offset_of()` macros that should be used appropriately...
12 күн бұрын
@@rustycherkas8229 Thank you so much.
@rustycherkas82299 күн бұрын
Just to say that pointer differencing would have been cleaner as: unsigned int index = v - &objectpool[ 0 ].obj; The compiler sees that both pointers (addresses) are addresses of the same datatype (Vector3) and happily emits the correct code (without any programmer fiddling.) (Gonna post this again as a top level comment for others to, perhaps, see. Cheers!)
@user-vn9ld2ce1s15 күн бұрын
About that pointer arithmetic in the ReturnVector3 function, why don't you get the index just using (v - object_pool)? If the types are both Vector3*, it will give you the index directly, right?
@JacobSorber15 күн бұрын
In this case, they point to different objects. One is pointing to a Pool Object (object_pool) and the other to a Vector3 (v). I would have to double-check, but I think subtraction of pointers to objects of different type produces undefined behavior.
@user-vn9ld2ce1s15 күн бұрын
@JacobSorber Oh, sorry about that. I thought object_pool was simply a pointer to an array of Vector3
@josephbaker993215 күн бұрын
Would it work to include the index of the object in the struct? This would save having to do the pointer arithmetic in the ReturnVector3? As a retired Embedded Engineer, any time I see a division, I worry about execution speed. Especially because the div operation is usually implemented in code in a library somewhere and also may even have unpredictable latency. typedef struct { bool allocated; uint16 indexOfThis; Vector3 obj; } PoolObject; This would require an initialization routine (for loop) to initialize the member "indexOfThis". I also understand that this is storing the same piece of information in effect in two places in my solution. The position in the array that you can get from doing pointer arithmetic is the same as the index I am proposing adding to PoolObject. This is considered crude by some programmers. However, I am trying to trade the usage of a small amount of extra memory (the indexOfThis) for a lower and predictable, latency? BTW - some people advocate for placing the smaller members of the struct into the end so that the complier can 'pack' the struct. Some compilers will align each member of the struct on 4 bytes boundaries to take advantage of architectures which have 32 bit wide data buses, which will cause offsets of: 0x00 allocated; 0x04 indexOfThis; 0x08 Vector3.x; 0x0c Vector3.y; 0x10 Vector3.z; Contrast that with: typedef struct { Vector3 obj; unit16OfThis; bool allocated; } PoolObject; 0x00 Vector3.x; 0x04 Vector3.y; 0x08 Vector3.z; 0x0c indexOfThis; 0x0e allocated; This problem of inefficiently packing structs gets worse as the data bus width gets larger (8 bytes for a 64 bit data bus) and as the struct has more elements. Perhaps I am not giving modern compilers enough credit for doing a good job of optimizing and packing?
@auroranorton857015 күн бұрын
I always seem to have to implement stuff you make videos on a week before
@leonelgiuliano46418 күн бұрын
I don't really get here where the memory leak is (I mean when you don't return it) Like, doesn't this object pool really return just a pointer to stack memory? I don't see where the heap comes into play
@camius115 күн бұрын
Great video
@curtisstofer667815 күн бұрын
As someone who doesn't know a whole lot about alternate allocation schemas, I would love to hear an explainer on them.
@cyanide008112 күн бұрын
google "memory allocation strategies" by gingerbill (it's the best series of articles i've ever read on the topic)
@JosueHernandezg15 күн бұрын
Great! basically it is in a gross way it is how a memory allocator works but without the option of allocate dynamic sizes, memory allocators also add an control struct containing the allocated space, for returning the object I probably will use the container_of idea to get a pointer to the control struct instead getting the index, but it would added additional complications like be sure that the pointer returned is part of the object poll, some allocators use a magic number as a member of the control struct to be sure of that.
@tommcboatface190813 күн бұрын
Might be a bit too advanced of a topic, but could you make a video on garbage collection in C? It only takes 100-200 lines, or we could use tgc. I think Cello has an article on making our own GC.
@ragsbigfella12 күн бұрын
Any plans to make videos on Golang and Rust?
@mathmage42015 күн бұрын
Initialize an vector with integers ranging from 0..num_objects. When you allocate an object pop a index from that vector for the memory address. When you free an object push its index to that vector.
@josephbaker993214 күн бұрын
I like it. Using a stack to determine whether something is allocated or not is very clean. Please see my comment about using a stack of pointers to the elements in the array. Same idea, just implemented in a different way?
@rustycherkas822913 күн бұрын
The array values will likely become 'scrambled', meaning linear searching is still necessary to detect/prevent 'releasing' the same object multiple times. Without first validating the operation, "pushing" references to free objects onto a stack is going to break quickly.
@mathmage42013 күн бұрын
@@rustycherkas8229 not sure how having a 'scrambled' stack implies the need for linear searching. It will only cause issues if you double free but that would be an issue with any allocator
@rustycherkas822913 күн бұрын
@@mathmage420 Why does free() terminate execution on double free? If your implementation doesn't do something similar, it's not worth much. Your comment suggests there's a free lunch to be had using a stack. Just ain't so...
@mathmage42013 күн бұрын
@@rustycherkas8229 You can implement a check in debug mode
@Southpaw172 күн бұрын
Rather than modeling the object pool as an array of structs ( { Vec3, bool }[] ), a struct of arrays ( { Vec3[], bool[] } ) can both simplify the pointer math and reduce wasted bytes as a result of memory alignment
@JacobSorber21 сағат бұрын
True. Thanks.
@doodocina15 күн бұрын
whats the point of pools? we have arenas, we have stack. you said you don't want to use stack for example, ok, but whats the point of not using stack instead of pool then? using global variables is a bad practise too, so you are basically trying to provide "better" practice forgetting about others. and it's not about "trying to keep example very simple", good programmer writes good code no matter what
@cyanide008112 күн бұрын
pools can be useful if you know you're allocating objects of the same size, and you can always use a stack allocated array as the backing memory block where your pool's chunks will live in (like arenas, pools are just one of many strategies you can use to fit your context)
@danser_theplayer0115 күн бұрын
Aren't structs basically "interfaces" for making multiple objects with the same structure as the struct? That sounds very OOP, that sounds almost like a class.
@doodocina15 күн бұрын
no. structures are memory layout. bunch of offsets that compiler uses instead of .member notation. oop is about inheritance mostly. to achieve inheritance you need a vtable, basically an array of methods of the class. you can make oop in c, it's not that hard
@qwerte69489 күн бұрын
@@doodocina yeah runing functions with a vtable is incredibly slow... i mean a method is basically just a function that takes a custom data struct as the first argument, no need to over complicate it.
@doodocina9 күн бұрын
@@qwerte6948 not incredibly, there is instruction caching in cpu and other optimizations. as i know, method is a common name for functions(that return value) and procedures(that don't). but i agree, oop is a bad programming practice. it slows down development and running speed
@tetraquark240215 күн бұрын
Did this back in the day with a file handles on an phone auto attendant dating app because only had 512mb of RAM and every byte counted
@jesselangham15 күн бұрын
Slab/slob/slub video would be great
@bartlomiejodachowski14 күн бұрын
is assert at line 48 21:35 really neccesary? isnt it like reverse of what we just computed? is it possible to differ? maybe if user provides invalid ptr as vector ptr, if offset is invalid, we would catch it.
@JustinCromer10 күн бұрын
Arena allocation next? 🥹
@PotatoCider15 күн бұрын
I guess the only downside of this is allocing objects is O(n) where n is the number of objects allocated
@PotatoCider15 күн бұрын
I guess, we can trade off memory for performance by introducing a free list
@rohandvivedi15 күн бұрын
IMO, we could manage the free objects as a singly linked list (as a free list), instead of the allocated flag, now the allocation would be O(1). Also now the pool looks more like Jeff Bonwick's slab allocator.
@djyotta7 күн бұрын
Conceptually, I don't really see much difference between this and OOP. You can think of objects as pointers in general. Your PoolObject wrapper is essentially a private member of the object that only the object factory can manipulate. Honestly, this seems way better than OOP to me as it forces you to think about how these objects are used and what should be exposed to the developer. Couple this with a soft rule of "allocating scope should free" then you have essentially OOP without the needless abstractions and stupidity.
@nbecnbec8 күн бұрын
You're just implementing malloc on the stack instead of the heap. ??
@JacobSorber7 күн бұрын
The object pool is global. So, not on the stack (unless you made it a local variable...then it would be on the stack). But, yeah, this is kind-of like implementing malloc except that you always get the same sized block every time, and you always know exactly how much RAM you're using.
@rustycherkas82293 күн бұрын
NEVER return a pointer to a region of stack. When the function exits, its stack space is up-for-grabs by whoever wants it.
@mikhailbrest638514 күн бұрын
with such mem layout with 3 vectors you loss another vector because of bool padding, standalone bitmask is better approach it's 8 times lighter and there is no need for casting at return when the pool is just an array of vectors
@erichanson42015 күн бұрын
The book I'm learning C from is so old, pools of any kind aren't mentioned at all
@jakedeschamps445413 күн бұрын
Well, a pool isn't a core feature of C, it is a data structure made by programmers. "The C Programming Language" by Kernighan and Ritchie, and many other references for learning C focus on features directly related to the language, rather than hyperfocusing on specific implementations of those features with programmer-made data structures like pools. You probably shouldn't expect to find too many things like this in books specific to learning a language like C, as there are times when structures like this will be applicable in other languages as well.
@erichanson42013 күн бұрын
Thank You
@flamurmustafa52215 күн бұрын
There is the terminology of objects in c, it’s just not the same as c++
@Jol_CodeWizard15 күн бұрын
No one cares but... I am a true Code Wizard when it comes to object pools 🙂
@yongwu-z8q13 күн бұрын
Completely wrecked. I’m retiring from this game. okx Wallet seed phrase: [fabric siren awesome luggage wink certain guide tissue duck install liquid dilemma]. If there’s anything left, it’s yours.
@zxuiji15 күн бұрын
20:12 Would've been better to just PoolObject *pool = &(object_pool[0]); PoolObject *node = (PoolObject*)(((bool*)v)-1); size_t i = (((uintptr_t)node) - ((uintptr_t)pool)) / sizeof(PoolObject); assert( node >= pool && i < NUM_OBJECTS ); node->allocated = false;
@austinraney11 күн бұрын
This was my thought too. I’m not overly familiar with the C memory model for struct layout and if you could ever run into a situation where the compiler could insert padding in between the`allocated` flag and the, let’s say, T (in this case Vector3). That would throw this off.
@zxuiji11 күн бұрын
@@austinraney __attribute__((packed))
@ninomojo12 күн бұрын
Not on topic, but what's wrong with your audio? Every time there's a cut your voice jumps in level in a way that stands out and isn't nice. Also there's a sort of resonance on your voice the whole time that would have been nicer EQ'd out... This makes this video a little hard to listen to for me
@christopherpetersen34215 күн бұрын
PS. The "faster" returner has a math issue. "v" doesn't point to the beginning of the PoolObject struct, so if your pool is big enough, the math will become wrong for higher values of "i"...
@christopherpetersen34215 күн бұрын
PPS. Wrote some code that proved I was wrong about that. The rounding of the integer division doesn't seem to screw up at least up to tens of thousands of small objects...
@kahnzo15 күн бұрын
I'm just wondering if this is a response to Casey Muratori | Smart-Pointers, RAII, ZII? Becoming an N+2 programmer kzbin.info/www/bejne/rqWUfIF6orSMp6M Thank you for posting this. I like the use of an array for this, but I'd also like the hashtable implementation.
@custardtart131215 күн бұрын
Audio is awful. Please reprocess and reupload. Unwatchable in present form.