From 60ada0f5fa1b01e471f91115bd24d180ec74b6f8 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt Date: Thu, 16 Sep 2021 02:46:48 +0200 Subject: avcodec/elbg: Keep buffers to avoid allocations and frees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, each call to avpriv_elbg_do() would result in at least six allocations. And this function is called a lot: A typical FATE run results in 52213653 calls to av_malloc; of these, 34974671 originate from av_malloc_array and from these 34783679 originate from avpriv_elbg_do; the msvideo1 encoder tests are behind most of these. This commit changes this by keeping the buffers and only reallocating them when needed. E.g. for the encoding part of fate-vsynth1-msvideo1 total heap usage went down from 11,407,939 allocs and frees with 468,106,207 bytes allocated to 3,149 allocs and frees with 13,181,847 bytes allocated. The time for one encode2-call went down by 69%. Reviewed-by: Paul B Mahol Reviewed-by: Tomas Härdin Signed-off-by: Andreas Rheinhardt --- libavcodec/elbg.c | 84 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 30 deletions(-) (limited to 'libavcodec') diff --git a/libavcodec/elbg.c b/libavcodec/elbg.c index 24c6f06f54..4397bff1ef 100644 --- a/libavcodec/elbg.c +++ b/libavcodec/elbg.c @@ -53,8 +53,20 @@ typedef struct ELBGContext { int64_t *utility_inc; int *nearest_cb; int *points; + int *size_part; AVLFG *rand_state; int *scratchbuf; + cell *cell_buffer; + + /* Sizes for the buffers above. Pointers without such a field + * are not allocated by us and only valid for the duration + * of a single call to avpriv_elbg_do(). */ + unsigned utility_allocated; + unsigned utility_inc_allocated; + unsigned size_part_allocated; + unsigned cells_allocated; + unsigned scratchbuf_allocated; + unsigned cell_buffer_allocated; } ELBGContext; static inline int distance_limited(int *a, int *b, int dim, int limit) @@ -332,32 +344,19 @@ static void do_shiftings(ELBGContext *elbg) } } -static int do_elbg(ELBGContext *elbg, int *points, int numpoints, - int max_steps) +static void do_elbg(ELBGContext *elbg, int *points, int numpoints, + int max_steps) { - int i, j, steps = 0, ret = 0; - int *size_part = av_malloc_array(elbg->num_cb, sizeof(int)); - cell *list_buffer = av_malloc_array(numpoints, sizeof(cell)); - cell *free_cells; + int *const size_part = elbg->size_part; + int i, j, steps = 0; int best_idx = 0; int64_t last_error; elbg->error = INT64_MAX; - elbg->cells = av_malloc_array(elbg->num_cb, sizeof(cell *)); - elbg->utility = av_malloc_array(elbg->num_cb, sizeof(*elbg->utility)); elbg->points = points; - elbg->utility_inc = av_malloc_array(elbg->num_cb, sizeof(*elbg->utility_inc)); - elbg->scratchbuf = av_malloc_array(5 * elbg->dim, sizeof(int)); - - if (!size_part || !list_buffer || !elbg->cells || - !elbg->utility || !elbg->utility_inc || !elbg->scratchbuf) { - ret = AVERROR(ENOMEM); - goto out; - } - do { - free_cells = list_buffer; + cell *free_cells = elbg->cell_buffer; last_error = elbg->error; steps++; memset(elbg->utility, 0, elbg->num_cb * sizeof(*elbg->utility)); @@ -408,15 +407,6 @@ static int do_elbg(ELBGContext *elbg, int *points, int numpoints, } while(((last_error - elbg->error) > DELTA_ERR_MAX*elbg->error) && (steps < max_steps)); - -out: - av_free(size_part); - av_free(elbg->utility); - av_free(list_buffer); - av_free(elbg->cells); - av_free(elbg->utility_inc); - av_free(elbg->scratchbuf); - return ret; } #define BIG_PRIME 433494437LL @@ -450,13 +440,13 @@ static int init_elbg(ELBGContext *elbg, int *points, int numpoints, av_freep(&temp_points); return ret; } - ret = do_elbg(elbg, temp_points, numpoints / 8, 2 * max_steps); + do_elbg(elbg, temp_points, numpoints / 8, 2 * max_steps); av_free(temp_points); } else // If not, initialize the codebook with random positions for (int i = 0; i < elbg->num_cb; i++) memcpy(elbg->codebook + i * dim, points + ((i*BIG_PRIME)%numpoints)*dim, dim * sizeof(*elbg->codebook)); - return ret; + return 0; } int avpriv_elbg_do(ELBGContext **elbgp, int *points, int dim, int numpoints, @@ -476,13 +466,47 @@ int avpriv_elbg_do(ELBGContext **elbgp, int *points, int dim, int numpoints, elbg->num_cb = num_cb; elbg->dim = dim; +#define ALLOCATE_IF_NECESSARY(field, new_elements, multiplicator) \ + if (elbg->field ## _allocated < new_elements) { \ + av_freep(&elbg->field); \ + elbg->field = av_malloc_array(new_elements, \ + multiplicator * sizeof(*elbg->field)); \ + if (!elbg->field) { \ + elbg->field ## _allocated = 0; \ + return AVERROR(ENOMEM); \ + } \ + elbg->field ## _allocated = new_elements; \ + } + /* Allocating the buffers for do_elbg() here once relies + * on their size being always the same even when do_elbg() + * is called from init_elbg(). It also relies on do_elbg() + * never calling itself recursively. */ + ALLOCATE_IF_NECESSARY(cells, num_cb, 1) + ALLOCATE_IF_NECESSARY(utility, num_cb, 1) + ALLOCATE_IF_NECESSARY(utility_inc, num_cb, 1) + ALLOCATE_IF_NECESSARY(size_part, num_cb, 1) + ALLOCATE_IF_NECESSARY(cell_buffer, numpoints, 1) + ALLOCATE_IF_NECESSARY(scratchbuf, dim, 5) + ret = init_elbg(elbg, points, numpoints, max_steps); if (ret < 0) return ret; - return do_elbg (elbg, points, numpoints, max_steps); + do_elbg (elbg, points, numpoints, max_steps); + return 0; } av_cold void avpriv_elbg_free(ELBGContext **elbgp) { + ELBGContext *elbg = *elbgp; + if (!elbg) + return; + + av_freep(&elbg->size_part); + av_freep(&elbg->utility); + av_freep(&elbg->cell_buffer); + av_freep(&elbg->cells); + av_freep(&elbg->utility_inc); + av_freep(&elbg->scratchbuf); + av_freep(elbgp); } -- cgit v1.2.3