History

avcodec/vvc: simplify priority logical to improve performance for 4K/8K

For 4K/8K video processing, it's possible to have over 1,000 tasks pending on the executor. In such cases, O(n) and O(log(n)) insertion times are too costly. Reducing this to O(1) will significantly decrease the time spent in critical sections clip | before | after | delta ------------------------------------------------------------|--------|--------|------- VVC_HDR_UHDTV2_OpenGOP_7680x4320_50fps_HLG10.bit | 24 | 27 | 12.5% VVC_HDR_UHDTV2_OpenGOP_7680x4320_50fps_HLG10_HighBitrate.bit| 12 | 17 | 41.7% tears_of_steel_4k_8M_8bit_2000.vvc | 34 | 102 | 200.0% VVC_UHDTV1_OpenGOP_3840x2160_60fps_HLG10.bit | 126 | 128 | 1.6% RitualDance_1920x1080_60_10_420_37_RA.266 | 350 | 378 | 8.0% NovosobornayaSquare_1920x1080.bin | 341 | 369 | 8.2% Tango2_3840x2160_60_10_420_27_LD.266 | 69 | 70 | 1.4% RitualDance_1920x1080_60_10_420_32_LD.266 | 243 | 259 | 6.6% Chimera_8bit_1080P_1000_frames.vvc | 420 | 392 | -6.7% BQTerrace_1920x1080_60_10_420_22_RA.vvc | 148 | 144 | -2.7%

Nuo Mi committed 2 years ago

Commit 846fbc31 parent 40a14ef3 files · +55 −50Browse files

Unified · Split

3 files
  1. libavcodec/executor.c +33 −19
  2. libavcodec/executor.h +3 −2
  3. libavcodec/vvc/thread.c +19 −29
