-
Notifications
You must be signed in to change notification settings - Fork 89
/
palBuddyAllocator.h
202 lines (176 loc) · 9.21 KB
/
palBuddyAllocator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
***********************************************************************************************************************
*
* Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
**********************************************************************************************************************/
/**
***********************************************************************************************************************
* @file palBuddyAllocator.h
* @brief PAL utility BuddyAllocator class declaration.
***********************************************************************************************************************
*/
#pragma once
#include "palUtil.h"
#include "palHashSet.h"
#include "palHashMap.h"
#include "palMutex.h"
namespace Util
{
/**
***********************************************************************************************************************
* @brief Buddy Allocator
*
* Responsible for managing small GPU memory requests by allocating a large base allocation and dividing it into
* appropriately sized suballocation blocks.
***********************************************************************************************************************
*/
template <typename Allocator>
class BuddyAllocator
{
public:
/// Constructor.
///
/// @param [in] pAllocator The allocator that will allocate memory if required.
/// @param [in] baseAllocSize The size of the base allocation this buddy allocator suballocates.
/// @param [in] minAllocSize The size of the smallest block this buddy allocator can allocate.
BuddyAllocator(
Allocator* pAllocator,
gpusize baseAllocSize,
gpusize minAllocSize);
~BuddyAllocator();
/// Initializes the buddy allocator.
///
/// @returns Success if the buddy allocator has been successfully initialized.
Result Init();
/// Suballocates a block from the base allocation that this buddy allocator manages. Expects @ref ClaimGpuMemory to
/// be called directly before it. If a memory manager with multiple buddyAllocators is used, use pattern should
/// be: Iterate through buddyAllocators calling ClaimGpuMemory, if one returns @ref Success break out of the loop,
/// then call Allocate on that buddyAllocator. If none return @ref Success, then a new buddyAllocator needs to be
/// created. The purpose of splitting up buddyAllocator selection and Allocation is to reduce lock contention in
/// multithreaded memory managers.
///
/// @param [in] size The size of the requested suballocation.
/// @param [in] alignment The alignment requirements of the requested suballocation.
/// @param [out] pOffset The offset the suballocated block starts within the base allocation.
///
/// @returns Success if the allocation succeeded, @ref ErrorOutOfMemory if there isn't enough system memory to
/// fulfill the request, or @ref ErrorOutOfGpuMemory if there isn't a large enough block free in the
/// base allocation to fulfill the request.
///
/// @warning Unless @ref ClaimGpuMemory is called before every single call, the results of @ref Allocate will
/// be invalid. If @ref ClaimGpuMemory returns @ref Success, then @ref ErrorOutOfGpuMemory will never be
/// returned.
Result Allocate(
gpusize size,
gpusize alignment,
gpusize* pOffset);
/// Frees a previously allocated suballocation.
///
/// @param [in] offset The offset the suballocated block starts within the base allocation.
/// @param [in] size Optional parameter specifying the size of the original allocation.
/// @param [in] alignment Optional parameter specifying the alignment of the original allocation.
void Free(
gpusize offset,
gpusize size = 0,
gpusize alignment = 0);
/// Tells whether the base allocation is completely free. If the returned value is true then the caller is safe
/// to deallocate the base allocation.
bool IsEmpty() const
{
return (m_numSuballocations == 0);
}
/// Returns the size of the largest allocation that can be suballocated with this buddy allocator.
gpusize MaximumAllocationSize() const;
/// Claims (doesn't allocate) some memory, used to quickly determine if a pool of memory has availible memory.
/// Doesn't affect internal state unless Result::Success is returned
///
/// @param [in] size The size of the requested suballocation.
/// @param [in] alignment The alignment requirements of the requested suballocation.
///
/// @returns Success if there is enough memory in this buddyAllocator to allocate the requested size of memory,
/// @ref ErrorOutOfGpuMemory if there is not enough memory
///
/// @warning Unless this is called to test availible memory before every call to Allocate, then the results will not
/// be valid.
Result ClaimGpuMemory(
gpusize size,
gpusize alignment);
/// Checks if @ref ClaimGpuMemory can actually claim memory, can be used to find the best fit pool. This function
/// does NOT acquire a lock on the structures ClaimGpuMemory uses, and does NOT claim or allocate the memory.
///
/// @param [in] size The size of the requested suballocation.
/// @param [in] alignment The alignment requirements of the requested suballocation.
/// @param [out] pKval The highest kval that will need to be split will be stored here.
///
/// @returns Success if there is enough memory in this buddyAllocator to allocate the requested size of memory,
/// @ref ErrorOutOfGpuMemory if there is not enough memory
///
Result CheckIfOpenMemory(
gpusize size,
gpusize alignment,
uint32* pKval);
private:
typedef Util::HashSet<gpusize, Allocator, JenkinsHashFunc> FreeSet;
typedef Util::HashMap<gpusize, uint32, Allocator, JenkinsHashFunc> UsedMap;
Result GetNextFreeBlock(
uint32 kval,
gpusize* pOffset);
Result FreeBlock(gpusize offset);
static constexpr gpusize KvalToSize(uint32 kVal) { return (1ull << kVal); }
static uint32 SizeToKval(gpusize size) { return Log2(size); }
Allocator* const m_pAllocator;
const uint32 m_baseAllocKval;
const uint32 m_minKval;
// Array of hashSets of blocks that are free at each level
FreeSet* m_pFreeBlockSets;
// Hashmap of blocks that are used, key=offset, value=level (kval)
UsedMap* m_pUsedBlockMap;
// List of the free memory at each level
uint32* m_pNumFreeList;
// The highest Kval that has at least 1 free block (used in ClaimGpuMemory)
uint32 m_highestFreeKval;
uint32 m_numSuballocations;
// mutex on altering the numFreeList
Util::Mutex m_numFreeMutex;
// mutex on the used block map
Util::Mutex m_usedBlockMapMutex;
// array of mutexes, one for each freeBlockSet
Util::Mutex* m_pFreeSetMutexes;
// mutex on the freeing. Serialize freeing blocks and don't allow allocating blocks while one is freeing. Based on
// testing, applications typically don't try to free and allocate memory at the same time, and almost all of the
// memory freeing is done at the end of the application.
Util::RWLock m_freeLock;
// Set to true if ClaimGpuMemory is ever called on this buddyAllocator. This signals to free to not merge blocks
// if m_pNumFreeList[kval - m_minKval] = 0
bool m_usedClaim;
// HashSet and HashMap utility functions
Result InsertToFreeSet(gpusize offset, uint32 kval);
bool GetKvalUsed(gpusize offset, uint32* pKval);
Result SetKvalUsed(gpusize offset, uint32 kval);
Result PopFromFreeSet(gpusize* pOffset, uint32 kval);
bool IsOffsetFree(gpusize offset, uint32 kval);
Result RemoveOffsetFromFreeSet(gpusize offset, uint32 kval);
Result RemoveOffsetFromUsedMap(gpusize offset);
PAL_DISALLOW_COPY_AND_ASSIGN(BuddyAllocator);
PAL_DISALLOW_DEFAULT_CTOR(BuddyAllocator);
};
} // Util