july.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "july.h"
  3. #define JULYL_MIN_ENTRIES 10
  4. struct JulyL_item {
  5. Word_t index;
  6. void *value;
  7. };
  8. struct JulyL {
  9. size_t entries;
  10. size_t used;
  11. // statistics
  12. size_t bytes;
  13. size_t bytes_moved;
  14. size_t reallocs;
  15. struct {
  16. struct JulyL *prev;
  17. struct JulyL *next;
  18. } cache;
  19. struct JulyL_item array[];
  20. };
  21. // ----------------------------------------------------------------------------
  22. // JulyL cache
  23. static struct {
  24. struct {
  25. SPINLOCK spinlock;
  26. struct JulyL *available_items;
  27. size_t available;
  28. } protected;
  29. struct {
  30. size_t bytes;
  31. size_t allocated;
  32. size_t bytes_moved;
  33. size_t reallocs;
  34. } atomics;
  35. } julyl_globals = {
  36. .protected = {
  37. .spinlock = NETDATA_SPINLOCK_INITIALIZER,
  38. .available_items = NULL,
  39. .available = 0,
  40. },
  41. .atomics = {
  42. .bytes = 0,
  43. .allocated = 0,
  44. .bytes_moved = 0,
  45. .reallocs = 0,
  46. },
  47. };
  48. void julyl_cleanup1(void) {
  49. struct JulyL *item = NULL;
  50. if(!netdata_spinlock_trylock(&julyl_globals.protected.spinlock))
  51. return;
  52. if(julyl_globals.protected.available_items && julyl_globals.protected.available > 10) {
  53. item = julyl_globals.protected.available_items;
  54. DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(julyl_globals.protected.available_items, item, cache.prev, cache.next);
  55. julyl_globals.protected.available--;
  56. }
  57. netdata_spinlock_unlock(&julyl_globals.protected.spinlock);
  58. if(item) {
  59. size_t bytes = item->bytes;
  60. freez(item);
  61. __atomic_sub_fetch(&julyl_globals.atomics.bytes, bytes, __ATOMIC_RELAXED);
  62. __atomic_sub_fetch(&julyl_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
  63. }
  64. }
  65. struct JulyL *julyl_get(void) {
  66. struct JulyL *j;
  67. netdata_spinlock_lock(&julyl_globals.protected.spinlock);
  68. j = julyl_globals.protected.available_items;
  69. if(likely(j)) {
  70. DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(julyl_globals.protected.available_items, j, cache.prev, cache.next);
  71. julyl_globals.protected.available--;
  72. }
  73. netdata_spinlock_unlock(&julyl_globals.protected.spinlock);
  74. if(unlikely(!j)) {
  75. size_t bytes = sizeof(struct JulyL) + JULYL_MIN_ENTRIES * sizeof(struct JulyL_item);
  76. j = mallocz(bytes);
  77. j->bytes = bytes;
  78. j->entries = JULYL_MIN_ENTRIES;
  79. __atomic_add_fetch(&julyl_globals.atomics.bytes, bytes, __ATOMIC_RELAXED);
  80. __atomic_add_fetch(&julyl_globals.atomics.allocated, 1, __ATOMIC_RELAXED);
  81. }
  82. j->used = 0;
  83. j->bytes_moved = 0;
  84. j->reallocs = 0;
  85. j->cache.next = j->cache.prev = NULL;
  86. return j;
  87. }
  88. static void julyl_release(struct JulyL *j) {
  89. if(unlikely(!j)) return;
  90. __atomic_add_fetch(&julyl_globals.atomics.bytes_moved, j->bytes_moved, __ATOMIC_RELAXED);
  91. __atomic_add_fetch(&julyl_globals.atomics.reallocs, j->reallocs, __ATOMIC_RELAXED);
  92. netdata_spinlock_lock(&julyl_globals.protected.spinlock);
  93. DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(julyl_globals.protected.available_items, j, cache.prev, cache.next);
  94. julyl_globals.protected.available++;
  95. netdata_spinlock_unlock(&julyl_globals.protected.spinlock);
  96. }
  97. size_t julyl_cache_size(void) {
  98. return __atomic_load_n(&julyl_globals.atomics.bytes, __ATOMIC_RELAXED);
  99. }
  100. size_t julyl_bytes_moved(void) {
  101. return __atomic_load_n(&julyl_globals.atomics.bytes_moved, __ATOMIC_RELAXED);
  102. }
  103. // ----------------------------------------------------------------------------
  104. // JulyL
  105. size_t JulyLGet_binary_search_position_of_index(const struct JulyL *July, Word_t Index) {
  106. // return the position of the first item >= Index
  107. size_t left = 0;
  108. size_t right = July->used;
  109. while(left < right) {
  110. size_t middle = (left + right) >> 1;
  111. if(July->array[middle].index > Index)
  112. right = middle;
  113. else
  114. left = middle + 1;
  115. }
  116. internal_fatal(left > July->used, "JULY: invalid position returned");
  117. if(left > 0 && July->array[left - 1].index == Index)
  118. return left - 1;
  119. internal_fatal( (left < July->used && July->array[left].index < Index) ||
  120. (left > 0 && July->array[left - 1].index >= Index)
  121. , "JULY: wrong item returned");
  122. return left;
  123. }
  124. PPvoid_t JulyLGet(Pcvoid_t PArray, Word_t Index, PJError_t PJError __maybe_unused) {
  125. const struct JulyL *July = PArray;
  126. if(!July)
  127. return NULL;
  128. size_t pos = JulyLGet_binary_search_position_of_index(July, Index);
  129. if(unlikely(pos >= July->used || July->array[pos].index != Index))
  130. return NULL;
  131. return (PPvoid_t)&July->array[pos].value;
  132. }
  133. PPvoid_t JulyLIns(PPvoid_t PPArray, Word_t Index, PJError_t PJError __maybe_unused) {
  134. struct JulyL *July = *PPArray;
  135. if(unlikely(!July)) {
  136. July = julyl_get();
  137. July->used = 0;
  138. *PPArray = July;
  139. }
  140. size_t pos = JulyLGet_binary_search_position_of_index(July, Index);
  141. if((pos == July->used || July->array[pos].index != Index)) {
  142. // we have to add this entry
  143. if (unlikely(July->used == July->entries)) {
  144. // we have to expand the array
  145. size_t bytes = sizeof(struct JulyL) + July->entries * 2 * sizeof(struct JulyL_item);
  146. __atomic_add_fetch(&julyl_globals.atomics.bytes, bytes - July->bytes, __ATOMIC_RELAXED);
  147. July = reallocz(July, bytes);
  148. July->bytes = bytes;
  149. July->entries *= 2;
  150. July->reallocs++;
  151. *PPArray = July;
  152. }
  153. if (unlikely(pos != July->used)) {
  154. // we have to shift some members to make room
  155. size_t size = (July->used - pos) * sizeof(struct JulyL_item);
  156. memmove(&July->array[pos + 1], &July->array[pos], size);
  157. July->bytes_moved += size;
  158. }
  159. July->used++;
  160. July->array[pos].value = NULL;
  161. July->array[pos].index = Index;
  162. }
  163. return &July->array[pos].value;
  164. }
  165. PPvoid_t JulyLFirst(Pcvoid_t PArray, Word_t *Index, PJError_t PJError __maybe_unused) {
  166. const struct JulyL *July = PArray;
  167. if(!July)
  168. return NULL;
  169. size_t pos = JulyLGet_binary_search_position_of_index(July, *Index);
  170. // pos is >= Index
  171. if(unlikely(pos == July->used))
  172. return NULL;
  173. *Index = July->array[pos].index;
  174. return (PPvoid_t)&July->array[pos].value;
  175. }
  176. PPvoid_t JulyLNext(Pcvoid_t PArray, Word_t *Index, PJError_t PJError __maybe_unused) {
  177. const struct JulyL *July = PArray;
  178. if(!July)
  179. return NULL;
  180. size_t pos = JulyLGet_binary_search_position_of_index(July, *Index);
  181. // pos is >= Index
  182. if(unlikely(pos == July->used))
  183. return NULL;
  184. if(July->array[pos].index == *Index) {
  185. pos++;
  186. if(unlikely(pos == July->used))
  187. return NULL;
  188. }
  189. *Index = July->array[pos].index;
  190. return (PPvoid_t)&July->array[pos].value;
  191. }
  192. PPvoid_t JulyLLast(Pcvoid_t PArray, Word_t *Index, PJError_t PJError __maybe_unused) {
  193. const struct JulyL *July = PArray;
  194. if(!July)
  195. return NULL;
  196. size_t pos = JulyLGet_binary_search_position_of_index(July, *Index);
  197. // pos is >= Index
  198. if(pos > 0 && (pos == July->used || July->array[pos].index > *Index))
  199. pos--;
  200. if(unlikely(pos == 0 && July->array[0].index > *Index))
  201. return NULL;
  202. *Index = July->array[pos].index;
  203. return (PPvoid_t)&July->array[pos].value;
  204. }
  205. PPvoid_t JulyLPrev(Pcvoid_t PArray, Word_t *Index, PJError_t PJError __maybe_unused) {
  206. const struct JulyL *July = PArray;
  207. if(!July)
  208. return NULL;
  209. size_t pos = JulyLGet_binary_search_position_of_index(July, *Index);
  210. // pos is >= Index
  211. if(unlikely(pos == 0 || July->used == 0))
  212. return NULL;
  213. // get the previous one
  214. pos--;
  215. *Index = July->array[pos].index;
  216. return (PPvoid_t)&July->array[pos].value;
  217. }
  218. Word_t JulyLFreeArray(PPvoid_t PPArray, PJError_t PJError __maybe_unused) {
  219. struct JulyL *July = *PPArray;
  220. if(unlikely(!July))
  221. return 0;
  222. size_t bytes = July->bytes;
  223. julyl_release(July);
  224. *PPArray = NULL;
  225. return bytes;
  226. }
  227. // ----------------------------------------------------------------------------
  228. // unittest
  229. #define item_index(i) (((i) * 2) + 100)
  230. int julytest(void) {
  231. Word_t entries = 10000;
  232. Pvoid_t array = NULL;
  233. // test additions
  234. for(Word_t i = 0; i < entries ;i++) {
  235. Pvoid_t *PValue = JulyLIns(&array, item_index(i), PJE0);
  236. if(!PValue)
  237. fatal("JULY: cannot insert item %lu", item_index(i));
  238. *PValue = (void *)(item_index(i));
  239. }
  240. // test successful finds
  241. for(Word_t i = 0; i < entries ;i++) {
  242. Pvoid_t *PValue = JulyLGet(array, item_index(i), PJE0);
  243. if(!PValue)
  244. fatal("JULY: cannot find item %lu", item_index(i));
  245. if(*PValue != (void *)(item_index(i)))
  246. fatal("JULY: item %lu has the value %lu", item_index(i), (unsigned long)(*PValue));
  247. }
  248. // test finding the first item
  249. for(Word_t i = 0; i < entries ;i++) {
  250. Word_t index = item_index(i);
  251. Pvoid_t *PValue = JulyLFirst(array, &index, PJE0);
  252. if(!PValue)
  253. fatal("JULY: cannot find first item %lu", item_index(i));
  254. if(*PValue != (void *)(item_index(i)))
  255. fatal("JULY: item %lu has the value %lu", item_index(i), (unsigned long)(*PValue));
  256. if(index != item_index(i))
  257. fatal("JULY: item %lu has index %lu", item_index(i), index);
  258. }
  259. // test finding the next item
  260. for(Word_t i = 0; i < entries - 1 ;i++) {
  261. Word_t index = item_index(i);
  262. Pvoid_t *PValue = JulyLNext(array, &index, PJE0);
  263. if(!PValue)
  264. fatal("JULY: cannot find next item %lu", item_index(i));
  265. if(*PValue != (void *)(item_index(i + 1)))
  266. fatal("JULY: item %lu next has the value %lu", item_index(i), (unsigned long)(*PValue));
  267. if(index != item_index(i + 1))
  268. fatal("JULY: item %lu next has index %lu", item_index(i), index);
  269. }
  270. // test finding the last item
  271. for(Word_t i = 0; i < entries ;i++) {
  272. Word_t index = item_index(i);
  273. Pvoid_t *PValue = JulyLLast(array, &index, PJE0);
  274. if(!PValue)
  275. fatal("JULY: cannot find last item %lu", item_index(i));
  276. if(*PValue != (void *)(item_index(i)))
  277. fatal("JULY: item %lu has the value %lu", item_index(i), (unsigned long)(*PValue));
  278. if(index != item_index(i))
  279. fatal("JULY: item %lu has index %lu", item_index(i), index);
  280. }
  281. // test finding the prev item
  282. for(Word_t i = 1; i < entries ;i++) {
  283. Word_t index = item_index(i);
  284. Pvoid_t *PValue = JulyLPrev(array, &index, PJE0);
  285. if(!PValue)
  286. fatal("JULY: cannot find prev item %lu", item_index(i));
  287. if(*PValue != (void *)(item_index(i - 1)))
  288. fatal("JULY: item %lu prev has the value %lu", item_index(i), (unsigned long)(*PValue));
  289. if(index != item_index(i - 1))
  290. fatal("JULY: item %lu prev has index %lu", item_index(i), index);
  291. }
  292. // test full traversal forward
  293. {
  294. Word_t i = 0;
  295. Word_t index = 0;
  296. bool first = true;
  297. Pvoid_t *PValue;
  298. while((PValue = JulyLFirstThenNext(array, &index, &first))) {
  299. if(*PValue != (void *)(item_index(i)))
  300. fatal("JULY: item %lu traversal has the value %lu", item_index(i), (unsigned long)(*PValue));
  301. if(index != item_index(i))
  302. fatal("JULY: item %lu traversal has index %lu", item_index(i), index);
  303. i++;
  304. }
  305. if(i != entries)
  306. fatal("JULY: expected to forward traverse %lu entries, but traversed %lu", entries, i);
  307. }
  308. // test full traversal backward
  309. {
  310. Word_t i = 0;
  311. Word_t index = (Word_t)(-1);
  312. bool first = true;
  313. Pvoid_t *PValue;
  314. while((PValue = JulyLLastThenPrev(array, &index, &first))) {
  315. if(*PValue != (void *)(item_index(entries - i - 1)))
  316. fatal("JULY: item %lu traversal has the value %lu", item_index(i), (unsigned long)(*PValue));
  317. if(index != item_index(entries - i - 1))
  318. fatal("JULY: item %lu traversal has index %lu", item_index(i), index);
  319. i++;
  320. }
  321. if(i != entries)
  322. fatal("JULY: expected to back traverse %lu entries, but traversed %lu", entries, i);
  323. }
  324. // test finding non-existing first item
  325. for(Word_t i = 0; i < entries ;i++) {
  326. Word_t index = item_index(i) - 1;
  327. Pvoid_t *PValue = JulyLFirst(array, &index, PJE0);
  328. if(!PValue)
  329. fatal("JULY: cannot find first item %lu", item_index(i) - 1);
  330. if(*PValue != (void *)(item_index(i)))
  331. fatal("JULY: item %lu has the value %lu", item_index(i), (unsigned long)(*PValue));
  332. if(index != item_index(i))
  333. fatal("JULY: item %lu has index %lu", item_index(i), index);
  334. }
  335. // test finding non-existing last item
  336. for(Word_t i = 0; i < entries ;i++) {
  337. Word_t index = item_index(i) + 1;
  338. Pvoid_t *PValue = JulyLLast(array, &index, PJE0);
  339. if(!PValue)
  340. fatal("JULY: cannot find last item %lu", item_index(i) + 1);
  341. if(*PValue != (void *)(item_index(i)))
  342. fatal("JULY: item %lu has the value %lu", item_index(i), (unsigned long)(*PValue));
  343. if(index != item_index(i))
  344. fatal("JULY: item %lu has index %lu", item_index(i), index);
  345. }
  346. JulyLFreeArray(&array, PJE0);
  347. return 0;
  348. }