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

Showing the changes to libavcodec (3 of 3 files). Show all

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
4848 ExecutorThread thread;
4949 } ThreadInfo;
5050
51+typedef struct Queue {
52+ FFTask *head;
53+ FFTask *tail;
54+} Queue;
55+
5156 struct FFExecutor {
5257 FFTaskCallbacks cb;
5358 int thread_count;
@@ -60,29 +65,39 @@ struct FFExecutor { · 6 unmodified lines
6065 AVCond cond;
6166 int die;
6267
63− FFTask *tasks;
68+ Queue *q;
6469 };
6570
66−static FFTask* remove_task(FFTask **prev, FFTask *t)
71+static FFTask* remove_task(Queue *q)
6772 {
68− *prev = t->next;
69− t->next = NULL;
73+ FFTask *t = q->head;
74+ if (t) {
75+ q->head = t->next;
76+ t->next = NULL;
77+ if (!q->head)
78+ q->tail = NULL;
79+ }
7080 return t;
7181 }
7282
73−static void add_task(FFTask **prev, FFTask *t)
83+static void add_task(Queue *q, FFTask *t)
7484 {
75− t->next = *prev;
76− *prev = t;
85+ t->next = NULL;
86+ if (!q->head)
87+ q->tail = q->head = t;
88+ else
89+ q->tail = q->tail->next = t;
7790 }
7891
7992 static int run_one_task(FFExecutor *e, void *lc)
8093 {
8194 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);
8399
84− if (*prev) {
85− FFTask *t = remove_task(prev, *prev);
100+ if (t) {
86101 if (e->thread_count > 0)
87102 ff_mutex_unlock(&e->lock);
88103 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
132147 ff_mutex_destroy(&e->lock);
133148
134149 av_free(e->threads);
150+ av_free(e->q);
135151 av_free(e->local_contexts);
136152
137153 av_free(e);
@@ -141,7 +157,7 @@ FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count) · 3 unmodified lines
141157 {
142158 FFExecutor *e;
143159 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)
145161 return NULL;
146162
147163 e = av_mallocz(sizeof(*e));
@@ -153,6 +169,10 @@ FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count) · 5 unmodified lines
153169 if (!e->local_contexts)
154170 goto free_executor;
155171
172+ e->q = av_calloc(e->cb.priorities, sizeof(Queue));
173+ if (!e->q)
174+ goto free_executor;
175+
156176 e->threads = av_calloc(FFMAX(thread_count, 1), sizeof(*e->threads));
157177 if (!e->threads)
158178 goto free_executor;
@@ -192,16 +212,10 @@ void ff_executor_free(FFExecutor **executor) · 33 unmodified lines
192212
193213 void ff_executor_execute(FFExecutor *e, FFTask *t)
194214 {
195− FFTaskCallbacks *cb = &e->cb;
196− FFTask **prev;
197−
198215 if (e->thread_count)
199216 ff_mutex_lock(&e->lock);
200− if (t) {
201− for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t); prev = &(*prev)->next)
202− /* nothing */;
203− add_task(prev, t);
204− }
217+ if (t)
218+ add_task(e->q + t->priority % e->cb.priorities, t);
205219 if (e->thread_count) {
206220 ff_cond_signal(&e->cond);
207221 ff_mutex_unlock(&e->lock);
libavcodec/executor.h+3−2
@@ -32,6 +32,7 @@ typedef struct FFTask FFTask; · 31 unmodified lines
3232
3333 struct FFTask {
3434 FFTask *next;
35+ int priority; // task priority should >= 0 and < AVTaskCallbacks.priorities
3536 };
3637
3738 typedef struct FFTaskCallbacks {
@@ -39,8 +40,8 @@ typedef struct FFTaskCallbacks { · 1 unmodified line
3940
4041 int local_context_size;
4142
42− // return 1 if a's priority > b's priority
43− int (*priority_higher)(const FFTask *a, const FFTask *b);
43+ // how many priorities do we have?
44+ int priorities;
4445
4546 // run the task
4647 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
103103 AVCond cond;
104104 } VVCFrameThread;
105105
106+#define PRIORITY_LOWEST 2
106107 static void add_task(VVCContext *s, VVCTask *t)
107108 {
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+ };
109124
110125 atomic_fetch_add(&ft->nb_scheduled_tasks, 1);
111−
112− ff_executor_execute(s->executor, &t->u.task);
126+ task->priority = priorities[t->stage];
127+ ff_executor_execute(s->executor, task);
113128 }
114129
115130 static 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
372387 return task_has_target_score(t, stage, score);
373388 }
374389
375−#define CHECK(a, b) \
376− do { \
377− if ((a) != (b)) \
378− return (a) < (b); \
379− } while (0)
380−
381−static 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−
400390 static void check_colocation(VVCContext *s, VVCTask *t)
401391 {
402392 const VVCFrameContext *fc = t->fc;
@@ -681,7 +671,7 @@ FFExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count) · 278 unmodified lines
681671 FFTaskCallbacks callbacks = {
682672 s,
683673 sizeof(VVCLocalContext),
684− task_priority_higher,
674+ PRIORITY_LOWEST + 1,
685675 task_run,
686676 };
687677 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