allocator.cpp 4.2 KB

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