├── .gitignore ├── LICENCE.md ├── Makefile ├── Makefile.common ├── Makefile.debug ├── Makefile.release ├── README.md ├── TODO.md ├── buddy-alloc ├── buddy-alloc.c ├── buddy-alloc.mk └── subdir.mk ├── doc ├── bitsquid-buddy-allocator-design.md └── buddy-alloc-math.md ├── include └── buddy-alloc.h ├── samples ├── sample-allocator.c ├── samples.mk └── subdir.mk ├── tests ├── basic │ ├── basic.c │ ├── basic.mk │ └── subdir.mk ├── sim │ ├── sim.c │ ├── sim.mk │ └── subdir.mk └── tests.mk └── tools ├── .gitignore ├── c-copyright-template ├── makefiles ├── .gitignore ├── project.mk ├── target.mk └── tree.mk └── script-copyright-template /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. "Contributor" 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. "Contributor Version" 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. "Covered Software" 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the terms of 34 | a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. "Larger Work" 41 | 42 | means a work that combines Covered Software with other material, in a 43 | separate file or files, that is not Covered Software. 44 | 45 | 1.8. "License" 46 | 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | 51 | means having the right to grant, to the maximum extent possible, whether 52 | at the time of the initial grant or subsequently, any and all of the 53 | rights conveyed by this License. 54 | 55 | 1.10. "Modifications" 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, 60 | deletion from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, 67 | process, and apparatus claims, in any patent Licensable by such 68 | Contributor that would be infringed, but for the grant of the License, 69 | by the making, using, selling, offering for sale, having made, import, 70 | or transfer of either its Contributions or its Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. "Source Code Form" 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. "You" (or "Your") 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, "You" includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, "control" means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or 104 | as part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its 108 | Contributions or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution 113 | become effective for each Contribution on the date the Contributor first 114 | distributes such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under 119 | this License. No additional rights or licenses will be implied from the 120 | distribution or licensing of Covered Software under this License. 121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 122 | Contributor: 123 | 124 | a. for any code that a Contributor has removed from Covered Software; or 125 | 126 | b. for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | c. under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights to 149 | grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 160 | Section 2.1. 161 | 162 | 163 | 3. Responsibilities 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | a. such Covered Software must also be made available in Source Code Form, 180 | as described in Section 3.1, and You must inform recipients of the 181 | Executable Form how they can obtain a copy of such Source Code Form by 182 | reasonable means in a timely manner, at a charge no more than the cost 183 | of distribution to the recipient; and 184 | 185 | b. You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter the 188 | recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, or 207 | limitations of liability) contained within the Source Code Form of the 208 | Covered Software, except that You may alter any license notices to the 209 | extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | 226 | If it is impossible for You to comply with any of the terms of this License 227 | with respect to some or all of the Covered Software due to statute, 228 | judicial order, or regulation then You must: (a) comply with the terms of 229 | this License to the maximum extent possible; and (b) describe the 230 | limitations and the code they affect. Such description must be placed in a 231 | text file included with all distributions of the Covered Software under 232 | this License. Except to the extent prohibited by statute or regulation, 233 | such description must be sufficiently detailed for a recipient of ordinary 234 | skill to be able to understand it. 235 | 236 | 5. Termination 237 | 238 | 5.1. The rights granted under this License will terminate automatically if You 239 | fail to comply with any of its terms. However, if You become compliant, 240 | then the rights granted under this License from a particular Contributor 241 | are reinstated (a) provisionally, unless and until such Contributor 242 | explicitly and finally terminates Your grants, and (b) on an ongoing 243 | basis, if such Contributor fails to notify You of the non-compliance by 244 | some reasonable means prior to 60 days after You have come back into 245 | compliance. Moreover, Your grants from a particular Contributor are 246 | reinstated on an ongoing basis if such Contributor notifies You of the 247 | non-compliance by some reasonable means, this is the first time You have 248 | received notice of non-compliance with this License from such 249 | Contributor, and You become compliant prior to 30 days after Your receipt 250 | of the notice. 251 | 252 | 5.2. If You initiate litigation against any entity by asserting a patent 253 | infringement claim (excluding declaratory judgment actions, 254 | counter-claims, and cross-claims) alleging that a Contributor Version 255 | directly or indirectly infringes any patent, then the rights granted to 256 | You by any and all Contributors for the Covered Software under Section 257 | 2.1 of this License shall terminate. 258 | 259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 260 | license agreements (excluding distributors and resellers) which have been 261 | validly granted by You or Your distributors under this License prior to 262 | termination shall survive termination. 263 | 264 | 6. Disclaimer of Warranty 265 | 266 | Covered Software is provided under this License on an "as is" basis, 267 | without warranty of any kind, either expressed, implied, or statutory, 268 | including, without limitation, warranties that the Covered Software is free 269 | of defects, merchantable, fit for a particular purpose or non-infringing. 270 | The entire risk as to the quality and performance of the Covered Software 271 | is with You. Should any Covered Software prove defective in any respect, 272 | You (not any Contributor) assume the cost of any necessary servicing, 273 | repair, or correction. This disclaimer of warranty constitutes an essential 274 | part of this License. No use of any Covered Software is authorized under 275 | this License except under this disclaimer. 276 | 277 | 7. Limitation of Liability 278 | 279 | Under no circumstances and under no legal theory, whether tort (including 280 | negligence), contract, or otherwise, shall any Contributor, or anyone who 281 | distributes Covered Software as permitted above, be liable to You for any 282 | direct, indirect, special, incidental, or consequential damages of any 283 | character including, without limitation, damages for lost profits, loss of 284 | goodwill, work stoppage, computer failure or malfunction, or any and all 285 | other commercial damages or losses, even if such party shall have been 286 | informed of the possibility of such damages. This limitation of liability 287 | shall not apply to liability for death or personal injury resulting from 288 | such party's negligence to the extent applicable law prohibits such 289 | limitation. Some jurisdictions do not allow the exclusion or limitation of 290 | incidental or consequential damages, so this exclusion and limitation may 291 | not apply to You. 292 | 293 | 8. Litigation 294 | 295 | Any litigation relating to this License may be brought only in the courts 296 | of a jurisdiction where the defendant maintains its principal place of 297 | business and such litigation shall be governed by laws of that 298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 299 | in this Section shall prevent a party's ability to bring cross-claims or 300 | counter-claims. 301 | 302 | 9. Miscellaneous 303 | 304 | This License represents the complete agreement concerning the subject 305 | matter hereof. If any provision of this License is held to be 306 | unenforceable, such provision shall be reformed only to the extent 307 | necessary to make it enforceable. Any law or regulation which provides that 308 | the language of a contract shall be construed against the drafter shall not 309 | be used to construe this License against a Contributor. 310 | 311 | 312 | 10. Versions of the License 313 | 314 | 10.1. New Versions 315 | 316 | Mozilla Foundation is the license steward. Except as provided in Section 317 | 10.3, no one other than the license steward has the right to modify or 318 | publish new versions of this License. Each version will be given a 319 | distinguishing version number. 320 | 321 | 10.2. Effect of New Versions 322 | 323 | You may distribute the Covered Software under the terms of the version 324 | of the License under which You originally received the Covered Software, 325 | or under the terms of any subsequent version published by the license 326 | steward. 327 | 328 | 10.3. Modified Versions 329 | 330 | If you create software not governed by this License, and you want to 331 | create a new license for such software, you may create and use a 332 | modified version of this License if you rename the license and remove 333 | any references to the name of the license steward (except to note that 334 | such modified license differs from this License). 335 | 336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 337 | Licenses If You choose to distribute Source Code Form that is 338 | Incompatible With Secondary Licenses under the terms of this version of 339 | the License, the notice described in Exhibit B of this License must be 340 | attached. 341 | 342 | Exhibit A - Source Code Form License Notice 343 | 344 | This Source Code Form is subject to the 345 | terms of the Mozilla Public License, v. 346 | 2.0. If a copy of the MPL was not 347 | distributed with this file, You can 348 | obtain one at 349 | http://mozilla.org/MPL/2.0/. 350 | 351 | If it is not possible or desirable to put the notice in a particular file, 352 | then You may include the notice in a location (such as a LICENSE file in a 353 | relevant directory) where a recipient would be likely to look for such a 354 | notice. 355 | 356 | You may add additional accurate notices of copyright ownership. 357 | 358 | Exhibit B - "Incompatible With Secondary Licenses" Notice 359 | 360 | This Source Code Form is "Incompatible 361 | With Secondary Licenses", as defined by 362 | the Mozilla Public License, v. 2.0. 363 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | export PROJECT_ROOT ?= ${CURDIR} 10 | export OUTPUT_ROOT ?= ${PROJECT_ROOT}/build 11 | export BUILD_TYPE ?= release 12 | 13 | CROSS_COMPILE := 14 | 15 | include ${PROJECT_ROOT}/tools/makefiles/tree.mk 16 | 17 | targets: buddy-alloc tests samples 18 | 19 | distclean: 20 | @echo "DISTCLEAN ${PROJECT_ROOT}" 21 | $(Q)-${RM} -r ${OUTPUT_ROOT} 22 | -------------------------------------------------------------------------------- /Makefile.common: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | ifeq (${.DEFAULT_GOAL},) 10 | .DEFAULT_GOAL := all 11 | endif 12 | 13 | ifeq ("$(origin V)", "command line") 14 | VERBOSE = ${V} 15 | endif 16 | 17 | ifndef VERBOSE 18 | VERBOSE = 0 19 | endif 20 | 21 | ifeq (${VERBOSE},1) 22 | quiet = 23 | Q = 24 | else 25 | quiet = (${1}) 26 | Q = @ 27 | endif 28 | 29 | export TOOLS_ROOT ?= ${PROJECT_ROOT}/tools 30 | export BUILD_ROOT ?= ${OUTPUT_ROOT}/${BUILD_TYPE} 31 | 32 | CC := ${CROSS_COMPILE}gcc 33 | CXX := ${CROSS_COMPILE}g++ 34 | LD := ${CROSS_COMPILE}gcc 35 | AR := ${CROSS_COMPILE}ar 36 | AS := ${CROSS_COMPILE}as 37 | OBJCOPY := ${CROSS_COMPILE}objcopy 38 | OBJDUMP := ${CROSS_COMPILE}objdump 39 | SIZE := ${CROSS_COMPILE}size 40 | NM := ${CROSS_COMPILE}nm 41 | FIND := find 42 | 43 | ARFLAGS := cr 44 | ASFLAGS := ${CROSS_FLAGS} 45 | CFLAGS := ${CROSS_FLAGS} -fno-omit-frame-pointer -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -Wall -Wunused -Wuninitialized -Wmissing-declarations -std=c11 46 | CXXFLAGS := ${CROSS_FLAGS} -fno-omit-frame-pointer -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -Wall -Wunused -Wuninitialized -Wmissing-declarations -std=c11 47 | CPPFLAGS += -DBUILD_TYPE="${BUILD_TYPE}" 48 | LDFLAGS := ${CROSS_FLAGS} 49 | LDLIBS := 50 | LOADLIBES := 51 | 52 | -include ${PROJECT_ROOT}/Makefile.${BUILD_TYPE} 53 | -include ${SOURCE_DIR}/Makefile.${BUILD_TYPE} 54 | -------------------------------------------------------------------------------- /Makefile.debug: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | LDFLAGS += -O0 -pg 10 | CFLAGS += -O0 -g -pg 11 | CPPFLAGS += -DBUDDY_ALLOC_DEBUG -DDEBUG -DBUILD_TYPE_DEBUG 12 | -------------------------------------------------------------------------------- /Makefile.release: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | LDFLAGS += -O3 10 | CFLAGS += -O3 11 | CPPFLAGS += -DNDEBUG -DBUILD_TYPE_RELEASE 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Buddy Allocator 2 | =============== 3 | 4 | Copyright 2015, Red Rocket Computing, LLC, stephen@redrocketcomputing.com 5 | 6 | Overview 7 | -------- 8 | 9 | This an implementation Knuth's "buddy" system for dynamic memory allocation 10 | (Knuth. The Art of Computer Programming, Volume 1, Fundamental Algorithms, 11 | Section 2.5C). 12 | 13 | The allocator manages free block using intrusive linked list structures with 14 | one list head for every available block size. Block splits and buddy states 15 | are tracked using binary bit trees. 16 | 17 | The library support both internal and external storage of the allocator 18 | metadata and provides both a standard unsized "free" API as well as an 19 | accelerated "release" API for use when the size of the block to be free 20 | is known. 21 | 22 | License 23 | ------- 24 | 25 | To facilate the use of this library in statically linked open and closed source 26 | embedded systems, this library is licensed under the terms of the 27 | [Mozilla Public License, V2.0.](http://mozilla.org/MPL/2.0) 28 | 29 | Acknowledgments 30 | --------------- 31 | 32 | This implementation of Knuth's Buddy Allocator closely follows the most 33 | excellent ideas posted by 34 | [Niklas](https://www.blogger.com/profile/10055379994557504977) in his post 35 | [Allocation Adventures 3: The Buddy Allocator](http://bitsquid.blogspot.com/2015/08/allocation-adventures-3-buddy-allocator.html) 36 | 37 | Niklas primer on memory allocation metadata management is fantastic and has 38 | also been included in the doc directory. I took the liberty of converting the 39 | blog post to markdown and attaching a copyright attributing the writeup him. 40 | 41 | *Niklas, if you have a problem with this, let me know and I will remove it 42 | markdown version from my project. Just reach out!* 43 | 44 | Dependencies 45 | ------------ 46 | 47 | The allocator requires a compatible compiler supporting the following built-ins: 48 | 49 | __builtin_clzl /* Count lead zeros long */ 50 | __builtin_ctzl /* Count trailing zeros long */ 51 | 52 | and is known to work with recent version of gcc and clang. 53 | 54 | Building The Allocator 55 | ---------------------- 56 | 57 | For the default release version (-O3) 58 | 59 | CD=path/to/project 60 | make 61 | 62 | To build a debug (-O0 -pg -g) version of the library: 63 | 64 | CD=path/to/project 65 | make V=1 BUILD_TYPE=debug 66 | 67 | The library can be found at 68 | 69 | path/to/project/BUILD_TYPE/libbuddy-alloc.a 70 | 71 | The build location can be adjusted with OUTPUT_ROOT which must be an absolute path: 72 | 73 | make OUTPUT_ROOT=/some/directory 74 | 75 | To cross compile use the CROSS_COMPILE variable: 76 | 77 | make CROSS_COMPILE=arm-none-eabi- 78 | 79 | Other build options can be adjusted in Makefile.common, Makefile.debug and Makefile.release. 80 | 81 | Using The Allocator 82 | ------------------- 83 | 84 | To create an 1MB buddy allocator with internal metadata: 85 | 86 | #include 87 | #include 88 | #include 89 | 90 | #define ALLOCATOR_SIZE 1048576 91 | 92 | unsigned long int memory_region[ALLOCATOR_SIZE / sizeof(unsigned long int)]; 93 | 94 | int main(int argc, char **argv) 95 | { 96 | buddy_allocator_t *allocator; 97 | void *ptr; 98 | 99 | /* The return address is always equal to the provide memory region */ 100 | allocator = buddy_create(memory_region, ALLOCATOR_SIZE); 101 | 102 | /* This will allocate a block 16K in size */ 103 | ptr = buddy_alloc(allocator, 13773); 104 | if(!ptr) { 105 | perror("allocation failed"); 106 | exit(1); 107 | } 108 | 109 | /* This will determine the size of the memory block, by searching the 110 | free index at each level, somewhat slower */ 111 | buddy_free(allocator, ptr); 112 | 113 | /* This will allocate a block 16K in size */ 114 | ptr = buddy_alloc(allocator, 13773); 115 | if(!ptr) { 116 | perror("allocation failed"); 117 | exit(1); 118 | } 119 | 120 | /* This frees the memory block, using the provided size, a little 121 | faster as the block level can be derived from the size */ 122 | buddy_release(allocator, ptr, 13773); 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - Add support for specific min allocations 2 | - Add support for max allocations, effectively pre-splitting of blocks with ceiling on min level when combining blocks 3 | - Add support for discontious memory when using max allocations setup 4 | - Add support for power of 2 aligned allocations using the follow idea: 5 | Allocate block of the size of alignments 6 | Split block to nearest power of 2 of the requested size 7 | Add the remainder to the free block lists 8 | - Use intptr_ instead of unsigned long int 9 | -------------------------------------------------------------------------------- /buddy-alloc/buddy-alloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Stephen Street 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #define BIT_ARRAY_NUM_BITS (8 * sizeof(unsigned long int)) 13 | #define BIT_ARRAY_INDEX_SHIFT (__builtin_ctzl(BIT_ARRAY_NUM_BITS)) 14 | #define BIT_ARRAY_INDEX_MASK (BIT_ARRAY_NUM_BITS - 1UL) 15 | 16 | static inline void bit_array_set(unsigned long int *bit_array, unsigned long int index) 17 | { 18 | bit_array[index >> BIT_ARRAY_INDEX_SHIFT] |= (1UL << (index & BIT_ARRAY_INDEX_MASK)); 19 | } 20 | 21 | static inline void bit_array_clear(unsigned long int *bit_array, unsigned long int index) 22 | { 23 | bit_array[index >> BIT_ARRAY_INDEX_SHIFT] &= ~(1UL << (index & BIT_ARRAY_INDEX_MASK)); 24 | } 25 | 26 | static inline bool bit_array_is_set(const unsigned long int *bit_array, unsigned long int index) 27 | { 28 | return (bit_array[index >> BIT_ARRAY_INDEX_SHIFT] & (1UL << (index & BIT_ARRAY_INDEX_MASK))) != 0; 29 | } 30 | 31 | static inline void bit_array_not(unsigned long int *bit_array, unsigned long int index) 32 | { 33 | register unsigned long int array_index = index >> BIT_ARRAY_INDEX_SHIFT; 34 | register unsigned long int bit_value = (1UL << (index & BIT_ARRAY_INDEX_MASK)); 35 | 36 | if (bit_array[array_index] & bit_value) 37 | bit_array[array_index] &= ~bit_value; 38 | else 39 | bit_array[array_index] |= bit_value; 40 | } 41 | 42 | static inline void list_init(buddy_block_info_t *list) 43 | { 44 | list->next = list; 45 | list->prev = list; 46 | } 47 | 48 | static inline void list_add(buddy_block_info_t *list, buddy_block_info_t *node) 49 | { 50 | node->next = list; 51 | node->prev = list->prev; 52 | list->prev->next = node; 53 | list->prev = node; 54 | } 55 | 56 | static inline void list_remove(buddy_block_info_t *list) 57 | { 58 | list->next->prev = list->prev; 59 | list->prev->next = list->next; 60 | list->next = list; 61 | list->prev = list; 62 | } 63 | 64 | static inline bool list_empty(buddy_block_info_t *list) 65 | { 66 | return list->next == list; 67 | } 68 | 69 | static inline buddy_block_info_t *list_pop(buddy_block_info_t *list) 70 | { 71 | buddy_block_info_t *front = NULL; 72 | if (!list_empty(list)) 73 | { 74 | front = list->next; 75 | list_remove(front); 76 | } 77 | return front; 78 | } 79 | 80 | static inline unsigned int long size_to_level(const buddy_allocator_t *allocator, size_t size) 81 | { 82 | /* Floor on the minimum allocation size */ 83 | if (size < allocator->min_allocation) 84 | return allocator->max_level; 85 | 86 | /* Round up to next power of two */ 87 | size = 1 << (BUDDY_NUM_BITS - __builtin_clzl(size - 1)); 88 | 89 | /* Delta between the number of levels and the first set bit in the size */ 90 | /* TODO simplify with size rounding */ 91 | return allocator->total_levels - BUDDY_ILOG2(size); 92 | } 93 | 94 | static inline unsigned long int index_of(const buddy_allocator_t *allocator, const void *ptr, unsigned long int level) 95 | { 96 | return (1UL << level) + ((ptr - allocator->address) >> (allocator->total_levels - level)) - 1UL; 97 | } 98 | 99 | static inline unsigned long int free_index(const buddy_allocator_t *allocator, unsigned long int index) 100 | { 101 | return (index - 1) >> 1; 102 | } 103 | 104 | static inline unsigned long int split_index(const buddy_allocator_t *allocator, unsigned long int index) 105 | { 106 | return index + (allocator->max_indexes >> 1); 107 | } 108 | 109 | static inline void *to_buddy(const buddy_allocator_t *allocator, const void *ptr, unsigned long int level) 110 | { 111 | #ifdef BUDDY_MEMORY_ALIGNED_ON_SIZE 112 | return (void *)((unsigned long int)ptr ^ (allocator->size >> level)); 113 | #else 114 | return ((ptr - allocator->address) ^ (allocator->size >> level)) + allocator->address; 115 | #endif 116 | } 117 | 118 | static void *buddy_alloc_from_level(buddy_allocator_t *allocator, unsigned long int level) 119 | { 120 | long int block_at_level; 121 | long int index; 122 | buddy_block_info_t *block_ptr = 0; 123 | buddy_block_info_t *buddy_block_ptr = 0; 124 | 125 | /* Search backwards up the levels */ 126 | for (block_at_level = level; block_at_level >= 0; --block_at_level) { 127 | if (!list_empty(&allocator->free_blocks[block_at_level])) { 128 | block_ptr = list_pop(&allocator->free_blocks[block_at_level]); 129 | break; 130 | } 131 | } 132 | 133 | /* Did we find a block? */ 134 | if (!block_ptr) 135 | return block_ptr; 136 | 137 | /* Calculate the index of the block found */ 138 | index = index_of(allocator, block_ptr, block_at_level); 139 | 140 | /* Do we need to split blocks? */ 141 | if (block_at_level != level) { 142 | 143 | /* Split the block until we reach the requested level */ 144 | while (block_at_level < level) { 145 | 146 | /* Mark block as split */ 147 | bit_array_set(allocator->block_index, split_index(allocator, index)); 148 | 149 | /* Mark as allocated, level zero does not use a allocation flags */ 150 | if (block_at_level > 0) 151 | bit_array_not(allocator->block_index, free_index(allocator, index)); 152 | 153 | /* Get the buddy pointer */ 154 | buddy_block_ptr = (buddy_block_info_t *)to_buddy(allocator, block_ptr, block_at_level + 1); 155 | 156 | /* Add right side to the free list */ 157 | list_add(&allocator->free_blocks[block_at_level + 1], buddy_block_ptr); 158 | 159 | /* Adjust index to left child */ 160 | index = (index << 1) + 1; 161 | 162 | /* Adjust to the next level */ 163 | ++block_at_level; 164 | } 165 | } 166 | 167 | /* Mark as allocated, level zero does not use a allocation flag */ 168 | if (level > 0) 169 | bit_array_not(allocator->block_index, free_index(allocator, index)); 170 | 171 | /* All done */ 172 | return block_ptr; 173 | } 174 | 175 | static void buddy_release_at_level(buddy_allocator_t *allocator, void *ptr, unsigned long int level) 176 | { 177 | void *buddy_ptr = to_buddy(allocator, ptr, level); 178 | unsigned long int index = index_of(allocator, ptr, level); 179 | 180 | /* Flip the allocation bit, level zero does not use a allocation bit */ 181 | if (level > 0) 182 | bit_array_not(allocator->block_index, free_index(allocator, index)); 183 | 184 | /* Consolidate the blocks */ 185 | while (level > 0 && !bit_array_is_set(allocator->block_index, free_index(allocator, index))) { 186 | 187 | /* Clear the split bit */ 188 | bit_array_clear(allocator->block_index, split_index(allocator, index)); 189 | 190 | /* Remove it from the list */ 191 | list_remove(buddy_ptr); 192 | 193 | /* Adjust the index and level */ 194 | index = (index - 1) >> 1; 195 | --level; 196 | 197 | /* Make sure our we are now using the left buddy */ 198 | if (buddy_ptr < ptr) 199 | ptr = buddy_ptr; 200 | 201 | /* Get the new buddy */ 202 | buddy_ptr = to_buddy(allocator, ptr, level); 203 | 204 | /* Flip the allocation bit */ 205 | if (level > 0) 206 | bit_array_not(allocator->block_index, free_index(allocator, index)); 207 | } 208 | 209 | /* Clear the split bit */ 210 | bit_array_clear(allocator->block_index, split_index(allocator, index)); 211 | 212 | /* Add combined block to it's free list */ 213 | list_add(&allocator->free_blocks[level], ptr); 214 | } 215 | 216 | void *buddy_alloc(buddy_allocator_t *allocator, size_t size) 217 | { 218 | unsigned long int level = size_to_level(allocator, size); 219 | return buddy_alloc_from_level(allocator, level); 220 | } 221 | 222 | void buddy_release(buddy_allocator_t *allocator, void *ptr, size_t size) 223 | { 224 | /* Do nothing on null pointer */ 225 | if (!ptr) 226 | return; 227 | 228 | /* Determine level and release */ 229 | buddy_release_at_level(allocator, ptr, size_to_level(allocator, size)); 230 | } 231 | 232 | void buddy_free(buddy_allocator_t *allocator, void *ptr) 233 | { 234 | unsigned long int index; 235 | 236 | /* Do nothing on null pointer */ 237 | if (!ptr) 238 | return; 239 | 240 | /* Determine level and release */ 241 | index = index_of(allocator, ptr, allocator->max_level); 242 | for (unsigned long int level = allocator->max_level; level > 0; --level) { 243 | index = (index - 1) >> 1; 244 | if (bit_array_is_set(allocator->block_index, split_index(allocator, index))) { 245 | buddy_release_at_level(allocator, ptr, level); 246 | return; 247 | } 248 | } 249 | 250 | /* Must be allocated from the root */ 251 | buddy_release_at_level(allocator, ptr, 0); 252 | } 253 | 254 | void buddy_init(buddy_allocator_t *allocator, void *address, size_t size) 255 | { 256 | int i; 257 | 258 | /* Initialize allocator setup */ 259 | allocator->address = address; 260 | allocator->size = size; 261 | allocator->min_allocation = BUDDY_MIN_LEAF_SIZE; 262 | allocator->total_levels = BUDDY_ILOG2(allocator->size); 263 | allocator->max_indexes = BUDDY_MAX_INDEXES(allocator->size, allocator->min_allocation); 264 | allocator->max_level = BUDDY_MAX_LEVELS(allocator->size, allocator->min_allocation); 265 | allocator->free_blocks = (void *)allocator + sizeof(buddy_allocator_t); 266 | allocator->block_index = (void *)allocator + sizeof(buddy_allocator_t) + (sizeof(buddy_block_info_t) * (allocator->max_level + 1)); 267 | allocator->extra_metadata = 0; 268 | 269 | /* Initial the block levels */ 270 | for (i = 0; i < allocator->max_level + 1; ++i) 271 | list_init(&allocator->free_blocks[i]); 272 | 273 | /* Initialize the clear the block index */ 274 | for (i = 0; i < (allocator->max_indexes + (BUDDY_NUM_BITS - 1)) / BUDDY_NUM_BITS; ++i) 275 | allocator->block_index[i] = 0; 276 | 277 | /* Add memory at level 0 */ 278 | list_add(&allocator->free_blocks[0], (buddy_block_info_t *)address); 279 | } 280 | 281 | buddy_allocator_t *buddy_create(void *address, size_t size) 282 | { 283 | int i; 284 | buddy_allocator_t *final_allocator = address; 285 | 286 | /* Setup initial alloctor usig the tailing side of the address range */ 287 | size_t metadata_size = buddy_sizeof_metadata(size, BUDDY_MIN_LEAF_SIZE); 288 | buddy_allocator_t *initial_allocator = (buddy_allocator_t *)((address + size) - metadata_size); 289 | 290 | /* Setup the initial allocator */ 291 | buddy_init(initial_allocator, address, size); 292 | 293 | /* Allocate enough min sized block to cover the meta data, we do not need the actual pointers for anything */ 294 | initial_allocator->extra_metadata = (void *)-1; 295 | for (i = 0; i < (metadata_size + (BUDDY_MIN_LEAF_SIZE - 1)) / BUDDY_MIN_LEAF_SIZE; ++i) 296 | (void)buddy_alloc_from_level(initial_allocator, initial_allocator->max_level); 297 | 298 | /* Move the initial allocator into place */ 299 | final_allocator = address; 300 | final_allocator->address = address; 301 | final_allocator->size = initial_allocator->size; 302 | final_allocator->min_allocation = initial_allocator->min_allocation; 303 | final_allocator->max_indexes = initial_allocator->max_indexes; 304 | final_allocator->total_levels = initial_allocator->total_levels; 305 | final_allocator->max_level = initial_allocator->max_level; 306 | final_allocator->free_blocks = address + sizeof(buddy_allocator_t); 307 | final_allocator->block_index = address + sizeof(buddy_allocator_t) + (sizeof(buddy_block_info_t) * (final_allocator->max_level + 1)); 308 | final_allocator->extra_metadata = 0; 309 | 310 | /* Copy the block indexes */ 311 | for (i = 0; i < (final_allocator->max_indexes + (BUDDY_NUM_BITS - 1)) / BUDDY_NUM_BITS; ++i) 312 | final_allocator->block_index[i] = initial_allocator->block_index[i]; 313 | 314 | /* Relink the free blocks */ 315 | for (int level = 0; level < initial_allocator->max_level + 1; ++level) { 316 | 317 | /* Initialize the final allocator free block list for the current level */ 318 | list_init(&final_allocator->free_blocks[level]); 319 | 320 | /* For non-empty free block lists in the initial allocator, rewrite the head and tail pointers */ 321 | if (!list_empty(&initial_allocator->free_blocks[level])) { 322 | 323 | final_allocator->free_blocks[level].next = initial_allocator->free_blocks[level].next; 324 | final_allocator->free_blocks[level].prev = initial_allocator->free_blocks[level].prev; 325 | 326 | final_allocator->free_blocks[level].next->prev = &final_allocator->free_blocks[level]; 327 | final_allocator->free_blocks[level].prev->next = &final_allocator->free_blocks[level]; 328 | } 329 | } 330 | 331 | /* All done */ 332 | return final_allocator; 333 | } 334 | 335 | size_t buddy_largest_available(const buddy_allocator_t *allocator) 336 | { 337 | /* Find first level with a block available */ 338 | for (unsigned long int level = 0; level < allocator->max_level + 1; ++level) 339 | if (!list_empty(&allocator->free_blocks[level])) 340 | return allocator->size >> level; 341 | 342 | /* No blocks available */ 343 | return 0; 344 | } 345 | 346 | size_t buddy_available(const buddy_allocator_t *allocator) 347 | { 348 | size_t available = 0; 349 | int blocks_available; 350 | 351 | for (unsigned long int level = 0; level < allocator->max_level + 1; ++level) { 352 | blocks_available = 0; 353 | for (buddy_block_info_t *cursor = allocator->free_blocks[level].next; cursor != &allocator->free_blocks[level]; cursor = cursor->next) 354 | ++blocks_available; 355 | available += blocks_available << (allocator->total_levels - level); 356 | } 357 | 358 | return available; 359 | } 360 | 361 | size_t buddy_used(const buddy_allocator_t *allocator) 362 | { 363 | return allocator->size - buddy_available(allocator); 364 | } 365 | -------------------------------------------------------------------------------- /buddy-alloc/buddy-alloc.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | ifeq ($(findstring ${BUILD_ROOT},${CURDIR}),) 10 | include ${PROJECT_ROOT}/tools/makefiles/target.mk 11 | else 12 | 13 | TARGET := libbuddy-alloc.a 14 | 15 | include ${PROJECT_ROOT}/tools/makefiles/project.mk 16 | 17 | CPPFLAGS += -I ${SOURCE_DIR}/../include 18 | 19 | endif 20 | 21 | -------------------------------------------------------------------------------- /buddy-alloc/subdir.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | where-am-i := $(lastword ${MAKEFILE_LIST}) 10 | 11 | SRC += $(wildcard $(dir $(where-am-i))*.c) 12 | SRC += $(wildcard $(dir $(where-am-i))*.S) 13 | SRC += $(wildcard $(dir $(where-am-i))*.s) 14 | -------------------------------------------------------------------------------- /doc/bitsquid-buddy-allocator-design.md: -------------------------------------------------------------------------------- 1 | Allocation Adventures 3: The Buddy Allocator 2 | ============================================ 3 | 4 | Copyright 2015, [Niklas](https://www.blogger.com/profile/10055379994557504977) 5 | 6 | Hello, allocator! 7 | ----------------- 8 | 9 | The job of a memory allocator is to take a big block of memory (from the OS) 10 | and chop it up into smaller pieces for individual allocations: 11 | 12 | 13 | void *A = malloc(10); 14 | void *B = malloc(100); 15 | void *C = malloc(20); 16 | 17 | ------------------------------------------------------ 18 | | A | free | B | C | free | 19 | ------------------------------------------------------ 20 | 21 | 22 | The allocator needs to be fast at serving an allocation request, i.e. finding a 23 | suitable piece of free memory. It also needs to be fast at freeing memory, i.e. 24 | making a previously used piece of memory available for new allocations. 25 | Finally, it needs to prevent fragmentation -- more about that in a moment. 26 | 27 | Suppose we put all free blocks in a linked list and allocate memory by 28 | searching that list for a block of a suitable size. That makes allocation an 29 | O(n) operation, where n is the total number of free blocks. There could be 30 | thousands of free blocks and following the links in the list will cause cache 31 | misses, so to make a competitive allocator we need a faster method. 32 | 33 | Fragmentation occurs when the free memory cannot be used effectively, because 34 | it is chopped up into little pieces: 35 | 36 | ------------------------------------------------------ 37 | | A | free | B | C | free | 38 | ------------------------------------------------------ 39 | 40 | Here, we might not be able to service a large allocation request, because the 41 | free memory is split up in two pieces. In a real world scenario, the memory 42 | can be fragmented into thousands of pieces. 43 | 44 | The first step in preventing fragmentation is to ensure that we have some way 45 | of merging free memory blocks together. Otherwise, allocating blocks and 46 | freeing them will leave the memory buffer in a chopped up state where it is 47 | unable to handle any large requests: 48 | 49 | ------------------------------------------------------- 50 | | free | free | free | free | free | free | 51 | ------------------------------------------------------- 52 | 53 | Merging needs to be a quick operation, so scanning the entire buffer for 54 | adjacent free blocks is not an option. 55 | 56 | Note that even if we merge all neighboring free blocks, we can still get 57 | fragmentation, because we can't merge the free blocks when there is a piece 58 | of allocated memory between them: 59 | 60 | ----------------------------------------------------------- 61 | | free | A | free | B | free | C | free | D | free | 62 | ----------------------------------------------------------- 63 | 64 | Some useful techniques for preventing this kind of fragmentation are: 65 | 66 | - Use separate allocators for long-lived and short-lived allocations, so that 67 | the short-lived allocations don't create "holes" between the long lived ones. 68 | - Put "small" allocations in a separate part of the buffer so they don't 69 | interfere with the big ones. 70 | - Make the memory blocks relocatable (i.e. use "handles" rather than pointers). 71 | - Allocate whole pages from the OS and rely on the page mapping to prevent 72 | fragmentation. 73 | 74 | The last approach can be surprisingly efficient if you have a small page size 75 | and follow the advice suggested earlier in this series, to try to have a few 76 | large allocations rather than many small ones. On the other hand, a small page 77 | size means more TLB misses. But maybe that doesn't matter so much if you have 78 | good data locality. Speculation, speculation! I should provide some real 79 | numbers instead, but that is too much work! 80 | 81 | Three techniques used by many allocators are in-place linked lists, preambles 82 | and postambles. 83 | 84 | In-place linked lists is a technique for storing linked lists of free memory 85 | blocks without using any extra memory. The idea is that since the memory in 86 | the blocks is free anyway, we can just store the prev and next pointers 87 | directly in the blocks themselves, which means we don't need any extra 88 | storage space. 89 | 90 | A preamble is a little piece of data that sits just before the allocated 91 | memory and contains some information about that memory block. The allocator 92 | allocates extra memory for the preamble and fills it with information when the 93 | memory is allocated: 94 | 95 | void *A = malloc(100); 96 | 97 | ------------------------ 98 | | pre | A | post| 99 | ------------------------ 100 | 101 | In C we pretty much need to have a preamble, because when the user calls 102 | free(void *p) on a pointer p, we get no information about how big the memory 103 | block allocated at p is. That information needs to come from somewhere and a 104 | preamble is a reasonable option, because it is easy to access from the free() 105 | code: 106 | 107 | struct Preamble 108 | { 109 | unsigned size; 110 | ... 111 | }; 112 | 113 | void free(void *p) 114 | { 115 | Preamble *pre = (Preamble *)p - 1; 116 | unsigned size = pre->size; 117 | } 118 | 119 | Note that there are other options. We could use a hash table to store the size 120 | of each pointer. We could reserve particular areas in the memory buffer for 121 | allocations of certain sizes and use pointer compare to find the area (and 122 | hence the size) for a certain pointer. But hash tables are expensive, and 123 | having certain areas for allocations of certain sizes only really work if you 124 | have a limited number of different sizes. So preambles are a common option. 125 | 126 | They are really annoying though. They increase the size of all memory 127 | allocations and they mess with alignment. For example, suppose that the user 128 | wants to allocate 4 K of memory and that our OS uses 4 K pages. Without 129 | preambles, we could just allocate a page from the OS and return it. But if we 130 | need a four byte preamble, then we will have to allocate 8 K from the OS so 131 | that we have somewhere to put those extra four bytes. So annoying! 132 | 133 | And what makes it even more annoying is that in most cases storing the size is 134 | pointless, because the caller already knows it. For example, in C++, when we do: 135 | 136 | delete x; 137 | 138 | The runtime knows the actual type of x, because otherwise it wouldn't be able 139 | to call the destructor properly. But since it knows the type, it knows the 140 | size of that type and it could provide that information to the allocator when 141 | the memory is freed.. 142 | 143 | Similarly, if the memory belongs to an std::vector, the vector class has a 144 | capacity field that stores how big the buffer is, so again the size is known. 145 | 146 | In fact, you could argue that whenever you have a pointer, some part of the 147 | runtime has to know how big that memory allocation is, because otherwise, how 148 | could the runtime use that memory without causing an access violation? 149 | 150 | So we could imagine a parallel world where instead of free(void *) we would 151 | have free(void *, size_t) and the caller would be required to explicitly pass 152 | the size when freeing a memory block. That world would be a paradise for 153 | allocators. But alas, it is not the world we live in. 154 | 155 | (You could enforce this parallel world in a subsystem, but I'm not sure if it 156 | is a good idea to enforce it across the board in a bigger project. Going 157 | against the grain of the programming language can be painful.) 158 | 159 | A postamble is a similar piece of data that is put at the end of an allocated 160 | memory block. 161 | 162 | Postambles are useful for merging. As mentioned above, when you free a memory 163 | block, you want to merge it with its free neighbors. But how do you know what 164 | the neighbors are and if they are free or not? 165 | 166 | For the memory block to the right it is easy. That memory block starts where 167 | yours end, so you can easily get to it and check its preamble. 168 | 169 | The neighbor to the left is trickier. Since you don't know how big that memory 170 | block might be, you don't know where to find its preamble. A postamble solves 171 | that problem, since the postamble of the block to the left will always be 172 | located just before your block. 173 | 174 | Again, the alternative to using preambles and postambles to check for merging 175 | is to have some centralized structure with information about the blocks that 176 | you can query. And the challenge is to make such queries efficient. 177 | 178 | If you require all allocations to be 16-byte aligned, then having both a 179 | preamble and a postamble will add 32 bytes of overhead to your allocations. 180 | That is not peanuts, especially if you have many small allocations. You can 181 | get around that by using slab or block allocators for such allocations, or 182 | even better, avoid them completely and try to make fewer and bigger 183 | allocations instead, as already mentioned in this series. 184 | 185 | The Buddy Allocator 186 | ------------------- 187 | 188 | With that short introduction to some general allocation issues, it is time to 189 | take a look at the buddy allocator. 190 | 191 | The buddy allocator works by repeatedly splitting memory blocks in half to 192 | create two smaller "buddies" until we get a block of the desired size. 193 | 194 | If we start with a 512 K block allocated from the OS, we can split it to 195 | create two 256 K buddies. We can then take one of those and split it further 196 | into two 128 K buddies, and so on. 197 | 198 | When allocating, we check to see if we have a free block of the appropriate 199 | size. If not, we split a larger block as many times as necessary to get a 200 | block of a suitable size. So if we want 32 K, we split the 128 K block into 201 | 64 K and then split one of those into 32 K. 202 | 203 | At the end of this, the state of the allocator will look something like this: 204 | 205 | Buddy allocator after 32 K allocation: 206 | 207 | ----------------------------------------------------------------- 208 | 512 | S | 209 | ----------------------------------------------------------------- 210 | 256 | S | F | 211 | ----------------------------------------------------------------- 212 | 128 | S | F | 213 | --------------------------------- 214 | 64 | S | F | S - split 215 | ----------------- F - free 216 | 32 | A | F | A - allocated 217 | --------- 218 | 219 | As you can see, this method of splitting means that the block sizes will 220 | always be a powers of two. If you try to allocate something smaller, say 13 K, 221 | the allocation will be rounded up to the nearest power of two (16 K) and then 222 | get assigned a 16 K block. 223 | 224 | So there is a significant amount of fragmentation happening here. This kind of 225 | fragmentation is called internal fragmentation since it is wasted memory 226 | inside a block, not wasted space between the blocks. 227 | 228 | Merging in the buddy allocator is dead simple. Whenever a block is freed, we 229 | check if it's buddy is also free. If it is, we merge the two buddies back 230 | together into the single block they were once split from. We continue to do 231 | this recursively, so if this newly created free block also has a free buddy, 232 | they get merged together into an even bigger block, etc. 233 | 234 | The buddy allocator is pretty good at preventing external fragmentation, since 235 | whenever something is freed there is a pretty good chance that we can merge, 236 | and if we can't the "hole" should be filled pretty soon by a similarly sized 237 | allocation. You can still imagine pathological worst-case scenarios. For 238 | example, if we first allocate every leaf node and then free every other of 239 | those allocations we would end up with a pretty fragmented memory. But such 240 | situations should be rare in practice. 241 | 242 | Worst case fragmentation, 16 K block size 243 | 244 | ----------------------------------------------------------------- 245 | 512 | S | 246 | ----------------------------------------------------------------- 247 | 256 | S | S | 248 | ----------------------------------------------------------------- 249 | 128 | S | S | S | S | 250 | ----------------------------------------------------------------- 251 | 64 | S | S | S | S | S | S | S | S | 252 | ----------------------------------------------------------------- 253 | 32 | S | S | S | S | S | S | S | S | S | S | S | S | S | S | S | S | 254 | ----------------------------------------------------------------- 255 | 16 |A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F|A|F| 256 | ----------------------------------------------------------------- 257 | 258 | I'm being pretty vague here, I know. That's because it is quite hard in 259 | general to say something meaningful about how "good" an allocator is at 260 | preventing fragmentation. You can say how good it does with a certain 261 | allocation pattern, but every program has a different allocation pattern. 262 | 263 | Implementing the buddy allocator 264 | -------------------------------- 265 | 266 | Articles on algorithms and data structures are often light on implementation 267 | details. For example, you can find tons of articles describing the high-level 268 | idea behind the buddy allocator as I've outlined it above, but not much 269 | information about how to implement the bloody thing! 270 | 271 | This is a pity, because the implementation details can really matter. For 272 | example, it's not uncommon to see someone carefully implement the 273 | A*-algorithm, but using a data structure for the open and closed sets that 274 | completely obliterates the performance advantages of the algorithm. 275 | 276 | So let's get into a bit more detail. 277 | 278 | We start with allocation. How can we find a free block of a requested size? We 279 | can use the technique described above: we put the free blocks of each size in 280 | an implicit linked list. To find a free block we just take the first item 281 | from the list of blocks of that size, remove it from the list and return it. 282 | 283 | If there is no block of the right size, we take the block of the next higher 284 | size and split that. We use one of the two blocks we get and put the other one 285 | on the free list for that size. If the list of blocks of the bigger size is 286 | also empty, we can go to the even bigger size, etc. 287 | 288 | To make things easier for us, let's introduce the concept of levels. We say 289 | that the single block that we start with, representing the entire buffer, is 290 | at level 0. When we split that we get two blocks at level 1. Splitting them, 291 | we get to level 2, etc. 292 | 293 | We can now write the pseudocode for allocating a block at level n: 294 | 295 | if the list of free blocks at level n is empty 296 | allocate a block at level n-1 (using this algorithm) 297 | split the block into two blocks at level n 298 | insert the two blocks into the list of free blocks for level n 299 | remove the first block from the list at level n and return it 300 | 301 | The only data structure we need for this is a list of pointers to the first 302 | free block at each level: 303 | 304 | static const int MAX_LEVELS = 32; 305 | void *_free_lists[MAX_LEVELS]; 306 | 307 | The prev and next pointers for the lists are stored directly in the free 308 | blocks themselves. 309 | 310 | We can also note some mathematical properties of the allocator: 311 | 312 | total_size == (1< 0 434 | if block_has_been_split(ptr, n-1) 435 | return n 436 | n = n - 1 437 | return 0 438 | 439 | Since the leaf blocks can't be split, we only need 1 << (num_levels - 1) 440 | entries in the split index. This means that the cost of the split index is the 441 | same as for the merge index, 0.5 bits per block. It's a bit amazing that 442 | we can do all this with a total overhead of just 1 bit per block. 443 | 444 | The prize of the memory savings is that we now have to loop a bit to find the 445 | allocated size. But num_levels is usually small (in any case <= 32) and since 446 | we only have 1 bit per entry the cache usage is pretty good. Furthermore, with 447 | this approach it is easy to offer both a free(void *) and a 448 | free(void *, size_t) interface. The latter can be used by more sophisticated 449 | callers to avoid the loop to calculate the block size. 450 | 451 | Memory arrangements 452 | ------------------- 453 | 454 | Where do we store this 1 bit of metadata per block? We could use a separate 455 | buffer, but it is not that elegant. It would mean that our allocator would 456 | have to request two buffers from the system, one for the data and one for the 457 | metadata. 458 | 459 | Instead, let's just put the metadata in the buffer itself, at the beginning 460 | where we can easily find it. We mark the blocks used to store the metadata as 461 | allocated so that they won't be used by other allocations: 462 | 463 | Initial state of memory after reserving metadata: 464 | 465 | ----------------------------------------------------------------- 466 | 512 | S | 467 | ----------------------------------------------------------------- 468 | 256 | S | F | 469 | ----------------------------------------------------------------- 470 | 128 | S | F | 471 | --------------------------------- 472 | 64 | S | S | 473 | ----------------- 474 | 32 | S | S | S | F | 475 | ----------------- 476 | 16 |A|A|A|A|A|F| 477 | ------------- 478 | ********** Metadata 479 | 480 | Note that when allocating the metadata we can be a bit sneaky and not round up 481 | the allocation to the nearest power of two. Instead we just take as many leaf 482 | blocks as we need. That is because when we allocate the metadata we know that 483 | the allocator is completely empty, so we are guaranteed to be able to allocate 484 | adjacent leaf blocks. In the example above we only have to use 5 * 16 = 80 K 485 | for the metadata instead of the 128 K we would have used if we rounded up. 486 | 487 | (The size of the metadata has been greatly exaggerated in the illustration 488 | above to show this effect. In reality, since the tree in the illustration has 489 | only six levels, the metadata is just 1 * (1 << 6) = 64 bits, that's 8 bytes, 490 | not 80 K.) 491 | 492 | Note that you have to be a bit careful when allocating the metadata in this 493 | way, because you are allocating memory for the metadata that your memory 494 | allocation functions depend on. That's a chicken-and-egg problem. Either 495 | you have to write a special allocation routine for this initial allocation, 496 | or be very careful with how you write your allocation code so that this case 497 | is handled gracefully. 498 | 499 | We can use the same technique to handle another pesky issue. 500 | 501 | It's a bit irritating that the size of the buddy allocator has to be a power 502 | of two of the leaf size. Say that we happen to have 400 K of memory lying 503 | around somewhere. It would be really nice if we could use all of that memory 504 | instead of just the first 256 K. 505 | 506 | We can do that using the same trick. For our 400 K, we can just create a 512 K 507 | buddy allocator and mark the first 144 K of it as "already allocated". We also 508 | offset the start of the buffer, so that the start of the usable memory 509 | coincides with the start of the buffer in memory. Like this: 510 | 511 | ----------------------------------------------------------------- 512 | 512 | S | 513 | ----------------------------------------------------------------- 514 | 256 | S | F | 515 | ----------------------------------------------------------------- 516 | 128 | S | S | 517 | --------------------------------- 518 | 64 | S | S | S | F | 519 | --------------------------------- 520 | 32 | S | S | S | S | S | F | 521 | ------------------------- 522 | 16 |A|A|A|A|A|A|A|A|A|A| 523 | --------------------- 524 | ******************* Unusable, unallocated memory 525 | MET * Metadata 526 | ^ 527 | +-- Usable memory starts here 528 | 529 | Again, this requires some care when writing the code that does the initial 530 | allocation so that it doesn't write into the unallocated memory and causes an access violation. 531 | -------------------------------------------------------------------------------- /doc/buddy-alloc-math.md: -------------------------------------------------------------------------------- 1 | 2 | Buddy Allocator Math: 3 | ========= 4 | 5 | memory_size = size in bytes of power of 2 memory area 6 | memory_address = address of memory aligned on a boundary of it size 7 | pointer_size = sizeof(void *) 8 | x * (2 ^ y) ==> x << y 9 | x / (2 ^ y) ==> x >> y 10 | number_bits = 8 * sizeof(pointer_size) 11 | bits_base = log2(number_of_bits) 12 | leaf_size = 2 * pointer_size 13 | total_levels = log2(memory_size) 14 | max_levels = log2(memory_size) - log2(leaf_size) 15 | max_indexes = 16 | (2 ^ max_levels) - 1 => 17 | (1 << max_levels) - 1 or (total_size / leaf_size) - 1 18 | block_indexes = ((max_indexes / number_of_bits) / 2) + 1 19 | total_size = 20 | 2 ^ total_levels => 21 | (2 ^ max_level) * leaf_size 22 | size_of_level(level) = 23 | total_size / (2 ^ level) => 24 | total_size >> level 25 | max_blocks_in_level(level) = 26 | 2 ^ level => 27 | 1 << level 28 | index_of(level) = 29 | (2 ^ level) - 1 => 30 | (1 << level) - 1 31 | index_offset_in_level(ptr, level) = 32 | (ptr - memory_address) / size_of_level(level) => 33 | (ptr - memory_address) / ((2 ^ total_levels) / 2 ^ level) => 34 | (ptr - memory_address) / 2 ^ (total_levels - level) => 35 | (ptr - memory_address) >> (total_levels - level) 36 | index(ptr, level) = index_of(level) + index_offset_in_level(ptr, level) 37 | address_from_level_offset(level_offset, level) = 38 | level_offset * size_of_level(level) + memory_address => 39 | level_offset * ((2 ^ total_levels) / (2 ^ level) + memory_address => 40 | level_offset * (2 ^ (total_levels - level)) + memory_address => 41 | level_offset << (total_levels - level) + memory_address 42 | address_from_level(index, level) = (index - index_of(level)) * size_of(level) + memory_address; 43 | address_from_index(index) = address_from_level(index, floor(log2(index + 1))) 44 | 45 | -------------------------------------------------------------------------------- /include/buddy-alloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Stephen Street 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef BUDDY_ALLOC_H_ 10 | #define BUDDY_ALLOC_H_ 11 | 12 | #include 13 | #include 14 | 15 | /* Uncomment this if the memory region for the allocator is aligned on a address boundary equal to the size to 16 | * enable a minor optimization when calculating the address of the buddy. 17 | #define BUDDY_MEMORY_ALIGNED_ON_SIZE 18 | */ 19 | 20 | #define BUDDY_ILOG2(value) ((BUDDY_NUM_BITS - 1UL) - __builtin_clzl(value)) 21 | #define BUDDY_NUM_BITS (8 * sizeof(unsigned long int)) 22 | #define BUDDY_MIN_LEAF_SIZE (sizeof(void *) << 1UL) 23 | #define BUDDY_LEAF_LEVEL_OFFSET (BUDDY_ILOG2(BUDDY_MIN_LEAF_SIZE)) 24 | #define BUDDY_MAX_LEVELS(total_size, min_size) (BUDDY_ILOG2(total_size) - BUDDY_ILOG2(min_size)) 25 | #define BUDDY_MAX_INDEXES(total_size, min_size) (1UL << (BUDDY_MAX_LEVELS(total_size, min_size) + 1)) 26 | #define BUDDY_BLOCK_INDEX_SIZE(total_size, min_size) ((BUDDY_MAX_INDEXES(total_size, min_size) + (BUDDY_NUM_BITS - 1)) / BUDDY_NUM_BITS) 27 | 28 | typedef struct buddy_block_info 29 | { 30 | struct buddy_block_info *next; 31 | struct buddy_block_info *prev; 32 | } buddy_block_info_t; 33 | 34 | typedef struct buddy_allocator 35 | { 36 | void *address; 37 | size_t size; 38 | unsigned long int min_allocation; 39 | unsigned long int max_indexes; 40 | unsigned long int total_levels; 41 | unsigned long int max_level; 42 | buddy_block_info_t *free_blocks; 43 | unsigned long int *block_index; 44 | void *extra_metadata; 45 | } buddy_allocator_t; 46 | 47 | #define buddy_sizeof_metadata(total_size, min_size) (sizeof(buddy_allocator_t) + \ 48 | (sizeof(buddy_block_info_t) * (BUDDY_MAX_LEVELS(total_size, min_size) + 1)) + \ 49 | BUDDY_BLOCK_INDEX_SIZE(total_size, min_size) * (BUDDY_NUM_BITS >> 3)) 50 | 51 | #define BUDDY_DECLARE_ALLOCATOR(name, size) unsigned long int name ## _metadata[buddy_sizeof_metadata(size, BUDDY_MIN_LEAF_SIZE)/ sizeof(unsigned long int)]; \ 52 | buddy_allocator_t * name = (buddy_allocator_t *)name ## _metadata 53 | 54 | 55 | void buddy_init(buddy_allocator_t *allocator, void *address, size_t size); 56 | buddy_allocator_t *buddy_create(void *address, size_t size); 57 | void *buddy_alloc(buddy_allocator_t *allocator, size_t size); 58 | void buddy_release(buddy_allocator_t *allocator, void *ptr, size_t size); 59 | void buddy_free(buddy_allocator_t *allocator, void *ptr); 60 | 61 | size_t buddy_largest_available(const buddy_allocator_t *allocator); 62 | size_t buddy_available(const buddy_allocator_t *allocator); 63 | size_t buddy_used(const buddy_allocator_t *allocator); 64 | 65 | #endif /* BUDDY_ALLOC_H_ */ 66 | -------------------------------------------------------------------------------- /samples/sample-allocator.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Stephen Street 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | 11 | #define MEMORY_REGION_SIZE 1048576 12 | 13 | static unsigned long int memory_region[MEMORY_REGION_SIZE / sizeof(unsigned long int)]; 14 | 15 | int main(int argc, char **argv) 16 | { 17 | /* Declare allocator for external metadata */ 18 | BUDDY_DECLARE_ALLOCATOR(allocator, MEMORY_REGION_SIZE); 19 | void *ptr; 20 | 21 | /* Create an allocate with external metadata */ 22 | buddy_init(allocator, memory_region, MEMORY_REGION_SIZE); 23 | 24 | /* This will allocate a block 16K in size */ 25 | ptr = buddy_alloc(allocator, 13773); 26 | 27 | /* This will determine the size of the memory block, by searching the 28 | free index at each level, somewhat slower */ 29 | buddy_free(allocator, ptr); 30 | 31 | /* This will allocate a block 16K in size */ 32 | ptr = buddy_alloc(allocator, 13773); 33 | 34 | /* This frees the memory block, using the provided size, a little 35 | faster as the block level can be derived from the size */ 36 | buddy_release(allocator, ptr, 13773); 37 | 38 | /* All done */ 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /samples/samples.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | ifeq ($(findstring ${BUILD_ROOT},${CURDIR}),) 10 | include ${PROJECT_ROOT}/tools/makefiles/target.mk 11 | else 12 | 13 | EXTRA_DEPS += ${BUILD_ROOT}/buddy-alloc/libbuddy-alloc.a 14 | 15 | EXEC := sample-allocator 16 | 17 | include ${PROJECT_ROOT}/tools/makefiles/project.mk 18 | 19 | CPPFLAGS += -I ${SOURCE_DIR}/../include 20 | LDFLAGS += -L ${BUILD_ROOT}/buddy-alloc 21 | LDLIBS += -lbuddy-alloc 22 | 23 | endif 24 | -------------------------------------------------------------------------------- /samples/subdir.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | where-am-i := $(lastword ${MAKEFILE_LIST}) 10 | 11 | SRC += $(wildcard $(dir $(where-am-i))*.c) 12 | SRC += $(wildcard $(dir $(where-am-i))*.S) 13 | SRC += $(wildcard $(dir $(where-am-i))*.s) 14 | -------------------------------------------------------------------------------- /tests/basic/basic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Stephen Street 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | 11 | #include 12 | 13 | #define BUDDY_MEMORY_SIZE 512 14 | #define BIT_ARRAY_NUM_BITS (8 * sizeof(unsigned long int)) 15 | #define BIT_ARRAY_INDEX_SHIFT (__builtin_ctzl(BIT_ARRAY_NUM_BITS)) 16 | #define BIT_ARRAY_INDEX_MASK (BIT_ARRAY_NUM_BITS - 1UL) 17 | 18 | static unsigned char __attribute__((aligned(BUDDY_MEMORY_SIZE))) memory[BUDDY_MEMORY_SIZE]; 19 | 20 | static unsigned long int metadata[buddy_sizeof_metadata(BUDDY_MEMORY_SIZE, BUDDY_MIN_LEAF_SIZE) / sizeof(unsigned long)]; 21 | static buddy_allocator_t *global_allocator = (buddy_allocator_t *)metadata; 22 | 23 | static inline unsigned long int bta_first_of_level(unsigned long int level) 24 | { 25 | return (1 << level) - 1; 26 | } 27 | 28 | static inline unsigned long int bta_last_of_level(unsigned long int level) 29 | { 30 | return (1 << (level + 1)) - 1; 31 | } 32 | 33 | static inline bool bit_array_is_set(const unsigned long int *bit_array, unsigned long int index) 34 | { 35 | return (bit_array[index >> BIT_ARRAY_INDEX_SHIFT] & (1UL << (index & BIT_ARRAY_INDEX_MASK))) != 0; 36 | } 37 | 38 | static inline unsigned long int index_of(const buddy_allocator_t *allocator, const void *ptr, unsigned long int level) 39 | { 40 | return (1UL << level) + ((ptr - allocator->address) >> (allocator->total_levels - level)) - 1UL; 41 | } 42 | 43 | static inline unsigned long int free_index(const buddy_allocator_t *allocator, unsigned long int index) 44 | { 45 | return (index - 1) >> 1; 46 | } 47 | 48 | static inline unsigned long int split_index(const buddy_allocator_t *allocator, unsigned long int index) 49 | { 50 | return index + (allocator->max_indexes >> 1); 51 | } 52 | 53 | static void buddy_dump_free_blocks(const buddy_allocator_t *allocator) 54 | { 55 | char buffer[128]; 56 | 57 | /* Dump the free lists */ 58 | printf("free blocks:\n"); 59 | for (unsigned long int level = 0; level < allocator->max_level + 1; ++level) { 60 | printf(buffer, "\t%6zu: ", allocator->size >> level); 61 | for (buddy_block_info_t *cursor = allocator->free_blocks[level].next; cursor != &allocator->free_blocks[level]; cursor = cursor->next) { 62 | printf(buffer, "%p(%lu) ", cursor, index_of(allocator, cursor, level)); 63 | } 64 | printf("\n"); 65 | } 66 | printf("\n"); 67 | } 68 | 69 | static void buddy_dump_free_index(const buddy_allocator_t *allocator) 70 | { 71 | char buffer[128]; 72 | 73 | printf("free index:\n"); 74 | 75 | /* Loop through the levels */ 76 | for (unsigned long int level = 0; level < allocator->max_level; ++level) { 77 | 78 | /* Loop through the indexes at this level */ 79 | printf(buffer, "\t%6u - %4lu:%-4lu: ", allocator->size >> (level + 1), bta_first_of_level(level + 1), bta_last_of_level(level + 1) - 1); 80 | for (unsigned long int index = bta_first_of_level(level); index < bta_last_of_level(level); ++index) 81 | printf(bit_array_is_set(allocator->block_index, index) ? "1" : "0"); 82 | printf("\n"); 83 | } 84 | printf("\n"); 85 | } 86 | 87 | static void buddy_dump_split_index(const buddy_allocator_t *allocator) 88 | { 89 | char buffer[128]; 90 | 91 | printf("split index:\n"); 92 | 93 | /* Loop through the levels */ 94 | for (unsigned long int level = 0; level < allocator->max_level; ++level) { 95 | 96 | /* Loop through the indexes at this level */ 97 | printf(buffer, "\t%6u - %4lu:%-4lu: ", allocator->size >> level, bta_first_of_level(level), bta_last_of_level(level) - 1); 98 | for (unsigned long int index = bta_first_of_level(level); index < bta_last_of_level(level); ++index) 99 | printf(bit_array_is_set(allocator->block_index, split_index(allocator, index)) ? "1" : "0"); 100 | printf("\n"); 101 | } 102 | printf("\n"); 103 | } 104 | 105 | static void buddy_dump_info(const buddy_allocator_t *allocator) 106 | { 107 | char buffer[128]; 108 | 109 | printf(buffer, "allocator @ %p\n", allocator); 110 | printf(buffer, "\taddress: %p\n", allocator->address); 111 | printf(buffer, "\tsize: %zu\n", allocator->size); 112 | printf(buffer, "\ttotal levels: %lu\n", allocator->total_levels); 113 | printf(buffer, "\tmax level: %lu\n", allocator->max_level); 114 | printf(buffer, "\tmax allocation: %zu\n", allocator->size); 115 | printf(buffer, "\tmin allocation: %lu\n", allocator->min_allocation); 116 | printf(buffer, "\tmax indexes: %lu\n", allocator->max_indexes); 117 | printf(buffer, "\tavailable: %zu\n", buddy_available(allocator)); 118 | printf(buffer, "\tused: %zu\n", buddy_used(allocator)); 119 | printf(buffer, "\tmax available: %zu\n", buddy_largest_available(allocator)); 120 | } 121 | 122 | static void buddy_dump_allocator(const buddy_allocator_t *allocator) 123 | { 124 | buddy_dump_info(allocator); 125 | buddy_dump_free_blocks(allocator); 126 | buddy_dump_split_index(allocator); 127 | buddy_dump_free_index(allocator); 128 | } 129 | 130 | int main(int argc, char **argv) 131 | { 132 | printf("test external metadata allocator\n"); 133 | buddy_init(global_allocator, memory, BUDDY_MEMORY_SIZE); 134 | printf("starting state\n"); 135 | buddy_dump_allocator(global_allocator); 136 | 137 | void *ptr_1 = buddy_alloc(global_allocator, 32); 138 | void *ptr_2 = buddy_alloc(global_allocator, 31); 139 | void *ptr_3 = buddy_alloc(global_allocator, 33); 140 | void *ptr_4 = buddy_alloc(global_allocator, 8); 141 | 142 | printf("outstanding blocks\n"); 143 | buddy_dump_allocator(global_allocator); 144 | 145 | buddy_free(global_allocator, ptr_3); 146 | buddy_free(global_allocator, ptr_2); 147 | buddy_free(global_allocator, ptr_1); 148 | buddy_free(global_allocator, ptr_4); 149 | printf("terminating state\n"); 150 | buddy_dump_allocator(global_allocator); 151 | 152 | printf("testing block sized allocations\n"); 153 | void *ptrs[6]; 154 | for (int i = 1; i < 6; ++i) { 155 | ptrs[i] = buddy_alloc(global_allocator, BUDDY_MEMORY_SIZE >> i); 156 | } 157 | for (int i = 1; i < 6; ++i) { 158 | buddy_free(global_allocator, ptrs[i]); 159 | } 160 | printf("terminating state\n"); 161 | buddy_dump_allocator(global_allocator); 162 | 163 | printf("testing internal metadata allocator\n"); 164 | buddy_allocator_t *allocator = buddy_create(memory, BUDDY_MEMORY_SIZE); 165 | printf("starting state\n"); 166 | buddy_dump_allocator(allocator); 167 | 168 | ptr_1 = buddy_alloc(allocator, 32); 169 | ptr_2 = buddy_alloc(allocator, 31); 170 | ptr_3 = buddy_alloc(allocator, 33); 171 | ptr_4 = buddy_alloc(allocator, 8); 172 | printf("outstanding blocks\n"); 173 | buddy_dump_allocator(allocator); 174 | 175 | buddy_free(allocator, ptr_3); 176 | buddy_free(allocator, ptr_2); 177 | buddy_free(allocator, ptr_1); 178 | buddy_free(allocator, ptr_4); 179 | printf("terminating state\n"); 180 | buddy_dump_allocator(allocator); 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /tests/basic/basic.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | ifeq ($(findstring ${BUILD_ROOT},${CURDIR}),) 10 | include ${PROJECT_ROOT}/tools/makefiles/target.mk 11 | else 12 | 13 | EXTRA_DEPS += ${BUILD_ROOT}/buddy-alloc/libbuddy-alloc.a 14 | 15 | EXEC := basic 16 | 17 | include ${PROJECT_ROOT}/tools/makefiles/project.mk 18 | 19 | CPPFLAGS += -DSTATIC_CONFIG -I ${SOURCE_DIR}/../../include 20 | LDFLAGS += -L ${BUILD_ROOT}/buddy-alloc 21 | LDLIBS += -lbuddy-alloc 22 | 23 | endif 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/basic/subdir.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | where-am-i := $(lastword ${MAKEFILE_LIST}) 10 | 11 | SRC += $(wildcard $(dir $(where-am-i))*.c) 12 | SRC += $(wildcard $(dir $(where-am-i))*.S) 13 | SRC += $(wildcard $(dir $(where-am-i))*.s) 14 | -------------------------------------------------------------------------------- /tests/sim/sim.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Stephen Street 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #define SIM_MEMORY_SIZE (1024 * 1024) 18 | #define SIM_MAX_TIME 10000000 19 | 20 | #define SIM_RAND_SEED 0x01371730UL 21 | 22 | #define SIM_MAX_ALLOC_SIZE (100 * 1024) 23 | #define SIM_MAX_DELAY (5) 24 | 25 | static unsigned long int memory[SIM_MEMORY_SIZE / sizeof(unsigned long int)]; 26 | static buddy_allocator_t *allocator; 27 | 28 | typedef struct sim_data { 29 | int mark; 30 | int expire; 31 | int size; 32 | struct sim_data *next; 33 | unsigned char data[]; 34 | } sim_data_t; 35 | 36 | static void buddy_dump_info(const buddy_allocator_t *allocator) 37 | { 38 | char buffer[128]; 39 | 40 | printf(buffer, "allocator @ %p\n", allocator); 41 | printf(buffer, "\taddress: %p\n", allocator->address); 42 | printf(buffer, "\tsize: %zu\n", allocator->size); 43 | printf(buffer, "\ttotal levels: %lu\n", allocator->total_levels); 44 | printf(buffer, "\tmax level: %lu\n", allocator->max_level); 45 | printf(buffer, "\tmax allocation: %zu\n", allocator->size); 46 | printf(buffer, "\tmin allocation: %lu\n", allocator->min_allocation); 47 | printf(buffer, "\tmax indexes: %lu\n", allocator->max_indexes); 48 | printf(buffer, "\tavailable: %zu\n", buddy_available(allocator)); 49 | printf(buffer, "\tused: %zu\n", buddy_used(allocator)); 50 | printf(buffer, "\tmax available: %zu\n", buddy_largest_available(allocator)); 51 | } 52 | 53 | int main(int argc, char **argv) 54 | { 55 | int mark; 56 | int new_size; 57 | int new_delay; 58 | sim_data_t *datum; 59 | sim_data_t *sim_data[SIM_MAX_DELAY]; 60 | int sim_data_mark; 61 | int outstanding = 0; 62 | 63 | printf("simulator testing of allocator: %lu overhead %1.2f%%\n", buddy_sizeof_metadata(SIM_MEMORY_SIZE, BUDDY_MIN_LEAF_SIZE), ((buddy_sizeof_metadata(SIM_MEMORY_SIZE, BUDDY_MIN_LEAF_SIZE) * 1.0) / SIM_MEMORY_SIZE) * 100); 64 | allocator = buddy_create(memory, SIM_MEMORY_SIZE); 65 | buddy_dump_info(allocator); 66 | 67 | srand(SIM_RAND_SEED + time(0)); 68 | memset(sim_data, 0, sizeof(sim_data)); 69 | 70 | /* Run the simulation */ 71 | for (mark = 0; mark < SIM_MAX_TIME; ++mark) { 72 | 73 | new_size = sizeof(sim_data_t) + (rand() % (SIM_MAX_ALLOC_SIZE - sizeof(sim_data_t))); 74 | new_delay = (rand() % SIM_MAX_DELAY); 75 | 76 | sim_data_mark = mark % SIM_MAX_DELAY; 77 | 78 | datum = buddy_alloc(allocator, new_size); 79 | if (datum) { 80 | ++outstanding; 81 | datum->mark = mark; 82 | datum->expire = mark + new_delay; 83 | datum->size = new_size; 84 | datum->next = sim_data[(sim_data_mark + new_delay) % SIM_MAX_DELAY]; 85 | sim_data[(sim_data_mark + new_delay) % SIM_MAX_DELAY] = datum; 86 | } else 87 | printf("fail at %u for size %d with %zu available and largest %zu\n", mark, new_size, buddy_available(allocator), buddy_largest_available(allocator)); 88 | 89 | while ((datum = sim_data[sim_data_mark]) != 0) { 90 | sim_data[sim_data_mark] = sim_data[sim_data_mark]->next; 91 | buddy_free(allocator, datum); 92 | --outstanding; 93 | } 94 | } 95 | 96 | for (int i = 0; i < SIM_MAX_DELAY; ++i) { 97 | while ((datum = sim_data[i]) != 0) { 98 | sim_data[i] = sim_data[i]->next; 99 | buddy_free(allocator, datum); 100 | --outstanding; 101 | } 102 | } 103 | 104 | printf("lost: %zu bytes, outstanding: %d\n", buddy_used(allocator), outstanding); 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /tests/sim/sim.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | ifeq ($(findstring ${BUILD_ROOT},${CURDIR}),) 10 | include ${PROJECT_ROOT}/tools/makefiles/target.mk 11 | else 12 | 13 | EXTRA_DEPS += ${BUILD_ROOT}/buddy-alloc/libbuddy-alloc.a 14 | 15 | EXEC := sim 16 | 17 | include ${PROJECT_ROOT}/tools/makefiles/project.mk 18 | 19 | CPPFLAGS += -I ${SOURCE_DIR}/../../include 20 | LDFLAGS += -L ${BUILD_ROOT}/buddy-alloc 21 | LDLIBS += -lbuddy-alloc 22 | 23 | endif 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/sim/subdir.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | where-am-i := $(lastword ${MAKEFILE_LIST}) 10 | 11 | SRC += $(wildcard $(dir $(where-am-i))*.c) 12 | SRC += $(wildcard $(dir $(where-am-i))*.S) 13 | SRC += $(wildcard $(dir $(where-am-i))*.s) 14 | -------------------------------------------------------------------------------- /tests/tests.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | targets: basic sim 10 | 11 | include ${TOOLS_ROOT}/makefiles/tree.mk 12 | -------------------------------------------------------------------------------- /tools/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-rocket-computing/buddy-alloc/b5ad48f23f29d77c4a4b314b6baeb37529013c61/tools/.gitignore -------------------------------------------------------------------------------- /tools/c-copyright-template: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Stephen Street 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ -------------------------------------------------------------------------------- /tools/makefiles/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/red-rocket-computing/buddy-alloc/b5ad48f23f29d77c4a4b314b6baeb37529013c61/tools/makefiles/.gitignore -------------------------------------------------------------------------------- /tools/makefiles/project.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | SUBDIRS ?= $(subst ${SOURCE_DIR}/,,$(shell $(FIND) ${SOURCE_DIR} -mindepth 2 -name "subdir.mk" -printf "%h ")) 10 | SRC := 11 | 12 | include ${PROJECT_ROOT}/Makefile.common 13 | include $(patsubst %, ${SOURCE_DIR}/%/subdir.mk, ${SUBDIRS}) 14 | -include ${SOURCE_DIR}/subdir.mk 15 | 16 | OBJ := $(patsubst %.c,%.o,$(filter %.c,${SRC})) $(patsubst %.cpp,%.o,$(filter %.cpp,${SRC})) $(patsubst %.s,%.o,$(filter %.s,${SRC})) $(patsubst %.S,%.o,$(filter %.S,${SRC})) 17 | OBJ := $(subst ${PROJECT_ROOT},${BUILD_ROOT},${OBJ}) 18 | 19 | #$(info SUBDIRS=${SUBDIRS}) 20 | #$(info SRC=${SRC}) 21 | #$(info OBJ=${OBJ}) 22 | #$(info SOURCE_DIR=${SOURCE_DIR}) 23 | #$(info TARGET=${TARGET}) 24 | #$(info TARGET=$(addprefix ${CURDIR}/,${TARGET})) 25 | #$(info CURDIR=${CURDIR}) 26 | 27 | .SECONDARY: 28 | 29 | all: ${SUBDIRS} $(addprefix ${CURDIR}/,${TARGET}) $(addprefix ${CURDIR}/,${EXEC}) 30 | 31 | clean: ${SUBDIRS} 32 | @echo "CLEANING ${TARGET}" 33 | $(Q)${RM} ${TARGET} $(basename ${TARGET}).img $(basename ${TARGET}).bin $(basename ${TARGET}).elf $(basename ${TARGET}).map $(basename ${TARGET}).smap ${OBJ} ${OBJ:%.o=%.d} ${OBJ:%.o=%.dis} 34 | 35 | ${SUBDIRS}: 36 | $(Q)mkdir -p ${CURDIR}/$@ 37 | 38 | ${CURDIR}/%.a: ${OBJ} ${EXTRA_DEPS} 39 | @echo "ARCHIVING $@" 40 | $(Q)$(AR) ${ARFLAGS} $@ ${OBJ} 41 | 42 | ${CURDIR}/${EXEC}: ${OBJ} ${EXTRA_DEPS} 43 | @echo "LINKING $@" 44 | $(Q)$(CC) ${LDFLAGS} ${LOADLIBES} -o $@ ${OBJ} ${LDLIBS} 45 | 46 | ${CURDIR}/%.elf: ${OBJ} ${EXTRA_DEPS} 47 | @echo "LINKING $@" 48 | $(Q)$(CC) ${LDFLAGS} ${LOADLIBES} -o $@ ${OBJ} ${LDLIBS} 49 | ${CURDIR}/%.o: ${SOURCE_DIR}/%.c 50 | @echo "COMPILING $<" 51 | $(Q)$(CC) ${CPPFLAGS} ${CFLAGS} -MMD -MP -c -o $@ $< 52 | 53 | ${CURDIR}/%.o: ${SOURCE_DIR}/%.cpp 54 | @echo "COMPILING $<" 55 | $(Q)$(CXX) ${CPPFLAGS} ${CXXFLAGS} -MMD -MP -c -o $@ $< 56 | 57 | ${CURDIR}/%.o: ${SOURCE_DIR}/%.s 58 | @echo "ASSEMBLING $<" 59 | $(Q)$(AS) ${ASFLAGS} -o $@ $< 60 | 61 | ${CURDIR}/%.o: ${SOURCE_DIR}/%.S 62 | @echo "ASSEMBLING $<" 63 | $(Q)$(CC) ${CPPFLAGS} ${ASFLAGS} -c -o $@ $< 64 | 65 | ifneq (${MAKECMDGOALS},clean) 66 | -include ${OBJ:.o=.d} 67 | endif 68 | -------------------------------------------------------------------------------- /tools/makefiles/target.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | .SUFFIXES: 10 | 11 | include ${PROJECT_ROOT}/Makefile.common 12 | 13 | ifndef BUILD_PATH 14 | BUILD_PATH := $(subst ${PROJECT_ROOT},${BUILD_ROOT},${CURDIR}) 15 | endif 16 | 17 | ${BUILD_PATH}: 18 | +$(Q)[ -d $@ ] || mkdir -p $@ 19 | +$(Q)${MAKE} --no-print-directory -C $@ -f ${CURDIR}/$(lastword $(subst /, ,${CURDIR})).mk SOURCE_DIR=${CURDIR} ${MAKECMDGOALS} 20 | .PHONY: ${BUILD_PATH} 21 | 22 | Makefile : ; 23 | %.mk :: ; 24 | 25 | % :: ${BUILD_PATH} ; @: 26 | -------------------------------------------------------------------------------- /tools/makefiles/tree.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | 9 | where-am-i := ${CURDIR}/$(lastword $(subst $(lastword ${MAKEFILE_LIST}),,${MAKEFILE_LIST})) 10 | 11 | include ${PROJECT_ROOT}/Makefile.common 12 | 13 | BUILD_PATH := $(subst ${PROJECT_ROOT},${BUILD_ROOT},${CURDIR}) 14 | SUBDIRS ?= $(subst ${CURDIR}/,,$(shell $(FIND) ${CURDIR} -mindepth 2 -maxdepth 2 -name "*.mk" -and -not -name "subdir.mk" -printf "%h ")) 15 | 16 | all: targets 17 | 18 | clean: targets 19 | 20 | #$(info SUBDIR=${SUBDIRS}) 21 | 22 | ${SUBDIRS}: 23 | @echo "ENTERING $@" 24 | $(Q)${MAKE} --no-print-directory -C $@ -f $@.mk ${MAKECMDGOALS} 25 | .PHONY: ${SUBDIRS} 26 | -------------------------------------------------------------------------------- /tools/script-copyright-template: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Stephen Street 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | --------------------------------------------------------------------------------