libavcodec/executor.c+33−19
@@ -48,6 +48,11 @@ typedef struct ThreadInfo { · 47 unmodified lines
48 ExecutorThread thread;48 ExecutorThread thread;
49} ThreadInfo;49} ThreadInfo;
50 50
51typedef struct Queue {
52 FFTask *head;
53 FFTask *tail;
54} Queue;
55
51struct FFExecutor {56struct FFExecutor {
52 FFTaskCallbacks cb;57 FFTaskCallbacks cb;
53 int thread_count;58 int thread_count;
@@ -60,29 +65,39 @@ struct FFExecutor { · 6 unmodified lines
60 AVCond cond;65 AVCond cond;
61 int die;66 int die;
62 67
63 FFTask *tasks;68 Queue *q;
64};69};
65 70
66static FFTask* remove_task(FFTask **prev, FFTask *t)71static FFTask* remove_task(Queue *q)
67{72{
68 *prev = t->next;73 FFTask *t = q->head;
69 t->next = NULL;74 if (t) {
75 q->head = t->next;
76 t->next = NULL;
77 if (!q->head)
78 q->tail = NULL;
79 }
70 return t;80 return t;
71}81}
72 82
73static void add_task(FFTask **prev, FFTask *t)83static void add_task(Queue *q, FFTask *t)
74{84{
75 t->next = *prev;85 t->next = NULL;
76 *prev = t;86 if (!q->head)
87 q->tail = q->head = t;
88 else
89 q->tail = q->tail->next = t;
77}90}
78 91
79static int run_one_task(FFExecutor *e, void *lc)92static int run_one_task(FFExecutor *e, void *lc)
80{93{
81 FFTaskCallbacks *cb = &e->cb;94 FFTaskCallbacks *cb = &e->cb;
82 FFTask **prev = &e->tasks;95 FFTask *t = NULL;
96
97 for (int i = 0; i < e->cb.priorities && !t; i++)
98 t = remove_task(e->q + i);
83 99
84 if (*prev) {100 if (t) {
85 FFTask *t = remove_task(prev, *prev);
86 if (e->thread_count > 0)101 if (e->thread_count > 0)
87 ff_mutex_unlock(&e->lock);102 ff_mutex_unlock(&e->lock);
88 cb->run(t, lc, cb->user_data);103 cb->run(t, lc, cb->user_data);
@@ -132,6 +147,7 @@ static void executor_free(FFExecutor *e, const int has_lock, const int has_cond) · 43 unmodified lines
132 ff_mutex_destroy(&e->lock);147 ff_mutex_destroy(&e->lock);
133 148
134 av_free(e->threads);149 av_free(e->threads);
150 av_free(e->q);
135 av_free(e->local_contexts);151 av_free(e->local_contexts);
136 152
137 av_free(e);153 av_free(e);
@@ -141,7 +157,7 @@ FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count) · 3 unmodified lines
141{157{
142 FFExecutor *e;158 FFExecutor *e;
143 int has_lock = 0, has_cond = 0;159 int has_lock = 0, has_cond = 0;
144 if (!cb || !cb->user_data || !cb->run || !cb->priority_higher)160 if (!cb || !cb->user_data || !cb->run || !cb->priorities)
145 return NULL;161 return NULL;
146 162
147 e = av_mallocz(sizeof(*e));163 e = av_mallocz(sizeof(*e));
@@ -153,6 +169,10 @@ FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count) · 5 unmodified lines
153 if (!e->local_contexts)169 if (!e->local_contexts)
154 goto free_executor;170 goto free_executor;
155 171
172 e->q = av_calloc(e->cb.priorities, sizeof(Queue));
173 if (!e->q)
174 goto free_executor;
175
156 e->threads = av_calloc(FFMAX(thread_count, 1), sizeof(*e->threads));176 e->threads = av_calloc(FFMAX(thread_count, 1), sizeof(*e->threads));
157 if (!e->threads)177 if (!e->threads)
158 goto free_executor;178 goto free_executor;
@@ -192,16 +212,10 @@ void ff_executor_free(FFExecutor **executor) · 33 unmodified lines
192 212
193void ff_executor_execute(FFExecutor *e, FFTask *t)213void ff_executor_execute(FFExecutor *e, FFTask *t)
194{214{
195 FFTaskCallbacks *cb = &e->cb;
196 FFTask **prev;
197
198 if (e->thread_count)215 if (e->thread_count)
199 ff_mutex_lock(&e->lock);216 ff_mutex_lock(&e->lock);
200 if (t) {217 if (t)
201 for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t); prev = &(*prev)->next)218 add_task(e->q + t->priority % e->cb.priorities, t);
202 /* nothing */;
203 add_task(prev, t);
204 }
205 if (e->thread_count) {219 if (e->thread_count) {
206 ff_cond_signal(&e->cond);220 ff_cond_signal(&e->cond);
207 ff_mutex_unlock(&e->lock);221 ff_mutex_unlock(&e->lock);
libavcodec/executor.h+3−2
@@ -32,6 +32,7 @@ typedef struct FFTask FFTask; · 31 unmodified lines
32 32
33struct FFTask {33struct FFTask {
34 FFTask *next;34 FFTask *next;
35 int priority; // task priority should >= 0 and < AVTaskCallbacks.priorities
35};36};
36 37
37typedef struct FFTaskCallbacks {38typedef struct FFTaskCallbacks {
@@ -39,8 +40,8 @@ typedef struct FFTaskCallbacks { · 1 unmodified line
39 40
40 int local_context_size;41 int local_context_size;
41 42
42 // return 1 if a's priority > b's priority43 // how many priorities do we have?
43 int (*priority_higher)(const FFTask *a, const FFTask *b);44 int priorities;
44 45
45 // run the task46 // run the task
46 int (*run)(FFTask *t, void *local_context, void *user_data);47 int (*run)(FFTask *t, void *local_context, void *user_data);
libavcodec/vvc/thread.c+19−29
@@ -103,13 +103,28 @@ typedef struct VVCFrameThread { · 102 unmodified lines
103 AVCond cond;103 AVCond cond;
104} VVCFrameThread;104} VVCFrameThread;
105 105
106#define PRIORITY_LOWEST 2
106static void add_task(VVCContext *s, VVCTask *t)107static void add_task(VVCContext *s, VVCTask *t)
107{108{
108 VVCFrameThread *ft = t->fc->ft;109 VVCFrameThread *ft = t->fc->ft;
110 FFTask *task = &t->u.task;
111 const int priorities[] = {
112 0, // VVC_TASK_STAGE_INIT,
113 0, // VVC_TASK_STAGE_PARSE,
114 // For an 8K clip, a CTU line completed in the reference frame may trigger 64 and more inter tasks.
115 // We assign these tasks the lowest priority to avoid being overwhelmed with inter tasks.
116 PRIORITY_LOWEST, // VVC_TASK_STAGE_INTER
117 1, // VVC_TASK_STAGE_RECON,
118 1, // VVC_TASK_STAGE_LMCS,
119 1, // VVC_TASK_STAGE_DEBLOCK_V,
120 1, // VVC_TASK_STAGE_DEBLOCK_H,
121 1, // VVC_TASK_STAGE_SAO,
122 1, // VVC_TASK_STAGE_ALF,
123 };
109 124
110 atomic_fetch_add(&ft->nb_scheduled_tasks, 1);125 atomic_fetch_add(&ft->nb_scheduled_tasks, 1);
111 126 task->priority = priorities[t->stage];
112 ff_executor_execute(s->executor, &t->u.task);127 ff_executor_execute(s->executor, task);
113}128}
114 129
115static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)130static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry)
@@ -372,31 +387,6 @@ static int task_is_stage_ready(VVCTask *t, int add) · 256 unmodified lines
372 return task_has_target_score(t, stage, score);387 return task_has_target_score(t, stage, score);
373}388}
374 389
375#define CHECK(a, b) \
376 do { \
377 if ((a) != (b)) \
378 return (a) < (b); \
379 } while (0)
380
381static int task_priority_higher(const FFTask *_a, const FFTask *_b)
382{
383 const VVCTask *a = (const VVCTask*)_a;
384 const VVCTask *b = (const VVCTask*)_b;
385
386
387 if (a->stage <= VVC_TASK_STAGE_PARSE || b->stage <= VVC_TASK_STAGE_PARSE) {
388 CHECK(a->stage, b->stage);
389 CHECK(a->fc->decode_order, b->fc->decode_order); //decode order
390 CHECK(a->ry, b->ry);
391 return a->rx < b->rx;
392 }
393
394 CHECK(a->fc->decode_order, b->fc->decode_order); //decode order
395 CHECK(a->rx + a->ry + a->stage, b->rx + b->ry + b->stage); //zigzag with type
396 CHECK(a->rx + a->ry, b->rx + b->ry); //zigzag
397 return a->ry < b->ry;
398}
399
400static void check_colocation(VVCContext *s, VVCTask *t)390static void check_colocation(VVCContext *s, VVCTask *t)
401{391{
402 const VVCFrameContext *fc = t->fc;392 const VVCFrameContext *fc = t->fc;
@@ -681,7 +671,7 @@ FFExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count) · 278 unmodified lines
681 FFTaskCallbacks callbacks = {671 FFTaskCallbacks callbacks = {
682 s,672 s,
683 sizeof(VVCLocalContext),673 sizeof(VVCLocalContext),
684 task_priority_higher,674 PRIORITY_LOWEST + 1,
685 task_run,675 task_run,
686 };676 };
687 return ff_executor_alloc(&callbacks, thread_count);677 return ff_executor_alloc(&callbacks, thread_count);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t