@cryptotaxi247 / netdata-1 / commits / 0232abdfe

ARAL: fast path to quickly allocate elements on a new page (#19376)

* fast path to quickly allocate elements on a new page * partitions on aral incoming lock

Costa Tsaousis committed Jan 12, 2025 at 11:48 UTC 0232abdfe99ee9b1ad44f963babcce2f1ed1fb78
1 file changed +75 -69
src/libnetdata/aral/aral.c
+75 -69
@@ -29,20 +29,23 @@
29 // use anonymous private mmap pages
30 #define ARAL_MMAP_PAGES_ABOVE (32ULL * 1024)
31
32 +#define ARAL_PAGE_INCOMING_PARTITIONS 4 // up to 32 (32-bits bitmap)
33 +
34 typedef struct aral_free {
35 size_t size;
36 struct aral_free *next;
37 } ARAL_FREE;
38
39 typedef struct aral_page {
40 + const char *filename;
41 + uint8_t *data;
42 +
43 bool marked;
44 bool started_marked;
45 bool mapped;
46 uint32_t size; // the allocation size of the page
42 - const char *filename;
43 - uint8_t *data;
44 -
47 uint32_t max_elements; // the number of elements that can fit on this page
48 + uint64_t elements_segmented; // fast path for acquiring new elements in this page
49
50 struct {
51 uint32_t used_elements; // the number of used elements on this page
@@ -61,7 +64,10 @@ typedef struct aral_page {
64 struct {
65 SPINLOCK spinlock;
66 ARAL_FREE *list;
64 - } incoming;
67 + char pad[48];
68 + } incoming[ARAL_PAGE_INCOMING_PARTITIONS];
69 +
70 + uint32_t incoming_partition_bitmap; // atomic
71
72 } ARAL_PAGE;
73
@@ -227,14 +233,14 @@ static inline void aral_page_available_unlock(ARAL *ar, ARAL_PAGE *page) {
233 spinlock_unlock(&page->available.spinlock);
234 }
235
230 -static inline void aral_page_incoming_lock(ARAL *ar, ARAL_PAGE *page) {
236 +static inline void aral_page_incoming_lock(ARAL *ar, ARAL_PAGE *page, size_t partition) {
237 if(likely(!(ar->config.options & ARAL_LOCKLESS)))
232 - spinlock_lock(&page->incoming.spinlock);
238 + spinlock_lock(&page->incoming[partition].spinlock);
239 }
240
235 -static inline void aral_page_incoming_unlock(ARAL *ar, ARAL_PAGE *page) {
241 +static inline void aral_page_incoming_unlock(ARAL *ar, ARAL_PAGE *page, size_t partition) {
242 if(likely(!(ar->config.options & ARAL_LOCKLESS)))
237 - spinlock_unlock(&page->incoming.spinlock);
243 + spinlock_unlock(&page->incoming[partition].spinlock);
244 }
245
246 static inline bool aral_adders_trylock(ARAL *ar, bool marked) {
@@ -566,7 +572,10 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
572 #endif
573
574 spinlock_init(&page->available.spinlock);
569 - spinlock_init(&page->incoming.spinlock);
575 +
576 + for(size_t p = 0; p < ARAL_PAGE_INCOMING_PARTITIONS ;p++)
577 + spinlock_init(&page->incoming[p].spinlock);
578 +
579 page->size = size;
580 page->max_elements = aral_elements_in_page_size(ar, page->size);
581 page->aral_lock.free_elements = page->max_elements;
@@ -582,14 +591,8 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
591 __atomic_add_fetch(&ar->stats->structures.allocations, 1, __ATOMIC_RELAXED);
592 __atomic_add_fetch(&ar->stats->structures.allocated_bytes, structures_size, __ATOMIC_RELAXED);
593
585 - // link the free space to its page
586 - ARAL_FREE *fr = (ARAL_FREE *)page->data;
587 -
588 - fr->size = page->max_elements * ar->config.element_size;
589 - fr->next = NULL;
590 - page->available.list = fr;
591 -
592 - aral_free_validate_internal_check(ar, fr);
594 + // Initialize elements_segmented last with RELEASE
595 + __atomic_store_n(&page->elements_segmented, 0, __ATOMIC_RELEASE);
596
597 return page;
598 }
@@ -693,9 +696,9 @@ static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool mar
696
697 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(*head_ptr_free, page, aral_lock.prev, aral_lock.next);
698
696 -//#ifdef NETDATA_ARAL_INTERNAL_CHECKS
697 -// added = true;
698 -//#endif
699 + //#ifdef NETDATA_ARAL_INTERNAL_CHECKS
700 + // added = true;
701 + //#endif
702
703 aral_adders_lock(ar, marked);
704 ar->ops[idx].adders.allocating_elements -= aral_elements_in_page_size(ar, page_allocation_size);
@@ -721,12 +724,12 @@ static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool mar
724
725 internal_fatal(marked && !page->marked, "ARAL: requested a marked page, but the page found is not marked");
726
724 -//#ifdef NETDATA_ARAL_INTERNAL_CHECKS
725 -// if(added) {
726 -// f2 = check_free_space___aral_lock_needed(ar, page, marked);
727 -// internal_fatal(f2.failed, "hey!");
728 -// }
729 -//#endif
727 + //#ifdef NETDATA_ARAL_INTERNAL_CHECKS
728 + // if(added) {
729 + // f2 = check_free_space___aral_lock_needed(ar, page, marked);
730 + // internal_fatal(f2.failed, "hey!");
731 + // }
732 + //#endif
733
734 internal_fatal(!page || !page->aral_lock.free_elements,
735 "ARAL: '%s' selected page does not have a free slot in it",
@@ -769,49 +772,50 @@ static inline ARAL_PAGE *aral_get_first_page_with_a_free_slot(ARAL *ar, bool mar
772 }
773
774 static void *aral_get_free_slot___no_lock_required(ARAL *ar, ARAL_PAGE *page, bool marked) {
772 - aral_page_available_lock(ar, page);
775 + // Try fast path first
776 + uint64_t slot = __atomic_fetch_add(&page->elements_segmented, 1, __ATOMIC_ACQUIRE);
777 + if (slot < page->max_elements) {
778 + // Fast path - we got a valid slot
779 + uint8_t *data = page->data + (slot * ar->config.element_size);
780
774 - if(!page->available.list) {
775 - aral_page_incoming_lock(ar, page);
776 - page->available.list = page->incoming.list;
777 - page->incoming.list = NULL;
778 - aral_page_incoming_unlock(ar, page);
779 - }
780 -
781 - ARAL_FREE *found_fr;
782 - found_fr = page->available.list;
783 -
784 - internal_fatal(!found_fr,
785 - "ARAL: '%s' incoming free list, cannot be NULL.", ar->config.name);
781 + // Set the page pointer after the element
782 + aral_set_page_pointer_after_element___do_NOT_have_aral_lock(ar, page, data, marked);
783
787 - internal_fatal(found_fr->size < ar->config.element_size,
788 - "ARAL: '%s' free element size %zu, cannot be smaller than %zu",
789 - ar->config.name, page->available.list->size, ar->config.element_size);
790 -
791 - // check if the remaining size (after we use this slot) is not enough for another element
792 - if(unlikely(found_fr->size - ar->config.element_size < ar->config.element_size)) {
793 - // we can use the entire free space entry
794 -
795 - page->available.list = found_fr->next;
784 + aral_element_given(ar, page);
785 + return data;
786 }
797 - else {
798 - // we can split the free space entry
787
800 - uint8_t *data = (uint8_t *)found_fr;
801 - ARAL_FREE *fr = (ARAL_FREE *)&data[ar->config.element_size];
802 -
803 - fr->size = found_fr->size - ar->config.element_size;
804 -
805 - // link the free slot first in the page
806 - fr->next = found_fr->next;
807 - page->available.list = fr;
788 + // Fall back to existing mechanism for reused memory
789 + aral_page_available_lock(ar, page);
790
809 - aral_free_validate_internal_check(ar, fr);
791 + if(!page->available.list) {
792 + uint32_t bitmap = __atomic_load_n(&page->incoming_partition_bitmap, __ATOMIC_RELAXED);
793 + if(!bitmap)
794 + fatal("ARAL: bitmap of incoming free elements cannot be empty at this point");
795 +
796 + size_t partition = __builtin_ffs((int)bitmap) - 1;
797 + // for(partition = 0; partition < ARAL_PAGE_INCOMING_PARTITIONS ; partition++) {
798 + // if (bitmap & (1U << partition))
799 + // break;
800 + // }
801 +
802 + if(partition >= ARAL_PAGE_INCOMING_PARTITIONS)
803 + fatal("ARAL: partition %zu must be smaller than %d", partition, ARAL_PAGE_INCOMING_PARTITIONS);
804 +
805 + aral_page_incoming_lock(ar, page, partition);
806 + page->available.list = page->incoming[partition].list;
807 + page->incoming[partition].list = NULL;
808 + __atomic_fetch_and(&page->incoming_partition_bitmap, ~(1U << partition), __ATOMIC_RELAXED);
809 + aral_page_incoming_unlock(ar, page, partition);
810 }
811
812 + ARAL_FREE *found_fr = page->available.list;
813 + internal_fatal(!found_fr, "ARAL: '%s' incoming free list, cannot be NULL.", ar->config.name);
814 + page->available.list = found_fr->next;
815 +
816 aral_page_available_unlock(ar, page);
817
814 - // put the page pointer after the element
818 + // Set the page pointer after the element
819 aral_set_page_pointer_after_element___do_NOT_have_aral_lock(ar, page, found_fr, marked);
820
821 aral_element_given(ar, page);
@@ -823,10 +827,12 @@ static inline void aral_add_free_slot___no_lock_required(ARAL *ar, ARAL_PAGE *pa
827 ARAL_FREE *fr = (ARAL_FREE *)ptr;
828 fr->size = ar->config.element_size;
829
826 - aral_page_incoming_lock(ar, page);
827 - fr->next = page->incoming.list;
828 - page->incoming.list = fr;
829 - aral_page_incoming_unlock(ar, page);
830 + size_t partition = gettid_cached() % ARAL_PAGE_INCOMING_PARTITIONS;
831 + aral_page_incoming_lock(ar, page, partition);
832 + fr->next = page->incoming[partition].list;
833 + page->incoming[partition].list = fr;
834 + __atomic_fetch_or(&page->incoming_partition_bitmap, 1U << partition, __ATOMIC_RELAXED);
835 + aral_page_incoming_unlock(ar, page, partition);
836 }
837
838 void *aral_callocz_internal(ARAL *ar, bool marked TRACE_ALLOCATIONS_FUNCTION_DEFINITION_PARAMS) {
@@ -935,7 +941,7 @@ void aral_freez_internal(ARAL *ar, void *ptr TRACE_ALLOCATIONS_FUNCTION_DEFINITI
941 (size_t)page->max_elements,
942 (size_t)page->aral_lock.used_elements, (size_t)page->aral_lock.free_elements,
943 (size_t)page->aral_lock.used_elements + (size_t)page->aral_lock.free_elements
938 - );
944 + );
945
946 ARAL_PAGE **head_ptr = page->aral_lock.free_elements ? aral_pages_head_free(ar, page->marked) : aral_pages_head_full(ar, page->marked);
947 internal_fatal(!is_page_in_list(*head_ptr, page), "Page is not in this list");
@@ -1230,10 +1236,10 @@ void aral_by_size_release(ARAL *ar) {
1236 fatal("ARAL BY SIZE: double release detected");
1237
1238 aral_by_size_globals.array[size].refcount--;
1233 -// if(!aral_by_size_globals.array[size].refcount) {
1234 -// aral_destroy(aral_by_size_globals.array[size].ar);
1235 -// aral_by_size_globals.array[size].ar = NULL;
1236 -// }
1239 + // if(!aral_by_size_globals.array[size].refcount) {
1240 + // aral_destroy(aral_by_size_globals.array[size].ar);
1241 + // aral_by_size_globals.array[size].ar = NULL;
1242 + // }
1243
1244 spinlock_unlock(&aral_by_size_globals.spinlock);
1245 }