doc.odin 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /*
  2. The `mem` package implements various allocators and provides utility procedures
  3. for dealing with memory, pointers and slices.
  4. The documentation below describes basic concepts, applicable to the `mem`
  5. package.
  6. ## Pointers, multipointers, and slices
  7. A *pointer* is an abstraction of an *address*, a numberic value representing the
  8. location of an object in memory. That object is said to be *pointed to* by the
  9. pointer. To obtain the address of a pointer, cast it to `uintptr`.
  10. A multipointer is a pointer that points to multiple objects. Unlike a pointer,
  11. a multipointer can be indexed, but does not have a definite length. A slice is
  12. a pointer that points to multiple objects equipped with the length, specifying
  13. the amount of objects a slice points to.
  14. When object's values are read through a pointer, that operation is called a
  15. *load* operation. When memory is read through a pointer, that operation is
  16. called a *store* operation. Both of these operations can be called a *memory
  17. access operation*.
  18. ## Allocators
  19. In C and C++ memory models, allocations of objects in memory are typically
  20. treated individually with a generic allocator (The `malloc` procedure). Which in
  21. some scenarios can lead to poor cache utilization, slowdowns on individual
  22. objects' memory management and growing complexity of the code needing to keep
  23. track of the pointers and their lifetimes.
  24. Using different kinds of *allocators* for different purposes can solve these
  25. problems. The allocators are typically optimized for specific use-cases and
  26. can potentially simplify the memory management code.
  27. For example, in the context of making a game, having an Arena allocator could
  28. simplify allocations of any temporary memory, because the programmer doesn't
  29. have to keep track of which objects need to be freed every time they are
  30. allocated, because at the end of every frame the whole allocator is reset to
  31. its initial state and all objects are freed at once.
  32. The allocators have different kinds of restrictions on object lifetimes, sizes,
  33. alignment and can be a significant gain, if used properly. Odin supports
  34. allocators on a language level.
  35. Operations such as `new`, `free` and `delete` by default will use
  36. `context.allocator`, which can be overridden by the user. When an override
  37. happens all called procedures will inherit the new context and use the same
  38. allocator.
  39. We will define one concept to simplify the description of some allocator-related
  40. procedures, which is ownership. If the memory was allocated via a specific
  41. allocator, that allocator is said to be the *owner* of that memory region. To
  42. note, unlike Rust, in Odin the memory ownership model is not strict.
  43. ## Alignment
  44. An address is said to be *aligned to `N` bytes*, if the addresses's numeric
  45. value is divisible by `N`. The number `N` in this case can be referred to as
  46. the *alignment boundary*. Typically an alignment is a power of two integer
  47. value.
  48. A *natural alignment* of an object is typically equal to its size. For example
  49. a 16 bit integer has a natural alignment of 2 bytes. When an object is not
  50. located on its natural alignment boundary, accesses to that object are
  51. considered *unaligned*.
  52. Some machines issue a hardware **exception**, or experience **slowdowns** when a
  53. memory access operation occurs from an unaligned address. Examples of such
  54. operations are:
  55. - SIMD instructions on x86. These instructions require all memory accesses to be
  56. on an address that is aligned to 16 bytes.
  57. - On ARM unaligned loads have an extra cycle penalty.
  58. As such, many operations that allocate memory in this package allow to
  59. explicitly specify the alignment of allocated pointers/slices. The default
  60. alignment for all operations is specified in a constant `mem.DEFAULT_ALIGNMENT`.
  61. ## Zero by default
  62. Whenever new memory is allocated, via an allocator, or on the stack, by default
  63. Odin will zero-initialize that memory, even if it wasn't explicitly
  64. initialized. This allows for some convenience in certain scenarios and ease of
  65. debugging, which will not be described in detail here.
  66. However zero-initialization can be a cause of slowdowns, when allocating large
  67. buffers. For this reason, allocators have `*_non_zeroed` modes of allocation
  68. that allow the user to request for uninitialized memory and will avoid a
  69. relatively expensive zero-filling of the buffer.
  70. ## Naming conventions
  71. The word `size` is used to denote the **size in bytes**. The word `length` is
  72. used to denote the count of objects.
  73. The allocation procedures use the following conventions:
  74. - If the name contains `alloc_bytes` or `resize_bytes`, then the procedure takes
  75. in slice parameters and returns slices.
  76. - If the procedure name contains `alloc` or `resize`, then the procedure takes
  77. in a raw pointer and returns raw pointers.
  78. - If the procedure name contains `free_bytes`, then the procedure takes in a
  79. slice.
  80. - If the procedure name contains `free`, then the procedure takes in a pointer.
  81. Higher-level allocation procedures follow the following naming scheme:
  82. - `new`: Allocates a single object
  83. - `free`: Free a single object (opposite of `new`)
  84. - `make`: Allocate a group of objects
  85. - `delete`: Free a group of objects (opposite of `make`)
  86. */
  87. package mem