Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w2dR1-000Skp-1S for pgsql-hackers@arkaria.postgresql.org; Tue, 17 Mar 2026 23:02:49 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w2dQz-005wWE-0i for pgsql-hackers@arkaria.postgresql.org; Tue, 17 Mar 2026 23:02:45 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w2dQy-005wVx-25 for pgsql-hackers@lists.postgresql.org; Tue, 17 Mar 2026 23:02:44 +0000 Received: from mail-ed1-x532.google.com ([2a00:1450:4864:20::532]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w2dQu-00000000GP3-1O1s for pgsql-hackers@postgresql.org; Tue, 17 Mar 2026 23:02:43 +0000 Received: by mail-ed1-x532.google.com with SMTP id 4fb4d7f45d1cf-6674cba2c50so551037a12.0 for ; Tue, 17 Mar 2026 16:02:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773788560; cv=none; d=google.com; s=arc-20240605; b=XrEytPM3clvKj762m/yvjudkCekyWJEa4tZ7VAsLyR63wpr8UZ+dZml5+d1asZU1Qx 4Q+cwb4J42eCq2CPLXMgvXlllmM/rW3MmhWxCIONWluTh+fx/O1ZwYeH4nqcOwBFqg39 oznKDVRGdtTmhFfmJxGu+f1wDv0QY+lOst2mDctAnHZXmSj82pfm0L+oP92Dm+d/oevW Dda7Tmas2W+VOdtfoOHBg8fYstV6zHsR6qK50kNUKN5OXcWJ09D5n1KJM3HU59IUnoWz K7w29E/hspbBRECMScdIpYv5c/S2MV5rICPQzRkomPkA3vHSjqHRbfIeV0Fft/nkDv40 j6+Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=hryMa3Erjt6R7XCC5wor+wueBnXv6DhNHQZ/HK9C+48=; fh=MeXFw8TlHT9jDwJRXa+yblx8XxpTEa8lFIiUWeGOWiw=; b=XJHUD1nqeXSZpjTQea7LaxKXcvYtXSfqznQcixbbXIiHfQI7O++KRmzY7Jphwjmeds KENLNLBX6AknWyZjizGTsCP8WiDdBJ/PebD6EUGMBgnVQqpVXwjwxlYsTs3YL6PqudyF S7YyzsmfEyiMI4kaUorj3737w/5WHN0Xk5wWAbtzzOW+l5/gzcKRlV9zBYs4lJxvK+VL +h4HXouIHTHz6CEKJL2WjDDfvz4FZKBXxa/CXqT4CeGq8Evd3xjo7QKwKJfL3kB2Rwfo XsHkKeIU54kdIOOEHV/STRQ90VMfUWJv9X6gRyeeQIEFEMAVDWJDLocSw6JEUAeRyy6U WM8A==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=esi.dz; s=google; t=1773788560; x=1774393360; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=hryMa3Erjt6R7XCC5wor+wueBnXv6DhNHQZ/HK9C+48=; b=b/dCl0UujntSgEeH9wZmp9HJXfqewbj+cJu4n9LrUSJOCQwIXO18Q4llFfgw7mAMl9 bjr1ZUwbcFTI09Ml2sW2YGSwbebz/DpV6ZoBKgAWyrD8W1ZLUJjWFBra8XKytTY6ofS+ qMg237Fhg1JNb8ul9eWNT8t1405HMpi9O1fafQQ4dgsE+H5yz6oIJYO45RemoXXOmyg3 mx2BvbzkzyhXYsAGkTsCc2moteoyLjy8bHggPtLaMjkV3zVPI/2WzPvVBnrCWEieEyet IexwoQig9d51yo1Ff8OHMFX9RlZ8cJfB7JbfZkzLboEksqQfMgIcKFhuMH7QeRWVz9gO d7Mg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773788560; x=1774393360; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=hryMa3Erjt6R7XCC5wor+wueBnXv6DhNHQZ/HK9C+48=; b=lPJOhPthsJPYutnpygInp8ky14Mv3WS2KQX2TI+Tzc+n1aNaUravYuvacrKQx5ym0G KI1Eyx7OqSz0DXQdpn1o8ObsNjCKc6VavOb30bMCsdfDHII8gdzvUo+wXzSRXZwj25ib 5FiRG8hPE3WgYW5ojFyjETHAByMQbTaThffdDFYetetoF7iUC1E8WrK+YlZObw4iKlwZ 3oWtcuV6qZhPA5YRy4ewErKKH6msb09o+FKlFmrQ/LsuNuuu7tyh3gFY2wVOo6rDMT/M 0u1zlH5LZn31sBgGXXrLQk/HXLuwOl2GcQidYOz0PnHIStg1bJApW7BcDnFvWohIZSwz PUoQ== X-Forwarded-Encrypted: i=1; AJvYcCVw1ZyRR6HVcqloC6vFIqrmrRvHJMRigOBYbo/JFZSt0Wgtc70ThOh8QMve2S4Caz0Ixmm7xwhquLK4FwID@postgresql.org X-Gm-Message-State: AOJu0YwNbSUA2TV6niJyxRN17GG7CHFlw0b7+mcaL2QNzYAAOwkrJuEO nUVGUI3y5U0XwzX7rcj8vwsade7aP01Nlm45yqtDdttQaiRFXpOx1Ag2pDmp8QP7JJGPpEuZD+c xvA2ohs7youiZxCpqEaWcSZJva38D7FMsvwaIBcPP X-Gm-Gg: ATEYQzyYEVhQ4Shh/xNDIi8Fy/SIPX5s/cE7LgbGzg9MZDcXE27Y1akQrfs+x4Dz2X4 ZouiuBymzWnm0FyvX9ad3stfMhYq+4WEyPqmUGVYLaar6HNfDx7zhzRpeGnAePJ279tnScZ21B7 QpwBd3M7RWzR6liZWK+MEzrSDqQ00tUZJYXwwmQ2i53MHpscDAdaAItVM27s8WSs7+oIA5quTyU QqGp8ndU1n0hvw/atDfKDJ0SuxouY9Fl0zyto9SrwQZ6jccTLFNNIwJ65dSLNzsT8jdYAR8EJEM 0StnwVeXFjO7cJNv+aQHXbcOJ4Ou7dKyLh0nauSFyQt9su0wOXphCz5A/E6BTcnGhvVPN8HlDuI MoRBmVxkn6NkASAAtpK5zCdp+UmM= X-Received: by 2002:a17:907:86a5:b0:b96:e79f:194c with SMTP id a640c23a62f3a-b97d6e51416mr428328866b.31.1773788559927; Tue, 17 Mar 2026 16:02:39 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: KAZAR Ayoub Date: Wed, 18 Mar 2026 00:02:28 +0100 X-Gm-Features: AaiRm52C0lZUxlAilD7dUu33RwMqjlB0ZreIRmLL8RUVDeYIGmlOxQ9b55LnZdI Message-ID: Subject: Re: Speed up COPY TO text/CSV parsing using SIMD To: Nathan Bossart Cc: Andres Freund , Pg Hackers , Neil Conway , Manni Wood , Andrew Dunstan , Shinya Kato , Mark Wong , Nazir Bilal Yavuz Content-Type: multipart/alternative; boundary="000000000000057a4c064d4055bc" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000057a4c064d4055bc Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Tue, Mar 17, 2026 at 7:49=E2=80=AFPM Nathan Bossart wrote: > On Sat, Mar 14, 2026 at 11:43:38PM +0100, KAZAR Ayoub wrote: > > Just a small concern about where some varlenas have a larger binary siz= e > > than its text representation ex: > > SELECT pg_column_size(to_tsvector('SIMD is GOOD')); > > pg_column_size > > ---------------- > > 32 > > > > its text representation is less than sizeof(Vector8) so currently v3 > would > > enter SIMD path and exit out just from the beginning (two extra branche= s) > > because it does this: > > + if (TupleDescAttr(tup_desc, attnum - 1)->attlen =3D=3D -1 && > > + VARSIZE_ANY_EXHDR(DatumGetPointer(value)) > sizeof(Vector8)) > > > > I thought maybe we could do * 2 or * 4 its binary size, depends on the > type > > really but this is just a proposition if this case is something > concerning. > > Can we measure the impact of this? How likely is this case? > I'll respond to this separately in a different email. > > > +static pg_attribute_always_inline void CopyAttributeOutText(CopyToStat= e > cstate, const char *string, > > + > bool use_simd, size_t len); > > +static pg_attribute_always_inline void CopyAttributeOutCSV(CopyToState > cstate, const char *string, > > + > bool use_quote, bool use_simd, > size_t len); > > Can you test this on its own, too? We might be able to separate this and > the change below into a prerequisite patch, assuming they show benefits. > I tested inlining alone and found the results were about an improvement of 1% to 4% across all configurations. The inlining is only meaningful in combination with the SIMD work, for the reason described below. > > > if (is_csv) > > - CopyAttributeOutCSV(cstate, string, > > - > cstate->opts.force_quote_flags[attnum - 1]); > > + { > > + if (use_simd) > > + CopyAttributeOutCSV(cstate, strin= g, > > + > cstate->opts.force_quote_flags[attnum - 1], > > + > true, len); > > + else > > + CopyAttributeOutCSV(cstate, strin= g, > > + > cstate->opts.force_quote_flags[attnum - 1], > > + > false, len); > > There isn't a terrible amount of branching on use_simd in these functions= , > so I'm a little skeptical this makes much difference. As above, it would > be good to measure it I compiled three variants v3: use_simd passed as compile-time, CopyAttribute functions inlined. v3_variable: use_simd as is variable, CopyAttribute functions inlined. v3_variable_noinline: use_simd as is variable, CopyAttribute functions are not inlined. None of the helpers are explicitly inlined by us. The assembly reveals two things: 1) The CSV SIMD helpers (CopyCheckCSVQuoteNeedSIMD, CopySkipCSVEscapeSIMD) are inlined by the compiler naturally in all three variants, CopySkipTextSIMD is never inlined by the compiler in any variant. 2) The constant-emitting approach (v3) does matter (just a little apparently) specifically for CopySkipTextSIMD. Its the same story as COPY FROM patch's first commit it just emits code without use_simd branch jbe ... ; len > sizeof(Vector8) je ... ; need_transcoding call CopySkipTextSIMD Whether the extra branching in for constant passing is worth it or not is demonstrated by the benchmark. Test Master v3 v3_var v3_var_noinl TEXT clean 1504ms -24.1% -23.0% -21.5% CSV clean 1760ms -34.9% -32.7% -33.0% TEXT 1/3 backslashes 3763ms +4.6% +6.9% +4.1% CSV 1/3 quotes 3885ms +3.1% +2.7% -0.8% Wide table TEXT (integer columns): Cols Master v3 v3_var v3_var_noinl 50 2083ms -0.7% -0.6% +3.5% 100 4094ms -0.1% -0.5% +4.5% 200 1560ms +0.6% -2.3% +3.2% 500 1905ms -1.0% -1.3% +4.7% 1000 1455ms +1.8% +0.4% +4.3% Wide table CSV: Cols Master v3 v3_var v3_var_noinl 50 2421ms +4.0% +6.7% +5.8% 100 4980ms +0.1% +2.0% +0.1% 200 1901ms +1.4% +3.5% +1.4% 500 2328ms +1.8% +2.7% +2.2% 1000 1815ms +2.0% +2.8% +2.5% I'm not sure whether there's a diff between v3 and v3_var practically speaking, what do you think ? Regards, Ayoub --000000000000057a4c064d4055bc Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: base64 PGRpdiBkaXI9Imx0ciI+PGRpdiBkaXI9Imx0ciI+PGRpdiBkaXI9Imx0ciI+T24gVHVlLCBNYXIg MTcsIDIwMjYgYXQgNzo0OeKAr1BNIE5hdGhhbiBCb3NzYXJ0ICZsdDs8YSBocmVmPSJtYWlsdG86 bmF0aGFuZGJvc3NhcnRAZ21haWwuY29tIiB0YXJnZXQ9Il9ibGFuayI+bmF0aGFuZGJvc3NhcnRA Z21haWwuY29tPC9hPiZndDsgd3JvdGU6PGJyPjwvZGl2PjxkaXYgY2xhc3M9ImdtYWlsX3F1b3Rl Ij48YmxvY2txdW90ZSBjbGFzcz0iZ21haWxfcXVvdGUiIHN0eWxlPSJtYXJnaW46MHB4IDBweCAw cHggMC44ZXg7Ym9yZGVyLWxlZnQ6MXB4IHNvbGlkIHJnYigyMDQsMjA0LDIwNCk7cGFkZGluZy1s ZWZ0OjFleCI+T24gU2F0LCBNYXIgMTQsIDIwMjYgYXQgMTE6NDM6MzhQTSArMDEwMCwgS0FaQVIg QXlvdWIgd3JvdGU6PGJyPg0KJmd0OyBKdXN0IGEgc21hbGwgY29uY2VybiBhYm91dCB3aGVyZSBz b21lIHZhcmxlbmFzIGhhdmUgYSBsYXJnZXIgYmluYXJ5IHNpemU8YnI+DQomZ3Q7IHRoYW4gaXRz IHRleHQgcmVwcmVzZW50YXRpb24gZXg6PGJyPg0KJmd0OyBTRUxFQ1QgcGdfY29sdW1uX3NpemUo dG9fdHN2ZWN0b3IoJiMzOTtTSU1EIGlzIEdPT0QmIzM5OykpOzxicj4NCiZndDvCoCBwZ19jb2x1 bW5fc2l6ZTxicj4NCiZndDsgLS0tLS0tLS0tLS0tLS0tLTxicj4NCiZndDvCoCDCoCDCoCDCoCDC oCDCoCDCoCAzMjxicj4NCiZndDsgPGJyPg0KJmd0OyBpdHMgdGV4dCByZXByZXNlbnRhdGlvbiBp cyBsZXNzIHRoYW4gc2l6ZW9mKFZlY3RvcjgpIHNvIGN1cnJlbnRseSB2MyB3b3VsZDxicj4NCiZn dDsgZW50ZXIgU0lNRCBwYXRoIGFuZCBleGl0IG91dCBqdXN0IGZyb20gdGhlIGJlZ2lubmluZyAo dHdvIGV4dHJhIGJyYW5jaGVzKTxicj4NCiZndDsgYmVjYXVzZSBpdCBkb2VzIHRoaXM6PGJyPg0K Jmd0OyArIGlmIChUdXBsZURlc2NBdHRyKHR1cF9kZXNjLCBhdHRudW0gLSAxKS0mZ3Q7YXR0bGVu ID09IC0xICZhbXA7JmFtcDs8YnI+DQomZ3Q7ICsgVkFSU0laRV9BTllfRVhIRFIoRGF0dW1HZXRQ b2ludGVyKHZhbHVlKSkgJmd0OyBzaXplb2YoVmVjdG9yOCkpPGJyPg0KJmd0OyA8YnI+DQomZ3Q7 IEkgdGhvdWdodCBtYXliZSB3ZSBjb3VsZCBkbyAqIDIgb3IgKiA0IGl0cyBiaW5hcnkgc2l6ZSwg ZGVwZW5kcyBvbiB0aGUgdHlwZTxicj4NCiZndDsgcmVhbGx5IGJ1dCB0aGlzIGlzIGp1c3QgYSBw cm9wb3NpdGlvbiBpZiB0aGlzIGNhc2UgaXMgc29tZXRoaW5nIGNvbmNlcm5pbmcuPGJyPg0KPGJy Pg0KQ2FuIHdlIG1lYXN1cmUgdGhlIGltcGFjdCBvZiB0aGlzP8KgIEhvdyBsaWtlbHkgaXMgdGhp cyBjYXNlPzxicj48L2Jsb2NrcXVvdGU+PGRpdj5JJiMzOTtsbCByZXNwb25kIHRvIHRoaXMgc2Vw YXJhdGVsecKgaW4gYSBkaWZmZXJlbnQgZW1haWwuPC9kaXY+PGJsb2NrcXVvdGUgY2xhc3M9Imdt YWlsX3F1b3RlIiBzdHlsZT0ibWFyZ2luOjBweCAwcHggMHB4IDAuOGV4O2JvcmRlci1sZWZ0OjFw eCBzb2xpZCByZ2IoMjA0LDIwNCwyMDQpO3BhZGRpbmctbGVmdDoxZXgiPg0KPGJyPg0KJmd0OyAr c3RhdGljIHBnX2F0dHJpYnV0ZV9hbHdheXNfaW5saW5lIHZvaWQgQ29weUF0dHJpYnV0ZU91dFRl eHQoQ29weVRvU3RhdGUgY3N0YXRlLCBjb25zdCBjaGFyICpzdHJpbmcsPGJyPg0KJmd0OyArwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqBib29sIHVzZV9zaW1kLCBzaXplX3QgbGVuKTs8YnI+DQomZ3Q7ICtzdGF0aWMgcGdfYXR0cmli dXRlX2Fsd2F5c19pbmxpbmUgdm9pZCBDb3B5QXR0cmlidXRlT3V0Q1NWKENvcHlUb1N0YXRlIGNz dGF0ZSwgY29uc3QgY2hhciAqc3RyaW5nLDxicj4NCiZndDsgK8KgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIGJvb2wgdXNlX3F1b3RlLCBib29sIHVz ZV9zaW1kLCBzaXplX3QgbGVuKTs8YnI+DQo8YnI+DQpDYW4geW91IHRlc3QgdGhpcyBvbiBpdHMg b3duLCB0b28/wqAgV2UgbWlnaHQgYmUgYWJsZSB0byBzZXBhcmF0ZSB0aGlzIGFuZDxicj4NCnRo ZSBjaGFuZ2UgYmVsb3cgaW50byBhIHByZXJlcXVpc2l0ZSBwYXRjaCwgYXNzdW1pbmcgdGhleSBz aG93IGJlbmVmaXRzLjxicj48L2Jsb2NrcXVvdGU+PGRpdj48ZGl2PkkgdGVzdGVkIGlubGluaW5n IGFsb25lIGFuZCBmb3VuZCB0aGUgcmVzdWx0cyB3ZXJlIGFib3V0IGFuIGltcHJvdmVtZW50IG9m IDElIHRvIDQlIGFjcm9zcyBhbGwgY29uZmlndXJhdGlvbnMuPC9kaXY+PGRpdj5UaGUgaW5saW5p bmcgaXMgb25seSBtZWFuaW5nZnVsIGluIGNvbWJpbmF0aW9uIHdpdGggdGhlIFNJTUQgd29yaywg Zm9yIHRoZSByZWFzb24gZGVzY3JpYmVkIGJlbG93LsKgPC9kaXY+PC9kaXY+PGJsb2NrcXVvdGUg Y2xhc3M9ImdtYWlsX3F1b3RlIiBzdHlsZT0ibWFyZ2luOjBweCAwcHggMHB4IDAuOGV4O2JvcmRl ci1sZWZ0OjFweCBzb2xpZCByZ2IoMjA0LDIwNCwyMDQpO3BhZGRpbmctbGVmdDoxZXgiPg0KPGJy Pg0KJmd0O8KgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgaWYgKGlzX2Nzdik8YnI+ DQomZ3Q7IC3CoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoENvcHlB dHRyaWJ1dGVPdXRDU1YoY3N0YXRlLCBzdHJpbmcsPGJyPg0KJmd0OyAtwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqBjc3RhdGUtJmd0O29wdHMuZm9yY2VfcXVv dGVfZmxhZ3NbYXR0bnVtIC0gMV0pOzxicj4NCiZndDsgK8KgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgezxicj4NCiZndDsgK8KgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgaWYgKHVzZV9zaW1kKTxicj4NCiZndDsgK8KgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgQ29weUF0dHJpYnV0ZU91dENTVihjc3Rh dGUsIHN0cmluZyw8YnI+DQomZ3Q7ICvCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoGNzdGF0ZS0mZ3Q7b3B0cy5mb3JjZV9xdW90ZV9mbGFn c1thdHRudW0gLSAxXSw8YnI+DQomZ3Q7ICvCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDC oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoHRydWUsIGxlbik7PGJyPg0KJmd0OyArwqAgwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqBlbHNlPGJyPg0KJmd0OyArwqAg wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqBDb3B5 QXR0cmlidXRlT3V0Q1NWKGNzdGF0ZSwgc3RyaW5nLDxicj4NCiZndDsgK8KgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgY3N0YXRlLSZndDtv cHRzLmZvcmNlX3F1b3RlX2ZsYWdzW2F0dG51bSAtIDFdLDxicj4NCiZndDsgK8KgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgZmFsc2UsIGxl bik7PGJyPg0KPGJyPg0KVGhlcmUgaXNuJiMzOTt0IGEgdGVycmlibGUgYW1vdW50IG9mIGJyYW5j aGluZyBvbiB1c2Vfc2ltZCBpbiB0aGVzZSBmdW5jdGlvbnMsPGJyPg0Kc28gSSYjMzk7bSBhIGxp dHRsZSBza2VwdGljYWwgdGhpcyBtYWtlcyBtdWNoIGRpZmZlcmVuY2UuwqAgQXMgYWJvdmUsIGl0 IHdvdWxkPGJyPg0KYmUgZ29vZCB0byBtZWFzdXJlIGl0PC9ibG9ja3F1b3RlPjxkaXY+SSBjb21w aWxlZCB0aHJlZSB2YXJpYW50czxicj48YnI+djM6IHVzZV9zaW1kIHBhc3NlZCBhcyBjb21waWxl LXRpbWUsIENvcHlBdHRyaWJ1dGUgZnVuY3Rpb25zIGlubGluZWQuPGJyPnYzX3ZhcmlhYmxlOiB1 c2Vfc2ltZCBhcyBpcyB2YXJpYWJsZSwgQ29weUF0dHJpYnV0ZSBmdW5jdGlvbnMgaW5saW5lZC48 YnI+djNfdmFyaWFibGVfbm9pbmxpbmU6IHVzZV9zaW1kIGFzIGlzIHZhcmlhYmxlLCBDb3B5QXR0 cmlidXRlIGZ1bmN0aW9ucyBhcmUgbm90IGlubGluZWQuPC9kaXY+PGRpdj48YnI+PC9kaXY+PGRp dj5Ob25lIG9mIHRoZSBoZWxwZXJzIGFyZSBleHBsaWNpdGx5IGlubGluZWQgYnkgdXMuPC9kaXY+ PGRpdj48YnI+VGhlIGFzc2VtYmx5IHJldmVhbHMgdHdvIHRoaW5nczo8YnI+MSkgVGhlIENTViBT SU1EIGhlbHBlcnMgKENvcHlDaGVja0NTVlF1b3RlTmVlZFNJTUQsIENvcHlTa2lwQ1NWRXNjYXBl U0lNRCkgYXJlIGlubGluZWQgYnkgdGhlIGNvbXBpbGVyIG5hdHVyYWxseSBpbiBhbGw8L2Rpdj48 ZGl2PnRocmVlIHZhcmlhbnRzLCBDb3B5U2tpcFRleHRTSU1EIGlzIG5ldmVyIGlubGluZWQgYnkg dGhlIGNvbXBpbGVyIGluIGFueSB2YXJpYW50Ljxicj48YnI+MikgVGhlIGNvbnN0YW50LWVtaXR0 aW5nIGFwcHJvYWNoICh2MykgZG9lcyBtYXR0ZXIgKGp1c3QgYSBsaXR0bGUgYXBwYXJlbnRseSkg c3BlY2lmaWNhbGx5IGZvciBDb3B5U2tpcFRleHRTSU1ELsKgwqA8L2Rpdj48ZGl2Pkl0cyB0aGUg c2FtZSBzdG9yeSBhcyBDT1BZIEZST00gcGF0Y2gmIzM5O3MgZmlyc3TCoGNvbW1pdCBpdCBqdXN0 IGVtaXRzIGNvZGUgd2l0aG91dCB1c2Vfc2ltZCBicmFuY2g8YnI+wqAgwqAgwqBqYmUgwqAuLi4g wqAgOyBsZW4gJmd0OyBzaXplb2YoVmVjdG9yOCk8YnI+wqAgwqAgwqBqZSDCoCAuLi4gwqAgOyBu ZWVkX3RyYW5zY29kaW5nPGJyPsKgIMKgIMKgY2FsbCBDb3B5U2tpcFRleHRTSU1EPC9kaXY+PGRp dj48YnI+PC9kaXY+PGRpdj5XaGV0aGVyIHRoZSBleHRyYSBicmFuY2hpbmcgaW4gZm9yIGNvbnN0 YW50IHBhc3NpbmcgaXMgd29ydGggaXQgb3Igbm90IGlzIGRlbW9uc3RyYXRlZCBieSB0aGUgYmVu Y2htYXJrLjwvZGl2PjxkaXY+PGJyPjxicj7CoCBUZXN0IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg IE1hc3RlciDCoCDCoHYzIMKgIMKgIMKgIHYzX3ZhciDCoCB2M192YXJfbm9pbmw8YnI+wqAgVEVY VCBjbGVhbiDCoCDCoCDCoCDCoCDCoCAxNTA0bXPCoCDCoC0yNC4xJcKgIMKgLTIzLjAlwqAgwqAt MjEuNSU8YnI+wqAgQ1NWIGNsZWFuIMKgIMKgIMKgIMKgIMKgIMKgMTc2MG1zwqAgwqAtMzQuOSXC oCDCoC0zMi43JcKgIMKgLTMzLjAlPGJyPsKgIFRFWFQgMS8zIGJhY2tzbGFzaGVzwqAgwqAgwqAz NzYzbXPCoCDCoCArNC42JcKgIMKgICs2LjklwqAgwqArNC4xJTxicj7CoCBDU1YgMS8zIHF1b3Rl cyDCoCDCoCDCoCDCoCDCoCAzODg1bXPCoCDCoCArMy4xJcKgIMKgICsyLjclwqAgwqAgLTAuOCU8 YnI+PGJyPldpZGUgdGFibGUgVEVYVCAoaW50ZWdlciBjb2x1bW5zKTo8YnI+PGJyPsKgIENvbHMg wqAgwqBNYXN0ZXIgwqAgwqB2MyDCoCDCoCDCoCB2M192YXIgwqAgdjNfdmFyX25vaW5sPGJyPsKg IDUwIMKgIMKgIMKgMjA4M21zwqAgwqAtMC43JcKgIMKgIC0wLjYlwqAgwqAgKzMuNSU8YnI+wqAg MTAwIMKgIMKgIDQwOTRtc8KgIMKgLTAuMSXCoCDCoCAtMC41JcKgIMKgICs0LjUlPGJyPsKgIDIw MCDCoCDCoCAxNTYwbXPCoCDCoCswLjYlwqAgwqAgLTIuMyXCoCDCoCArMy4yJTxicj7CoCA1MDAg wqAgwqAgMTkwNW1zwqAgwqAtMS4wJcKgIMKgIC0xLjMlwqAgwqAgKzQuNyU8YnI+wqAgMTAwMCDC oCDCoDE0NTVtc8KgIMKgKzEuOCXCoCDCoCArMC40JcKgIMKgICs0LjMlPGJyPjxicj5XaWRlIHRh YmxlIENTVjo8YnI+PGJyPsKgIENvbHMgwqAgwqBNYXN0ZXIgwqAgwqB2MyDCoCDCoCDCoCB2M192 YXIgwqAgdjNfdmFyX25vaW5sPGJyPsKgIDUwIMKgIMKgIMKgMjQyMW1zwqAgwqArNC4wJcKgIMKg ICs2LjclwqAgwqAgKzUuOCU8YnI+wqAgMTAwIMKgIMKgIDQ5ODBtc8KgIMKgKzAuMSXCoCDCoCAr Mi4wJcKgIMKgIMKgKzAuMSU8YnI+wqAgMjAwIMKgIMKgIDE5MDFtc8KgIMKgKzEuNCXCoCDCoCAr My41JcKgIMKgICsxLjQlPGJyPsKgIDUwMCDCoCDCoCAyMzI4bXPCoCDCoCsxLjglwqAgwqAgKzIu NyXCoCDCoCArMi4yJTxicj7CoCAxMDAwIMKgIMKgMTgxNW1zwqAgwqArMi4wJcKgIMKgICsyLjgl wqAgwqAgKzIuNSU8L2Rpdj48ZGl2Pjxicj48L2Rpdj48ZGl2PkkmIzM5O20gbm90IHN1cmUgd2hl dGhlciB0aGVyZSYjMzk7cyBhIGRpZmbCoGJldHdlZW4gdjMgYW5kIHYzX3ZhciBwcmFjdGljYWxs eSBzcGVha2luZywgd2hhdCBkbyB5b3UgdGhpbmsgPzwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXY+ PGJyPjwvZGl2PjxkaXY+UmVnYXJkcyw8L2Rpdj48ZGl2PkF5b3ViPC9kaXY+PC9kaXY+PC9kaXY+ DQo8L2Rpdj4NCg== --000000000000057a4c064d4055bc--