From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754002AbcEPRAb (ORCPT ); Mon, 16 May 2016 13:00:31 -0400 Received: from ms01.sssup.it ([193.205.80.99]:62683 "EHLO sssup.it" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753474AbcEPRA3 (ORCPT ); Mon, 16 May 2016 13:00:29 -0400 X-Greylist: delayed 3620 seconds by postgrey-1.27 at vger.kernel.org; Mon, 16 May 2016 13:00:28 EDT To: linux-kernel , Peter Zijlstra , Juri Lelli , Luca Abeni , mingo@redhat.com From: Tommaso Cucinotta Subject: SCHED_DEADLINE cpudeadline.{h,c} fixup Message-ID: <5739EE84.9070801@sssup.it> Date: Mon, 16 May 2016 18:00:04 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.7.2 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------020507050703000801070504" Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This is a multi-part message in MIME format. --------------020507050703000801070504 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Hi, looking at the SCHED_DEADLINE code, I spotted an opportunity to make cpudeadline.c faster, in that we can skip real swaps during re-heapify()ication of items after addition/removal. As such ops are done under a domain spinlock, it sounded like an interesting try. Indeed, I've got a speed-up of up to ~6% for the cpudl_set() calls on a randomly generated workload of 1K,10K,100K random insertions and deletions (75% cpudl_set() calls with is_valid=1 and 25% with is_valid=0), and randomly generated cpu IDs with 2, 4, ..., 256 CPUs. Details in the attached plot. The attached patch does this, along with a minimum of rework of cpudeadline.c internals, and a final clean-up of the cpudeadline.h interface (second patch). The measurements have been made on an Intel Core2 Duo with the CPU frequency fixed at max, by letting cpudeadline.c be initialized with various numbers of CPUs, then making many calls sequentially, taking the rdtsc among calls, then dumping all numbers through printk(), and I'm plotting the average of clock ticks between consecutive calls. [ I can share the benchmarking code as well if needed ] Also, this fixes what seems to me a bug I noticed comparing the whole heap contents as handledbut the modified code vs the original one, insertion by insertion. The problem is in this code: cp->elements[cp->size - 1].dl = 0; cp->elements[cp->size - 1].cpu = cpu; cp->elements[cpu].idx = cp->size - 1; mycpudl_change_key(cp, cp->size - 1, dl); when fed by an absolute deadline that is so large to have a negative value as a s64. In such a case, as from dl_time_before(), the kernel should handle correctly the abs deadline wrap-around, however the current code in cpudeadline.c goes mad, and doesn't re-heapify correctly the just inserted element... that said, if these are ns, such a bug should be hit after a ~292 years of uptime :-D... I'd be happy to hear comments from others. I can provide additional info / make additional experiments as needed. Please, reply-all to this e-mail, I'm not subscribed to linux-kernel@. Thanks, Tommaso -- Tommaso Cucinotta, Computer Engineering PhD Associate Professor at the Real-Time Systems Laboratory (ReTiS) Scuola Superiore Sant'Anna, Pisa, Italy http://retis.sssup.it/people/tommaso --------------020507050703000801070504 Content-Type: text/x-patch; name="0001-Make-deadline-max-heap-faster-and-fix-deadline-wrap-.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Make-deadline-max-heap-faster-and-fix-deadline-wrap-.pa"; filename*1="tch" >>From ee54c2849f1d9d7f7f8faeb474a61074cae868b9 Mon Sep 17 00:00:00 2001 From: Tommaso Cucinotta Date: Thu, 12 May 2016 19:06:37 +0200 Subject: [PATCH 1/2] Make deadline max-heap faster and fix deadline wrap-around bug. --- kernel/sched/cpudeadline.c | 122 ++++++++++++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/kernel/sched/cpudeadline.c b/kernel/sched/cpudeadline.c index 5a75b08..245d929 100644 --- a/kernel/sched/cpudeadline.c +++ b/kernel/sched/cpudeadline.c @@ -31,55 +31,91 @@ static inline int right_child(int i) return (i << 1) + 2; } -static void cpudl_exchange(struct cpudl *cp, int a, int b) -{ - int cpu_a = cp->elements[a].cpu, cpu_b = cp->elements[b].cpu; - - swap(cp->elements[a].cpu, cp->elements[b].cpu); - swap(cp->elements[a].dl , cp->elements[b].dl ); - - swap(cp->elements[cpu_a].idx, cp->elements[cpu_b].idx); -} - -static void cpudl_heapify(struct cpudl *cp, int idx) +static void cpudl_heapify_down(struct cpudl *cp, int idx) { int l, r, largest; + int orig_cpu = cp->elements[idx].cpu; + u64 orig_dl = cp->elements[idx].dl; + /* adapted from lib/prio_heap.c */ while(1) { + u64 largest_dl; l = left_child(idx); r = right_child(idx); largest = idx; + largest_dl = orig_dl; - if ((l < cp->size) && dl_time_before(cp->elements[idx].dl, - cp->elements[l].dl)) + if ((l < cp->size) && dl_time_before(orig_dl, cp->elements[l].dl)) { largest = l; - if ((r < cp->size) && dl_time_before(cp->elements[largest].dl, - cp->elements[r].dl)) + largest_dl = cp->elements[l].dl; + } + if ((r < cp->size) && dl_time_before(largest_dl, cp->elements[r].dl)) largest = r; + if (largest == idx) break; - /* Push idx down the heap one level and bump one up */ - cpudl_exchange(cp, largest, idx); + /* pull largest child onto idx */ + cp->elements[idx].cpu = cp->elements[largest].cpu; + cp->elements[idx].dl = cp->elements[largest].dl; + cp->elements[cp->elements[idx].cpu].idx = idx; idx = largest; } + /* actual push down of saved original values orig_* */ + cp->elements[idx].cpu = orig_cpu; + cp->elements[idx].dl = orig_dl; + cp->elements[cp->elements[idx].cpu].idx = idx; +} + +static void cpudl_heapify_up(struct cpudl *cp, int idx) +{ + int p; + + int orig_cpu = cp->elements[idx].cpu; + u64 orig_dl = cp->elements[idx].dl; + + while (idx != 0) { + p = parent(idx); + if (dl_time_before(cp->elements[idx].dl, cp->elements[p].dl)) + break; + /* pull parent onto idx */ + cp->elements[idx].cpu = cp->elements[p].cpu; + cp->elements[idx].dl = cp->elements[p].dl; + cp->elements[cp->elements[idx].cpu].idx = idx; + idx = p; + } + /* actual push up of saved original values orig_* */ + cp->elements[idx].cpu = orig_cpu; + cp->elements[idx].dl = orig_dl; + cp->elements[cp->elements[idx].cpu].idx = idx; +} + +static void cpudl_heapify(struct cpudl *cp, int idx) +{ + WARN_ON(idx == IDX_INVALID || !cpu_present(idx)); + if (idx == IDX_INVALID) + return; + + if (idx > 0 && dl_time_before(cp->elements[parent(idx)].dl, cp->elements[idx].dl)) { + cpudl_heapify_up(cp, idx); + } else { + cpudl_heapify_down(cp, idx); + } } static void cpudl_change_key(struct cpudl *cp, int idx, u64 new_dl) { WARN_ON(idx == IDX_INVALID || !cpu_present(idx)); + if (idx == IDX_INVALID) + return; if (dl_time_before(new_dl, cp->elements[idx].dl)) { cp->elements[idx].dl = new_dl; - cpudl_heapify(cp, idx); + cpudl_heapify_down(cp, idx); } else { cp->elements[idx].dl = new_dl; - while (idx > 0 && dl_time_before(cp->elements[parent(idx)].dl, - cp->elements[idx].dl)) { - cpudl_exchange(cp, idx, parent(idx)); - idx = parent(idx); - } + cpudl_heapify_up(cp, idx); } } @@ -148,33 +184,29 @@ void cpudl_set(struct cpudl *cp, int cpu, u64 dl, int is_valid) */ goto out; } - new_cpu = cp->elements[cp->size - 1].cpu; - cp->elements[old_idx].dl = cp->elements[cp->size - 1].dl; - cp->elements[old_idx].cpu = new_cpu; cp->size--; - cp->elements[new_cpu].idx = old_idx; cp->elements[cpu].idx = IDX_INVALID; - while (old_idx > 0 && dl_time_before( - cp->elements[parent(old_idx)].dl, - cp->elements[old_idx].dl)) { - cpudl_exchange(cp, old_idx, parent(old_idx)); - old_idx = parent(old_idx); + if (old_idx != cp->size) { + new_cpu = cp->elements[cp->size].cpu; + cp->elements[old_idx].dl = cp->elements[cp->size].dl; + cp->elements[old_idx].cpu = new_cpu; + cp->elements[new_cpu].idx = old_idx; + cpudl_heapify(cp, old_idx); } - cpumask_set_cpu(cpu, cp->free_cpus); - cpudl_heapify(cp, old_idx); - - goto out; - } - if (old_idx == IDX_INVALID) { - cp->size++; - cp->elements[cp->size - 1].dl = 0; - cp->elements[cp->size - 1].cpu = cpu; - cp->elements[cpu].idx = cp->size - 1; - cpudl_change_key(cp, cp->size - 1, dl); - cpumask_clear_cpu(cpu, cp->free_cpus); + cpumask_set_cpu(cpu, cp->free_cpus); } else { - cpudl_change_key(cp, old_idx, dl); + if (old_idx == IDX_INVALID) { + int sz1 = cp->size++; + cp->elements[sz1].dl = dl; + cp->elements[sz1].cpu = cpu; + cp->elements[cpu].idx = sz1; + cpudl_heapify_up(cp, sz1); + + cpumask_clear_cpu(cpu, cp->free_cpus); + } else { + cpudl_change_key(cp, old_idx, dl); + } } out: -- 2.7.4 --------------020507050703000801070504 Content-Type: text/x-patch; name="0002-Split-cpudl_set-into-cpudl_set-and-cpudl_clear.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0002-Split-cpudl_set-into-cpudl_set-and-cpudl_clear.patch" >>From 903d4a0ea0df831e62fc8016ce55a77939d52dbc Mon Sep 17 00:00:00 2001 From: Tommaso Cucinotta Date: Fri, 13 May 2016 11:47:36 +0200 Subject: [PATCH 2/2] Split cpudl_set() into cpudl_set() and cpudl_clear(). These 2 exercise independent code paths and need different arguments. --- kernel/sched/cpudeadline.c | 69 +++++++++++++++++++++++++++++----------------- kernel/sched/cpudeadline.h | 3 +- kernel/sched/deadline.c | 10 +++---- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/kernel/sched/cpudeadline.c b/kernel/sched/cpudeadline.c index 245d929..82e7c66 100644 --- a/kernel/sched/cpudeadline.c +++ b/kernel/sched/cpudeadline.c @@ -156,16 +156,15 @@ out: } /* - * cpudl_set - update the cpudl max-heap + * cpudl_clear - remove a cpu from the cpudl max-heap * @cp: the cpudl max-heap context * @cpu: the target cpu - * @dl: the new earliest deadline for this cpu * * Notes: assumes cpu_rq(cpu)->lock is locked * * Returns: (void) */ -void cpudl_set(struct cpudl *cp, int cpu, u64 dl, int is_valid) +void cpudl_clear(struct cpudl *cp, int cpu) { int old_idx, new_cpu; unsigned long flags; @@ -173,17 +172,16 @@ void cpudl_set(struct cpudl *cp, int cpu, u64 dl, int is_valid) WARN_ON(!cpu_present(cpu)); raw_spin_lock_irqsave(&cp->lock, flags); + old_idx = cp->elements[cpu].idx; - if (!is_valid) { + if (old_idx == IDX_INVALID) { + /* + * Nothing to remove if old_idx was invalid. + * This could happen if a rq_offline_dl is + * called for a CPU without -dl tasks running. + */ + } else { /* remove item */ - if (old_idx == IDX_INVALID) { - /* - * Nothing to remove if old_idx was invalid. - * This could happen if a rq_offline_dl is - * called for a CPU without -dl tasks running. - */ - goto out; - } cp->size--; cp->elements[cpu].idx = IDX_INVALID; if (old_idx != cp->size) { @@ -193,23 +191,44 @@ void cpudl_set(struct cpudl *cp, int cpu, u64 dl, int is_valid) cp->elements[new_cpu].idx = old_idx; cpudl_heapify(cp, old_idx); } - cpumask_set_cpu(cpu, cp->free_cpus); + } + + raw_spin_unlock_irqrestore(&cp->lock, flags); +} + +/* + * cpudl_set - update the cpudl max-heap + * @cp: the cpudl max-heap context + * @cpu: the target cpu + * @dl: the new earliest deadline for this cpu + * + * Notes: assumes cpu_rq(cpu)->lock is locked + * + * Returns: (void) + */ +void cpudl_set(struct cpudl *cp, int cpu, u64 dl) +{ + int old_idx; + unsigned long flags; + + WARN_ON(!cpu_present(cpu)); + + raw_spin_lock_irqsave(&cp->lock, flags); + + old_idx = cp->elements[cpu].idx; + if (old_idx == IDX_INVALID) { + int sz1 = cp->size++; + cp->elements[sz1].dl = dl; + cp->elements[sz1].cpu = cpu; + cp->elements[cpu].idx = sz1; + cpudl_heapify_up(cp, sz1); + + cpumask_clear_cpu(cpu, cp->free_cpus); } else { - if (old_idx == IDX_INVALID) { - int sz1 = cp->size++; - cp->elements[sz1].dl = dl; - cp->elements[sz1].cpu = cpu; - cp->elements[cpu].idx = sz1; - cpudl_heapify_up(cp, sz1); - - cpumask_clear_cpu(cpu, cp->free_cpus); - } else { - cpudl_change_key(cp, old_idx, dl); - } + cpudl_change_key(cp, old_idx, dl); } -out: raw_spin_unlock_irqrestore(&cp->lock, flags); } diff --git a/kernel/sched/cpudeadline.h b/kernel/sched/cpudeadline.h index fcbdf83..f7da8c5 100644 --- a/kernel/sched/cpudeadline.h +++ b/kernel/sched/cpudeadline.h @@ -23,7 +23,8 @@ struct cpudl { #ifdef CONFIG_SMP int cpudl_find(struct cpudl *cp, struct task_struct *p, struct cpumask *later_mask); -void cpudl_set(struct cpudl *cp, int cpu, u64 dl, int is_valid); +void cpudl_set(struct cpudl *cp, int cpu, u64 dl); +void cpudl_clear(struct cpudl *cp, int cpu); int cpudl_init(struct cpudl *cp); void cpudl_set_freecpu(struct cpudl *cp, int cpu); void cpudl_clear_freecpu(struct cpudl *cp, int cpu); diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 686ec8a..e3ffc2f 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -795,7 +795,7 @@ static void inc_dl_deadline(struct dl_rq *dl_rq, u64 deadline) if (dl_rq->earliest_dl.curr == 0 || dl_time_before(deadline, dl_rq->earliest_dl.curr)) { dl_rq->earliest_dl.curr = deadline; - cpudl_set(&rq->rd->cpudl, rq->cpu, deadline, 1); + cpudl_set(&rq->rd->cpudl, rq->cpu, deadline); } } @@ -810,14 +810,14 @@ static void dec_dl_deadline(struct dl_rq *dl_rq, u64 deadline) if (!dl_rq->dl_nr_running) { dl_rq->earliest_dl.curr = 0; dl_rq->earliest_dl.next = 0; - cpudl_set(&rq->rd->cpudl, rq->cpu, 0, 0); + cpudl_clear(&rq->rd->cpudl, rq->cpu); } else { struct rb_node *leftmost = dl_rq->rb_leftmost; struct sched_dl_entity *entry; entry = rb_entry(leftmost, struct sched_dl_entity, rb_node); dl_rq->earliest_dl.curr = entry->deadline; - cpudl_set(&rq->rd->cpudl, rq->cpu, entry->deadline, 1); + cpudl_set(&rq->rd->cpudl, rq->cpu, entry->deadline); } } @@ -1667,7 +1667,7 @@ static void rq_online_dl(struct rq *rq) cpudl_set_freecpu(&rq->rd->cpudl, rq->cpu); if (rq->dl.dl_nr_running > 0) - cpudl_set(&rq->rd->cpudl, rq->cpu, rq->dl.earliest_dl.curr, 1); + cpudl_set(&rq->rd->cpudl, rq->cpu, rq->dl.earliest_dl.curr); } /* Assumes rq->lock is held */ @@ -1676,7 +1676,7 @@ static void rq_offline_dl(struct rq *rq) if (rq->dl.overloaded) dl_clear_overload(rq); - cpudl_set(&rq->rd->cpudl, rq->cpu, 0, 0); + cpudl_clear(&rq->rd->cpudl, rq->cpu); cpudl_clear_freecpu(&rq->rd->cpudl, rq->cpu); } -- 2.7.4 --------------020507050703000801070504 Content-Type: application/pdf; name="cpudl.pdf" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="cpudl.pdf" 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 Cx4oXwqsnpoWy242O7NhWV0PRyfAshv1lH7KEUJvv/3MJdP16eEAK9tOeFZu1DHFwip3vWrq KPfO26a2fNBCTjb9dd2rJgVPQWE/eW30jauu528/fhlf3/3y+gm9alIhTMZqkJV0h5Fhdw/w 6E9cI/ny68xyjeSZn3XWA47DA3qtEDT+umoRV1EsRR7gWbw4yrlxAJPEc7Yy+ZOMEnuzc/4I enaW+4LzKTfg1cSynUM6lQzjZxIBhlmFWK3CuMawY/26IIshMsigG6LmLl/LHFuViYIsBscg U7uPvovc82vfDlv6BF+w9c1yM06tP/rGc++vjQG2vAi6YoslspdE2HwP5fIt9f+oZYeCp1s0 xKIlcM0gpUMTvKkNaFbhvH3JhIEfQsWWrWA4rHV1i6Q+oCIbpivSQRTM/M1xmCDp5svtv2Y3 9q6W8oPl9yTVwDVIY6T7O/cCG8sOyfS2YtECK+F+sK1oEllagmxWoZLU/NReOs8gy3VTzcfl uonLx8t1ky9INHi9/qH9WQl7MfwHUfNR7QplbmRzdHJlYW0KZW5kb2JqCjQgMCBvYmoKICAg MjE2NQplbmRvYmoKMiAwIG9iago8PAogICAvRXh0R1N0YXRlIDw8CiAgICAgIC9hMCA8PCAv Q0EgMSAvY2EgMSA+PgogICA+PgogICAvRm9udCA8PAogICAgICAvZi0wLTAgNSAwIFIKICAg Pj4KPj4KZW5kb2JqCjYgMCBvYmoKPDwgL1R5cGUgL1BhZ2UKICAgL1BhcmVudCAxIDAgUgog ICAvTWVkaWFCb3ggWyAwIDAgMzYwIDIxNiBdCiAgIC9Db250ZW50cyAzIDAgUgogICAvR3Jv dXAgPDwKICAgICAgL1R5cGUgL0dyb3VwCiAgICAgIC9TIC9UcmFuc3BhcmVuY3kKICAgICAg L0kgdHJ1ZQogICAgICAvQ1MgL0RldmljZVJHQgogICA+PgogICAvUmVzb3VyY2VzIDIgMCBS Cj4+CmVuZG9iago3IDAgb2JqCjw8IC9MZW5ndGggOCAwIFIKICAgL0ZpbHRlciAvRmxhdGVE ZWNvZGUKICAgL0xlbmd0aDEgODU0OAo+PgpzdHJlYW0KeJzlWXtcVVX23/uss+/7ce4LuNzL fQAXxBcIoqKWN1PzNYWK5iMNBcmxTIwsFQ3FBB84iAKmmV4LnFJzGHIMxBxL8hHa1KjNONpY 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 ed5APQyNQGf/rckDW86D+e6DqCVJ8zwOoi72ieMQ+ch8JGZNkTSKWRFLZklcMBfEJXNJfGA+ EGtmHVmxRpFGsVeRt2QuiSvmilhxBkUZFGdQlEEPieMQ9Zynojya82vKryvmihiZkZj30bRP xZqKNCVzmfRcE001kXwWSWeR7JXklfyvOFDBb5Wl0tMduffUXEOI7UwXKfWROjg6vN81P3ty pecX1RKuQwplbmRzdHJlYW0KZW5kb2JqCjEwIDAgb2JqCiAgIDM0OQplbmRvYmoKMTEgMCBv YmoKPDwgL1R5cGUgL0ZvbnREZXNjcmlwdG9yCiAgIC9Gb250TmFtZSAvS1FFV0FQK0RlamFW dVNhbnMKICAgL0ZvbnRGYW1pbHkgKERlamFWdSBTYW5zKQogICAvRmxhZ3MgMzIKICAgL0Zv bnRCQm94IFsgLTEwMjAgLTQ2MiAxNzkzIDEyMzIgXQogICAvSXRhbGljQW5nbGUgMAogICAv QXNjZW50IDkyOAogICAvRGVzY2VudCAtMjM1CiAgIC9DYXBIZWlnaHQgMTIzMgogICAvU3Rl bVYgODAKICAgL1N0ZW1IIDgwCiAgIC9Gb250RmlsZTIgNyAwIFIKPj4KZW5kb2JqCjUgMCBv YmoKPDwgL1R5cGUgL0ZvbnQKICAgL1N1YnR5cGUgL1RydWVUeXBlCiAgIC9CYXNlRm9udCAv S1FFV0FQK0RlamFWdVNhbnMKICAgL0ZpcnN0Q2hhciAzMgogICAvTGFzdENoYXIgMTE5CiAg IC9Gb250RGVzY3JpcHRvciAxMSAwIFIKICAgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcK ICAgL1dpZHRocyBbIDMxNyAwIDAgMCAwIDk1MCAwIDAgMzkwIDM5MCAwIDAgMCAwIDAgMCA2 MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgMCA2MzYgNjM2IDAgMCAwIDAgMCAwIDAgMCAw IDY5OCAwIDAgMCA3NzQgMCAwIDAgMCAwIDAgMCAwIDYwMyAwIDAgNjM0IDYxMCA3MzEgMCAw IDAgMCAwIDAgMCAwIDAgMCAwIDYxMiAwIDAgMCA2MTUgMCA2MzQgMCAyNzcgMCAwIDAgMCA2 MzMgNjExIDAgMCA0MTEgNTIwIDAgMCAwIDgxNyBdCiAgICAvVG9Vbmljb2RlIDkgMCBSCj4+ CmVuZG9iagoxIDAgb2JqCjw8IC9UeXBlIC9QYWdlcwogICAvS2lkcyBbIDYgMCBSIF0KICAg L0NvdW50IDEKPj4KZW5kb2JqCjEyIDAgb2JqCjw8IC9DcmVhdG9yIChjYWlybyAxLjE0LjYg KGh0dHA6Ly9jYWlyb2dyYXBoaWNzLm9yZykpCiAgIC9Qcm9kdWNlciAoY2Fpcm8gMS4xNC42 IChodHRwOi8vY2Fpcm9ncmFwaGljcy5vcmcpKQo+PgplbmRvYmoKMTMgMCBvYmoKPDwgL1R5 cGUgL0NhdGFsb2cKICAgL1BhZ2VzIDEgMCBSCj4+CmVuZG9iagp4cmVmCjAgMTQKMDAwMDAw MDAwMCA2NTUzNSBmIAowMDAwMDEwMTA2IDAwMDAwIG4gCjAwMDAwMDIyODAgMDAwMDAgbiAK MDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDAyMjU3IDAwMDAwIG4gCjAwMDAwMDk2NjEgMDAw MDAgbiAKMDAwMDAwMjM4OSAwMDAwMCBuIAowMDAwMDAyNjAzIDAwMDAwIG4gCjAwMDAwMDg5 MTcgMDAwMDAgbiAKMDAwMDAwODk0MCAwMDAwMCBuIAowMDAwMDA5MzY3IDAwMDAwIG4gCjAw MDAwMDkzOTAgMDAwMDAgbiAKMDAwMDAxMDE3MSAwMDAwMCBuIAowMDAwMDEwMjk5IDAwMDAw IG4gCnRyYWlsZXIKPDwgL1NpemUgMTQKICAgL1Jvb3QgMTMgMCBSCiAgIC9JbmZvIDEyIDAg Ugo+PgpzdGFydHhyZWYKMTAzNTIKJSVFT0YK --------------020507050703000801070504--