[video output=day351 member=cmuratori stream_platform=twitch stream_username=handmade_hero project=code title="Optimizing Multithreaded Simulation Regions" vod_platform=youtube id=6mTkcOlaUUc annotator=Miblo] [0:35][A few words on the new format] [1:41][Recap and set the stage for the day] [2:43][Run the game, consult the profiler and assess our current situation] [9:32][handmade_entity.cpp: Stop profiling the entity system, run the game and consult the event count] [11:48][handmade_world.cpp: Stop profiling GetWorldChunkInternal(), run the game and consult the profiler] [13:22][handmade_world_mode.cpp: Make PlayWorld() set the WorldChunkDimInMeters larger, run the game and consider drawing the bounds of those chunks] [16:46][handmade_world.cpp: Introduce GetWorldChunkBounds()] [21:25][handmade_world_mode.cpp: Make UpdateAndRenderWorld() call GetWorldChunkBounds() and draw them] [23:22][Run the game and view that debug visualisation for the chunk] [26:52][handmade_world_mode.cpp: Make PlayWorld() set the chunk size roughly equal to the room size, run the game and view the visualisation] [29:31][handmade_sim_region.cpp: Change BeginWorldChange() to only simulate one room's worth] [31:20][Run the game and note how our simulation region erroneously splits entities] [32:29][handmade_world_mode.cpp: Make UpdateAndRenderWorld() operate on the eight rooms above, below and around the player] [36:33][Run the game to take a look at how that's working] [38:46][handmade_platform.h: Add BeginTicketMutex() to the profiler, run the game to consult the profiler and consider how to improve the multithreading] [42:11][handmade_sim_region.cpp: Enable BeginWorldChange() to do a bunch of work without needing to be in a mutex] [46:17][handmade_world.cpp: Enable AddToFreeList() to insert the whole list in one go] [48:05][Run the game and consult the profiler] [50:27][handmade_world.cpp: Make RemoveWorldChunk() and AddToFreeList() themselves begin and end the mutex] [52:54][handmade_world.cpp: Consider what UseChunkSpace() does, make it begin and end a mutex, run the game and consult the profiler] [56:22][handmade_sim_region.cpp: Consider how to improve the ticket-taking in BeginWorldChange()] [59:38][Run the game and consult the profiler until we hit a deadlock and investigate why] [1:02:43][handmade_world.cpp: Make the correct version of UseChunkSpace() begin and end a mutex, run the game and consult the profiler again] [1:04:40][handmade_world.cpp: Add RemoveWorldChunk() and AddToFreeList() to the profiler, run the game and consult it] [1:06:00][handmade_sim_region.cpp: Add more timing blocks and functions to the profiler, running the game and consulting that profiler] [1:10:04][handmade_sim_region.cpp: Establish that the PushStruct() call in BeginWorldChange() is taking half our time] [1:10:37][Note that the timer totally failed] [1:11:09][handmade_memory.h: Investigate why that PushStruct() is so expensive] [1:13:11][handmade_sim_region.cpp: Add a timing block around that PushStruct() call in BeginWorldChange(), run the game and inspect the profiler] [1:14:11][Consider why we are clearing this buffer] [1:16:20][Break] [1:21:35][Return and wonder if the chat figured out what's wrong with the code] [1:23:30][handmade_sim_region.cpp: Make BeginWorldChange() pass NoClear to that PushStruct() call and run the game to consult the profiler] [1:25:11][handmade_sim_region.cpp: Time the ZeroStruct() call, run the game to consult the profiler and consider how to optimise this routine] [1:28:27][handmade_debug_ui.cpp: Enable DrawTopClocksList() to print out the cycles per invocation] [1:29:58][handmade_math.h: Introduce a 64-bit version of SafeRatio0()] [1:30:24][Run the game and consult the profiler] [1:31:12][Break into the ZeroStruct() call in BeginWorldChange() and determine that the clear is costing 7 cycles per byte] [1:33:55][handmade_sim_region.cpp: Verify that the clear's cycle-time is reasonable] [1:37:21][Consider ways to minimise the memory footprint] [1:40:55][handmade_sim_region.h: Add EntityHashOccupancy and BrainHashOccupancy to the sim_region struct in order to enable doing the clear with a bitfield] [1:44:03][handmade_sim_region.cpp: Introduce MarkOccupied(), MarkBit() and IsEmpty()] [1:49:03][Run the game, hit the assert in AddEntityToHash(), and fix that assertion to handle the new scheme] [1:51:12][handmade_sim_region.cpp: Fix the sense of IsEmpty(), run the game and crash in ExecuteBrain()] [1:52:02][Build and run in -Od and investigate what's happening] [1:54:18][handmade_sim_region.cpp: Add an assert in AddEntityToHash() to validate the hash, run the game and do not hit that assertion] [1:55:07][Step through AddEntityToHash() and investigate what's happening] [1:56:30][handmade_sim_region.cpp: Walk through GetOrAddBrain()] [1:57:38][handmade_sim_region.cpp: Make GetOrAddBrain() set the Hash->ID, run the game and crash in FreeFrame()] [1:58:31][handmade_sim_region.h: Remove the ID from the entity_hash and brain_hash and instead use their Ptr to identify them] [1:59:36][Run the game and note that "our brains are still not working properly"][quote 535] [2:03:55][handmade_sim_region.cpp: Add assertions to GetOrAddBrain() in order to verify ID resolution] [2:06:51][handmade_sim_region.cpp: Make BeginWorldChange() and GetOrAddBrain() clear the Brains, run the game and note that we're still getting weird behaviour] [2:08:06][handmade_memory.h: Enable overflow checking in PushSize_(), run the game and...] [2:08:41]["So we don't seem to be having any overflow problems at the moment... *stream crashes*"][quote 536] [2:09:43][Wait for Windows to decide to suspend the process] [2:13:23][Run the game normally having removed that overflow checking, and watch the memory counter] [2:14:30][handmade_sim_region.cpp: Make BeginWorldChange() clear the SimRegion, run the game and watch the profiler] [2:15:40][handmade_sim_region.cpp: Temporarily make BeginWorldChange() clear the EntityHash and BrainHash, run the game and watch the profiler] [2:17:39][handmade_sim_region.cpp: Make GetHashFromID() clear upon encountering an empty Entry for now] [2:18:42][Run the game successfully and consult the profiler] [2:19:52][Q&A][:speech] [2:20:17][@AsafGartner][The stream is down] [2:22:51][@AsafGartner][Did you fix the bug?] [2:23:45][@AsafGartner][There's a TODO in ZeroSize. Not sure if you noticed it] [2:24:07][handmade_memory.h: Remove the TODO from ZeroSize()] [2:24:23][@uplinkcoder][What was the bug?] [2:25:26][@longboolean][We should have something built into the build script that won't let you compile if the stream has gone down. Is this possible to do in a batch script?] [2:27:17][Update the TODO list] [2:28:01][@mtsmox][Is it an option to never clear to zero for arenas, and maybe only clear when resetting temporary memory?] [2:30:30][ctray.cpp: Investigate why the overlay disappeared] [2:33:17][@AsafGartner][Is there a still a benefit to using the sim region? Since chunks are room-sized, and simulation is room-based, why not use the chunks directly?] [2:35:52][Wrap it up][:speech] [2:36:33][Anticipate HandmadeCon 2016][:speech] [/video]