You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-10 06:10:52 +02:00
avcodec/elbg: Keep buffers to avoid allocations and frees
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 <onemda@gmail.com> Reviewed-by: Tomas Härdin <tjoppen@acc.umu.se> Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
This commit is contained in:
@@ -53,8 +53,20 @@ typedef struct ELBGContext {
|
|||||||
int64_t *utility_inc;
|
int64_t *utility_inc;
|
||||||
int *nearest_cb;
|
int *nearest_cb;
|
||||||
int *points;
|
int *points;
|
||||||
|
int *size_part;
|
||||||
AVLFG *rand_state;
|
AVLFG *rand_state;
|
||||||
int *scratchbuf;
|
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;
|
} ELBGContext;
|
||||||
|
|
||||||
static inline int distance_limited(int *a, int *b, int dim, int limit)
|
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,
|
static void do_elbg(ELBGContext *elbg, int *points, int numpoints,
|
||||||
int max_steps)
|
int max_steps)
|
||||||
{
|
{
|
||||||
int i, j, steps = 0, ret = 0;
|
int *const size_part = elbg->size_part;
|
||||||
int *size_part = av_malloc_array(elbg->num_cb, sizeof(int));
|
int i, j, steps = 0;
|
||||||
cell *list_buffer = av_malloc_array(numpoints, sizeof(cell));
|
|
||||||
cell *free_cells;
|
|
||||||
int best_idx = 0;
|
int best_idx = 0;
|
||||||
int64_t last_error;
|
int64_t last_error;
|
||||||
|
|
||||||
elbg->error = INT64_MAX;
|
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->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 {
|
do {
|
||||||
free_cells = list_buffer;
|
cell *free_cells = elbg->cell_buffer;
|
||||||
last_error = elbg->error;
|
last_error = elbg->error;
|
||||||
steps++;
|
steps++;
|
||||||
memset(elbg->utility, 0, elbg->num_cb * sizeof(*elbg->utility));
|
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) &&
|
} while(((last_error - elbg->error) > DELTA_ERR_MAX*elbg->error) &&
|
||||||
(steps < max_steps));
|
(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
|
#define BIG_PRIME 433494437LL
|
||||||
@@ -450,13 +440,13 @@ static int init_elbg(ELBGContext *elbg, int *points, int numpoints,
|
|||||||
av_freep(&temp_points);
|
av_freep(&temp_points);
|
||||||
return ret;
|
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);
|
av_free(temp_points);
|
||||||
} else // If not, initialize the codebook with random positions
|
} else // If not, initialize the codebook with random positions
|
||||||
for (int i = 0; i < elbg->num_cb; i++)
|
for (int i = 0; i < elbg->num_cb; i++)
|
||||||
memcpy(elbg->codebook + i * dim, points + ((i*BIG_PRIME)%numpoints)*dim,
|
memcpy(elbg->codebook + i * dim, points + ((i*BIG_PRIME)%numpoints)*dim,
|
||||||
dim * sizeof(*elbg->codebook));
|
dim * sizeof(*elbg->codebook));
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int avpriv_elbg_do(ELBGContext **elbgp, int *points, int dim, int numpoints,
|
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->num_cb = num_cb;
|
||||||
elbg->dim = dim;
|
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);
|
ret = init_elbg(elbg, points, numpoints, max_steps);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
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)
|
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);
|
av_freep(elbgp);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user