Game Engines 2021W Lecture 5

From Soma-notes

Notes

Lecture 5
---------
 - project proposal, see class wiki

How will technical scope be graded?
 - clean design
 - doable implementation in time given
 - testing your understanding of Godot
 - so, a mix of ambition and reason

Game engines - memory management

 - does Godot use malloc/standard new?  NO
 - why not?
 - memory allocation is inherently expensive
 - how memory is managed has significant performance implications
   - so, makes sense to make some effort to do it "right" for any
     performance-oriented application

the memory hierarchy
 - systems have a mix of small, fast, low latency memory and slow,
   high latency memory

registers
TLB <- virtual memory
on-CPU cache (L1, L2)
external CPU cache (L3)
main memory
SSD
(Spinning) Hard Drives
Tape

Every memory access starts with a virtual address but must end with a physical address
 - TLB is a cache of virtual->physical mappings at the page level
   (every 4K of memory)
 - OS's manage page tables which define the full virtual->physical mapping,
   most modern chips have hardware walk the page tables to get TLB entries

Part of why memory management is expensive is it ultimately requires
manipulating TLBs and page tables
 - so first rule of memory management in userspace is to ask the kernel
   to do it as infrequently as possible
 - reduce, reuse, recycle!

Classic malloc isn't shy about going to the OS for memory
 - yes, allocates in chunks
 - but, not so good at recycling
   - because of memory fragmentation

Memory fragmentation means memory is used less efficiently
 - you get "holes" in memory that can't be properly used

Example:
 - Let's say you do a bunch of 1K allocations, that are randomly allocated then freed
 - after doing this for a while, you'll have memory with randomly placed 1K holes, the allocations that you later de-allocated
 - then, you need 4K, or 8K, or...1MB
 - you can have that amount available, but in scattered 1K chunks
   - so, you have to go ask the OS for more memory :-(
   - or, you move things around to compact memory (this is basically garbage
     collection)

 - the best strategies to avoid memory fragmentation make assumptions about
   patterns of allocation
    - thus, if you know how your application allocates memory, then
      you can probably develop a way to allocate memory that minimizes
      fragmentation

Cache-friendly allocation
 - more to do with how you organize data structures wrt access patterns
 - should organize them so your "working set" at any time fits into the caches

To get best performance, you want small data structures
 - so data structures can all be packed into cache
but small data structures means more allocations/deallocations
 - which hurt performance

Core data type in Godot is Variant
 - can be used to represent almost anything
 - note, it allows for standard type checking to be completely
   bypassed (but Godot has its own mechanisms)

By having a small data structure that is uniform in size but can represent
all kinds of data, it makes the memory allocation problem easier
 - just managing uniform-sized variant's!
 - different kinds of data structures will get their own pools in general

Note, not the most efficient in terms of space
 - some Variant types are smaller than 20 bytes
 - but still can improve performance

Always trade-offs between time and space

Variant is interesting for reasons beyond allocator efficiency
 - why implement your own type system?!

Also see Godot's documentation on Variant.