this is a very naive implementation, but for our case the number of free blocks should be very small
| 154 | |
| 155 | // this is a very naive implementation, but for our case the number of free blocks should be very small |
| 156 | static void ggml_tallocr_free_tensor(ggml_tallocr_t alloc, struct ggml_tensor * tensor) { |
| 157 | if (ggml_tallocr_is_own(alloc, tensor) == false) { |
| 158 | // the tensor was not allocated in this buffer |
| 159 | // this can happen because the graph allocator will try to free weights and other tensors from different buffers |
| 160 | // the easiest way to deal with this is just to ignore it |
| 161 | // AT_PRINTF("ignoring %s (their buffer: %p, our buffer: %p)\n", tensor->name, (void *)tensor->buffer, (void *)alloc->buffer); |
| 162 | return; |
| 163 | } |
| 164 | |
| 165 | void * ptr = tensor->data; |
| 166 | |
| 167 | size_t size = ggml_backend_buffer_get_alloc_size(alloc->buffer, tensor); |
| 168 | size = aligned_offset(NULL, size, alloc->alignment); |
| 169 | AT_PRINTF("%s: freeing %s at %p (%zu bytes) - n_free_blocks = %d\n", __func__, tensor->name, ptr, size, alloc->n_free_blocks); |
| 170 | |
| 171 | if (!alloc->measure) { |
| 172 | ggml_backend_buffer_free_tensor(alloc->buffer, tensor); |
| 173 | } |
| 174 | |
| 175 | #ifdef GGML_ALLOCATOR_DEBUG |
| 176 | remove_allocated_tensor(alloc, tensor); |
| 177 | #endif |
| 178 | |
| 179 | // see if we can merge with an existing block |
| 180 | for (int i = 0; i < alloc->n_free_blocks; i++) { |
| 181 | struct free_block * block = &alloc->free_blocks[i]; |
| 182 | // check if ptr is at the end of the block |
| 183 | if ((char*)block->addr + block->size == ptr) { |
| 184 | block->size += size; |
| 185 | // check if we can merge with the next block |
| 186 | if (i < alloc->n_free_blocks - 1 && (char*)block->addr + block->size == alloc->free_blocks[i+1].addr) { |
| 187 | block->size += alloc->free_blocks[i+1].size; |
| 188 | alloc->n_free_blocks--; |
| 189 | for (int j = i+1; j < alloc->n_free_blocks; j++) { |
| 190 | alloc->free_blocks[j] = alloc->free_blocks[j+1]; |
| 191 | } |
| 192 | } |
| 193 | return; |
| 194 | } |
| 195 | // check if ptr is at the beginning of the block |
| 196 | if ((char*)ptr + size == block->addr) { |
| 197 | block->addr = ptr; |
| 198 | block->size += size; |
| 199 | // check if we can merge with the previous block |
| 200 | if (i > 0 && (char*)alloc->free_blocks[i-1].addr + alloc->free_blocks[i-1].size == block->addr) { |
| 201 | alloc->free_blocks[i-1].size += block->size; |
| 202 | alloc->n_free_blocks--; |
| 203 | for (int j = i; j < alloc->n_free_blocks; j++) { |
| 204 | alloc->free_blocks[j] = alloc->free_blocks[j+1]; |
| 205 | } |
| 206 | } |
| 207 | return; |
| 208 | } |
| 209 | } |
| 210 | // otherwise, add a new block |
| 211 | GGML_ASSERT(alloc->n_free_blocks < MAX_FREE_BLOCKS && "out of free blocks"); |
| 212 | // insert the new block in the correct position to keep the array sorted by address (to make merging blocks faster) |
| 213 | int insert_pos = 0; |
no test coverage detected