| 199 | #endif |
| 200 | |
| 201 | static struct buffer_address ggml_dyn_tallocr_alloc(struct ggml_dyn_tallocr * alloc, size_t size, const struct ggml_tensor * tensor) { |
| 202 | size = aligned_offset(NULL, size, alloc->alignment); |
| 203 | |
| 204 | AT_PRINTF("%s: allocating %s (%zu bytes) - ", __func__, tensor->name, size); |
| 205 | |
| 206 | int best_fit_chunk = -1; |
| 207 | int best_fit_block = -1; |
| 208 | size_t max_avail = 0; |
| 209 | |
| 210 | // find the best fitting free block besides the last block, within any chunk |
| 211 | for (int c = 0; c < alloc->n_chunks; ++c) { |
| 212 | struct tallocr_chunk * chunk = alloc->chunks[c]; |
| 213 | size_t best_fit_size = SIZE_MAX; |
| 214 | for (int i = 0; i < chunk->n_free_blocks - 1; i++) { |
| 215 | struct free_block * block = &chunk->free_blocks[i]; |
| 216 | max_avail = MAX(max_avail, block->size); |
| 217 | if (block->size >= size && block->size <= best_fit_size) { |
| 218 | best_fit_chunk = c; |
| 219 | best_fit_block = i; |
| 220 | best_fit_size = block->size; |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | if (best_fit_block == -1) { |
| 226 | // no suitable block found, try the last block (this may grow a chunks size) |
| 227 | int64_t best_reuse = INT64_MIN; |
| 228 | for (int c = 0; c < alloc->n_chunks; ++c) { |
| 229 | struct tallocr_chunk * chunk = alloc->chunks[c]; |
| 230 | if (chunk->n_free_blocks > 0) { |
| 231 | struct free_block * block = &chunk->free_blocks[chunk->n_free_blocks - 1]; |
| 232 | max_avail = MAX(max_avail, block->size); |
| 233 | int64_t reuse_factor = chunk->max_size - block->offset - size; |
| 234 | // reuse_factor < 0 : amount of extra memory that needs to be allocated |
| 235 | // reuse_factor = 0 : allocated free space exactly matches tensor size |
| 236 | // reuse_factor > 0 : superfluous memory that will remain unused |
| 237 | bool better_reuse = best_reuse < 0 && reuse_factor > best_reuse; |
| 238 | bool better_fit = reuse_factor >= 0 && reuse_factor < best_reuse; |
| 239 | if (block->size >= size && (better_reuse || better_fit)) { |
| 240 | best_fit_chunk = c; |
| 241 | best_fit_block = chunk->n_free_blocks - 1; |
| 242 | best_reuse = reuse_factor; |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | if (best_fit_block == -1) { |
| 249 | // none of the existing chunks have enough space left |
| 250 | best_fit_chunk = ggml_dyn_tallocr_new_chunk(alloc, size); |
| 251 | best_fit_block = 0; |
| 252 | } |
| 253 | if (best_fit_chunk == -1) { |
| 254 | // since the last chunk always has virtually endless memory, this should never happen |
| 255 | GGML_LOG_ERROR("%s: not enough space in the buffer to allocate %zu bytes, largest block available %zu bytes\n", |
| 256 | __func__, size, max_avail); |
| 257 | GGML_ABORT("graph allocation: failed to reserve memory"); |
| 258 | } |
no test coverage detected