allocator.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // This allocator does not have any header and can be disabled by not linking it into the application
  2. // or by defining DISABLE_ALLOCATOR (usually with a -DDISABLE_ALLOCATOR compiler flag).
  3. #ifndef DISABLE_ALLOCATOR
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <mutex>
  8. static std::mutex allocationLock;
  9. // Allocation head stored in the beginning of each allocation
  10. // The caller's content begins alignedHeadSize bytes after the head
  11. struct AllocationHead {
  12. AllocationHead* nextUnused = nullptr;
  13. uintptr_t contentSize = 0;
  14. };
  15. // A fixed byte offset to make sure that structures are still aligned in memory
  16. // Increase if values larger than this are stored in data structures using new and delete operations
  17. static const uintptr_t alignedHeadSize = 16;
  18. static_assert(sizeof(AllocationHead) <= alignedHeadSize, "Increase alignedHeadSize to the next power of two.\n");
  19. static AllocationHead* createAllocation(size_t contentSize) {
  20. //printf("createAllocation head(%li) + %li bytes\n", alignedHeadSize, contentSize);
  21. // calloc is faster than malloc for small allocations by not having to fetch old data to the cache
  22. AllocationHead* allocation = (AllocationHead*)calloc(alignedHeadSize + contentSize, 1);
  23. allocation->nextUnused = nullptr;
  24. allocation->contentSize = contentSize;
  25. return allocation;
  26. }
  27. static void* getContent(AllocationHead* head) {
  28. return (void*)(((uintptr_t)head) + alignedHeadSize);
  29. }
  30. static AllocationHead* getHead(void* content) {
  31. return (AllocationHead*)(((uintptr_t)content) - alignedHeadSize);
  32. }
  33. // Garbage pile
  34. struct GarbagePile {
  35. AllocationHead* pileHead; // Linked list using nextUnused in AllocationHead
  36. const size_t fixedBufferSize;
  37. ~GarbagePile() {
  38. AllocationHead* current = this->pileHead;
  39. while (current != nullptr) {
  40. // Free and go to the next allocation
  41. AllocationHead* next = current->nextUnused;
  42. free(current);
  43. current = next;
  44. }
  45. }
  46. AllocationHead* getAllocation() {
  47. //printf("getAllocation head(%li) + %li bytes\n", alignedHeadSize, this->fixedBufferSize);
  48. if (pileHead != nullptr) {
  49. // Pop the first unused allocation
  50. AllocationHead* result = this->pileHead;
  51. this->pileHead = this->pileHead->nextUnused;
  52. result->nextUnused = nullptr;
  53. result->contentSize = this->fixedBufferSize;
  54. return result;
  55. } else {
  56. // Create a new allocation
  57. return createAllocation(this->fixedBufferSize);
  58. }
  59. }
  60. void recycleAllocation(AllocationHead* unused) {
  61. // Clear old data to make debugging easier
  62. memset(getContent(unused), 0, this->fixedBufferSize);
  63. // Push new allocation to the pile
  64. unused->nextUnused = this->pileHead;
  65. this->pileHead = unused;
  66. }
  67. };
  68. static GarbagePile garbagePiles[8] = {
  69. {nullptr, 16},
  70. {nullptr, 32},
  71. {nullptr, 64},
  72. {nullptr, 128},
  73. {nullptr, 256},
  74. {nullptr, 512},
  75. {nullptr, 1024},
  76. {nullptr, 2048}
  77. };
  78. static int getBufferIndex(size_t contentSize) {
  79. if (contentSize <= 16) {
  80. return 0;
  81. } else if (contentSize <= 32) {
  82. return 1;
  83. } else if (contentSize <= 64) {
  84. return 2;
  85. } else if (contentSize <= 128) {
  86. return 3;
  87. } else if (contentSize <= 256) {
  88. return 4;
  89. } else if (contentSize <= 512) {
  90. return 5;
  91. } else if (contentSize <= 1024) {
  92. return 6;
  93. } else if (contentSize <= 2048) {
  94. return 7;
  95. } else {
  96. return -1;
  97. }
  98. }
  99. void* operator new(size_t contentSize) {
  100. allocationLock.lock();
  101. int bufferIndex = getBufferIndex(contentSize);
  102. AllocationHead* head;
  103. if (bufferIndex == -1) {
  104. head = createAllocation(contentSize);
  105. //printf("Allocated %li bytes without a size group\n", contentSize);
  106. } else {
  107. //printf("Requested at least %li bytes from size group %i\n", contentSize, bufferIndex);
  108. head = garbagePiles[bufferIndex].getAllocation();
  109. }
  110. allocationLock.unlock();
  111. return getContent(head);
  112. }
  113. void operator delete(void* content) {
  114. allocationLock.lock();
  115. AllocationHead* head = getHead(content);
  116. int bufferIndex = getBufferIndex(head->contentSize);
  117. if (bufferIndex == -1) {
  118. free(head);
  119. //printf("Freed memory of size %li without a size group\n", head->contentSize);
  120. } else {
  121. garbagePiles[bufferIndex].recycleAllocation(head);
  122. //printf("Freed memory of size %li from size group %i\n", head->contentSize, bufferIndex);
  123. }
  124. allocationLock.unlock();
  125. }
  126. #endif