13
13
14
14
#include < ur_api.h>
15
15
16
- EnqueuedPool::~EnqueuedPool () { cleanup (); }
16
+ namespace {
17
17
18
18
std::optional<EnqueuedPool::Allocation>
19
- EnqueuedPool::getBestFit (size_t Size, size_t Alignment, void *Queue) {
20
- auto Lock = std::lock_guard (Mutex);
19
+ getBestFitHelper (size_t Size, size_t Alignment, void *Queue,
20
+ EnqueuedPool::AllocationGroupMap &Freelist) {
21
+ // Iterate over the alignments for a given queue.
22
+ auto GroupIt = Freelist.lower_bound ({Queue, Alignment});
23
+ for (; GroupIt != Freelist.end () && GroupIt->first .Queue == Queue;
24
+ ++GroupIt) {
25
+ auto &AllocSet = GroupIt->second ;
26
+ // Find the first allocation that is large enough.
27
+ auto AllocIt = AllocSet.lower_bound ({nullptr , Size, nullptr , nullptr , 0 });
28
+ if (AllocIt != AllocSet.end ()) {
29
+ auto BestFit = *AllocIt;
30
+ AllocSet.erase (AllocIt);
31
+ if (AllocSet.empty ()) {
32
+ Freelist.erase (GroupIt);
33
+ }
34
+ return BestFit;
35
+ }
36
+ }
37
+ return std::nullopt;
38
+ }
21
39
22
- Allocation Alloc = {nullptr , Size, nullptr , Queue, Alignment};
40
+ void removeFromFreelist (const EnqueuedPool::Allocation &Alloc,
41
+ EnqueuedPool::AllocationGroupMap &Freelist,
42
+ bool IsGlobal) {
43
+ const EnqueuedPool::AllocationGroupKey Key = {
44
+ IsGlobal ? nullptr : Alloc.Queue , Alloc.Alignment };
23
45
24
- auto It = Freelist.lower_bound (Alloc);
25
- if (It != Freelist.end () && It->Size >= Size && It->Queue == Queue &&
26
- It->Alignment >= Alignment) {
27
- Allocation BestFit = *It;
28
- Freelist.erase (It);
46
+ auto GroupIt = Freelist.find (Key);
47
+ assert (GroupIt != Freelist.end () && " Allocation group not found in freelist" );
29
48
30
- return BestFit;
49
+ auto &AllocSet = GroupIt->second ;
50
+ auto AllocIt = AllocSet.find (Alloc);
51
+ assert (AllocIt != AllocSet.end () && " Allocation not found in group" );
52
+
53
+ AllocSet.erase (AllocIt);
54
+ if (AllocSet.empty ()) {
55
+ Freelist.erase (GroupIt);
31
56
}
57
+ }
32
58
33
- // To make sure there's no match on other queues, we need to reset it to
34
- // nullptr and try again.
35
- Alloc.Queue = nullptr ;
36
- It = Freelist.lower_bound (Alloc);
59
+ } // namespace
37
60
38
- if (It != Freelist.end () && It->Size >= Size && It->Alignment >= Alignment) {
39
- Allocation BestFit = *It;
40
- Freelist.erase (It);
61
+ EnqueuedPool::~EnqueuedPool () { cleanup (); }
41
62
63
+ std::optional<EnqueuedPool::Allocation>
64
+ EnqueuedPool::getBestFit (size_t Size, size_t Alignment, void *Queue) {
65
+ auto Lock = std::lock_guard (Mutex);
66
+
67
+ // First, try to find the best fit in the queue-specific freelist.
68
+ auto BestFit = getBestFitHelper (Size, Alignment, Queue, FreelistByQueue);
69
+ if (BestFit) {
70
+ // Remove the allocation from the global freelist as well.
71
+ removeFromFreelist (*BestFit, FreelistGlobal, true );
72
+ return BestFit;
73
+ }
74
+
75
+ // If no fit was found in the queue-specific freelist, try the global
76
+ // freelist.
77
+ BestFit = getBestFitHelper (Size, Alignment, nullptr , FreelistGlobal);
78
+ if (BestFit) {
79
+ // Remove the allocation from the queue-specific freelist.
80
+ removeFromFreelist (*BestFit, FreelistByQueue, false );
42
81
return BestFit;
43
82
}
44
83
@@ -52,45 +91,54 @@ void EnqueuedPool::insert(void *Ptr, size_t Size, ur_event_handle_t Event,
52
91
uintptr_t Address = (uintptr_t )Ptr;
53
92
size_t Alignment = Address & (~Address + 1 );
54
93
55
- Freelist.emplace (Allocation{Ptr, Size, Event, Queue, Alignment});
94
+ Allocation Alloc = {Ptr, Size, Event, Queue, Alignment};
95
+ FreelistByQueue[{Queue, Alignment}].emplace (Alloc);
96
+ FreelistGlobal[{nullptr , Alignment}].emplace (Alloc);
56
97
}
57
98
58
99
bool EnqueuedPool::cleanup () {
59
100
auto Lock = std::lock_guard (Mutex);
60
- auto FreedAllocations = !Freelist .empty ();
101
+ auto FreedAllocations = !FreelistGlobal .empty ();
61
102
62
103
auto Ret [[maybe_unused]] = UR_RESULT_SUCCESS;
63
- for (auto It : Freelist) {
64
- Ret = MemFreeFn (It.Ptr );
65
- assert (Ret == UR_RESULT_SUCCESS);
66
-
67
- if (It.Event )
68
- EventReleaseFn (It.Event );
104
+ for (const auto &[GroupKey, AllocSet] : FreelistGlobal) {
105
+ for (const auto &Alloc : AllocSet) {
106
+ Ret = MemFreeFn (Alloc.Ptr );
107
+ assert (Ret == UR_RESULT_SUCCESS);
108
+
109
+ if (Alloc.Event ) {
110
+ EventReleaseFn (Alloc.Event );
111
+ }
112
+ }
69
113
}
70
- Freelist.clear ();
114
+
115
+ FreelistGlobal.clear ();
116
+ FreelistByQueue.clear ();
71
117
72
118
return FreedAllocations;
73
119
}
74
120
75
121
bool EnqueuedPool::cleanupForQueue (void *Queue) {
76
122
auto Lock = std::lock_guard (Mutex);
77
-
78
- Allocation Alloc = {nullptr , 0 , nullptr , Queue, 0 };
79
- // first allocation on the freelist with the specific queue
80
- auto It = Freelist.lower_bound (Alloc);
81
-
82
123
bool FreedAllocations = false ;
83
124
84
125
auto Ret [[maybe_unused]] = UR_RESULT_SUCCESS;
85
- while (It != Freelist.end () && It->Queue == Queue) {
86
- Ret = MemFreeFn (It->Ptr );
87
- assert (Ret == UR_RESULT_SUCCESS);
88
-
89
- if (It->Event )
90
- EventReleaseFn (It->Event );
91
-
92
- // Erase the current allocation and move to the next one
93
- It = Freelist.erase (It);
126
+ auto GroupIt = FreelistByQueue.lower_bound ({Queue, 0 });
127
+ while (GroupIt != FreelistByQueue.end () && GroupIt->first .Queue == Queue) {
128
+ auto &AllocSet = GroupIt->second ;
129
+ for (const auto &Alloc : AllocSet) {
130
+ Ret = MemFreeFn (Alloc.Ptr );
131
+ assert (Ret == UR_RESULT_SUCCESS);
132
+
133
+ if (Alloc.Event ) {
134
+ EventReleaseFn (Alloc.Event );
135
+ }
136
+
137
+ removeFromFreelist (Alloc, FreelistGlobal, true );
138
+ }
139
+
140
+ // Move to the next group.
141
+ GroupIt = FreelistByQueue.erase (GroupIt);
94
142
FreedAllocations = true ;
95
143
}
96
144
0 commit comments