heap.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #include "pocketpy/interpreter/heap.h"
  2. #include "pocketpy/config.h"
  3. #include "pocketpy/interpreter/objectpool.h"
  4. #include "pocketpy/objects/base.h"
  5. #include "pocketpy/common/sstream.h"
  6. #include "pocketpy/pocketpy.h"
  7. #include <assert.h>
  8. void ManagedHeap__ctor(ManagedHeap* self) {
  9. MultiPool__ctor(&self->small_objects);
  10. c11_vector__ctor(&self->large_objects, sizeof(PyObject*));
  11. c11_vector__ctor(&self->gc_roots, sizeof(PyObject*));
  12. for(int i = 0; i < c11__count_array(self->freed_ma); i++) {
  13. self->freed_ma[i] = PK_GC_MIN_THRESHOLD;
  14. }
  15. self->gc_threshold = PK_GC_MIN_THRESHOLD;
  16. self->gc_counter = 0;
  17. self->gc_enabled = true;
  18. self->debug_callback = *py_None();
  19. }
  20. void ManagedHeap__dtor(ManagedHeap* self) {
  21. // small_objects
  22. MultiPool__dtor(&self->small_objects);
  23. // large_objects
  24. for(int i = 0; i < self->large_objects.length; i++) {
  25. PyObject* obj = c11__getitem(PyObject*, &self->large_objects, i);
  26. PyObject__dtor(obj);
  27. PK_FREE(obj);
  28. }
  29. c11_vector__dtor(&self->large_objects);
  30. c11_vector__dtor(&self->gc_roots);
  31. }
  32. static void ManagedHeap__fire_debug_callback(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) {
  33. assert(out_info != NULL);
  34. c11_sbuf buf;
  35. c11_sbuf__ctor(&buf);
  36. const int64_t NANOS_PER_SEC = 1000000000;
  37. const char* DIVIDER = "------------------------------------------------------------\n";
  38. double start = out_info->start_ns / 1e9;
  39. int64_t mark_ms = (out_info->mark_end_ns - out_info->start_ns) / NANOS_PER_SEC;
  40. int64_t swpet_ms = (out_info->swpet_end_ns - out_info->mark_end_ns) / NANOS_PER_SEC;
  41. c11_sbuf__write_cstr(&buf, DIVIDER);
  42. pk_sprintf(&buf, "start: %f\n", (double)start / 1000);
  43. pk_sprintf(&buf, "mark_ms: %i\n", (py_i64)mark_ms);
  44. pk_sprintf(&buf, "swpet_ms: %i\n", (py_i64)swpet_ms);
  45. pk_sprintf(&buf, "total_ms: %i\n", (py_i64)(mark_ms + swpet_ms));
  46. c11_sbuf__write_cstr(&buf, DIVIDER);
  47. pk_sprintf(&buf, "types_length: %d\n", out_info->types_length);
  48. pk_sprintf(&buf, "small_freed: %d\n", out_info->small_freed);
  49. pk_sprintf(&buf, "large_freed: %d\n", out_info->large_freed);
  50. c11_sbuf__write_cstr(&buf, DIVIDER);
  51. if(out_info->small_freed != 0 || out_info->large_freed != 0) {
  52. char line_buf[256];
  53. for(int i = 0; i < out_info->types_length; i++) {
  54. const char* type_name = py_tpname(i);
  55. int s_freed = out_info->small_types[i];
  56. int l_freed = out_info->large_types[i];
  57. if(s_freed == 0 && l_freed == 0) continue;
  58. snprintf(line_buf,
  59. sizeof(line_buf),
  60. "[%-24s] small: %6d large: %6d\n",
  61. type_name,
  62. s_freed,
  63. l_freed);
  64. c11_sbuf__write_cstr(&buf, line_buf);
  65. }
  66. c11_sbuf__write_cstr(&buf, DIVIDER);
  67. }
  68. pk_sprintf(&buf, "auto_thres.before: %d\n", out_info->auto_thres.before);
  69. pk_sprintf(&buf, "auto_thres.after: %d\n", out_info->auto_thres.after);
  70. pk_sprintf(&buf, "auto_thres.upper: %d\n", out_info->auto_thres.upper);
  71. pk_sprintf(&buf, "auto_thres.lower: %d\n", out_info->auto_thres.lower);
  72. pk_sprintf(&buf, "auto_thres.avg_freed: %d\n", out_info->auto_thres.avg_freed);
  73. pk_sprintf(&buf, "auto_thres.free_ratio: %f\n", out_info->auto_thres.free_ratio);
  74. c11_sbuf__write_cstr(&buf, DIVIDER);
  75. py_push(&self->debug_callback);
  76. py_pushnil();
  77. py_StackRef arg = py_pushtmp();
  78. c11_sbuf__py_submit(&buf, arg);
  79. bool ok = py_vectorcall(1, 0);
  80. if(!ok) {
  81. char* msg = py_formatexc();
  82. c11__abort("gc_debug_callback error!!\n%s", msg);
  83. }
  84. }
  85. void ManagedHeap__collect_hint(ManagedHeap* self) {
  86. if(self->gc_counter < self->gc_threshold) return;
  87. self->gc_counter = 0;
  88. ManagedHeapSwpetInfo* out_info = NULL;
  89. if(!py_isnone(&self->debug_callback)) out_info = ManagedHeapSwpetInfo__new();
  90. ManagedHeap__mark(self);
  91. if(out_info) out_info->mark_end_ns = time_ns();
  92. int freed = ManagedHeap__sweep(self, out_info);
  93. if(out_info) out_info->swpet_end_ns = time_ns();
  94. // adjust `gc_threshold` based on `freed_ma`
  95. self->freed_ma[0] = self->freed_ma[1];
  96. self->freed_ma[1] = self->freed_ma[2];
  97. self->freed_ma[2] = freed;
  98. int avg_freed = (self->freed_ma[0] + self->freed_ma[1] + self->freed_ma[2]) / 3;
  99. const int upper = PK_GC_MIN_THRESHOLD * 16;
  100. const int lower = PK_GC_MIN_THRESHOLD / 2;
  101. float free_ratio = (float)avg_freed / self->gc_threshold;
  102. int new_threshold = self->gc_threshold * (1.5f / free_ratio);
  103. if(out_info) {
  104. out_info->auto_thres.before = self->gc_threshold;
  105. out_info->auto_thres.after = new_threshold;
  106. out_info->auto_thres.upper = upper;
  107. out_info->auto_thres.lower = lower;
  108. out_info->auto_thres.avg_freed = avg_freed;
  109. out_info->auto_thres.free_ratio = free_ratio;
  110. }
  111. self->gc_threshold = c11__min(c11__max(new_threshold, lower), upper);
  112. if(!py_isnone(&self->debug_callback)) {
  113. ManagedHeap__fire_debug_callback(self, out_info);
  114. ManagedHeapSwpetInfo__delete(out_info);
  115. }
  116. }
  117. int ManagedHeap__collect(ManagedHeap* self) {
  118. self->gc_counter = 0;
  119. ManagedHeapSwpetInfo* out_info = NULL;
  120. if(!py_isnone(&self->debug_callback)) out_info = ManagedHeapSwpetInfo__new();
  121. ManagedHeap__mark(self);
  122. if(out_info) out_info->mark_end_ns = time_ns();
  123. int freed = ManagedHeap__sweep(self, out_info);
  124. if(out_info) out_info->swpet_end_ns = time_ns();
  125. if(out_info) {
  126. out_info->auto_thres.before = self->gc_threshold;
  127. out_info->auto_thres.after = self->gc_threshold;
  128. }
  129. if(!py_isnone(&self->debug_callback)) {
  130. ManagedHeap__fire_debug_callback(self, out_info);
  131. ManagedHeapSwpetInfo__delete(out_info);
  132. }
  133. return freed;
  134. }
  135. int ManagedHeap__sweep(ManagedHeap* self, ManagedHeapSwpetInfo* out_info) {
  136. // small_objects
  137. int small_freed =
  138. MultiPool__sweep_dealloc(&self->small_objects, out_info ? out_info->small_types : NULL);
  139. // large_objects
  140. int large_living_count = 0;
  141. for(int i = 0; i < self->large_objects.length; i++) {
  142. PyObject* obj = c11__getitem(PyObject*, &self->large_objects, i);
  143. if(obj->gc_marked) {
  144. obj->gc_marked = false;
  145. c11__setitem(PyObject*, &self->large_objects, large_living_count, obj);
  146. large_living_count++;
  147. } else {
  148. if(out_info) out_info->large_types[obj->type]++;
  149. PyObject__dtor(obj);
  150. PK_FREE(obj);
  151. }
  152. }
  153. // shrink `self->large_objects`
  154. int large_freed = self->large_objects.length - large_living_count;
  155. self->large_objects.length = large_living_count;
  156. if(out_info) {
  157. out_info->small_freed = small_freed;
  158. out_info->large_freed = large_freed;
  159. }
  160. return small_freed + large_freed;
  161. }
  162. PyObject* ManagedHeap__gcnew(ManagedHeap* self, py_Type type, int slots, int udsize) {
  163. assert(slots >= 0 || slots == -1);
  164. // header + slots + udsize
  165. int size = sizeof(PyObject) + PK_OBJ_SLOTS_SIZE(slots) + udsize;
  166. PyObject* obj = MultiPool__alloc(&self->small_objects, size);
  167. if(obj == NULL) {
  168. obj = PK_MALLOC(size);
  169. c11_vector__push(PyObject*, &self->large_objects, obj);
  170. }
  171. obj->type = type;
  172. obj->gc_marked = false;
  173. obj->slots = slots;
  174. // initialize slots or dict
  175. if(slots >= 0) {
  176. memset(obj->flex, 0, slots * sizeof(py_TValue));
  177. } else {
  178. float load_factor = (type == tp_type || type == tp_module) ? PK_TYPE_ATTR_LOAD_FACTOR
  179. : PK_INST_ATTR_LOAD_FACTOR;
  180. NameDict__ctor((void*)obj->flex, load_factor);
  181. }
  182. self->gc_counter++;
  183. return obj;
  184. }