<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<div class="markdown-here-wrapper" data-md-url="" style="">
<p style="margin: 0px 0px 1.2em !important;">It occurred to me
that, rather than speeding up access using an ALL segment, we
could use a very different tactic.</p>
<p style="margin: 0px 0px 1.2em !important;">Instead of having a
segment that encompasses the entire native heap, we could build
ad-hoc segments around the offset that needs to be accessed,
like this:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code class="hljs language-java" style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;display: block; overflow-x: auto; padding: 0.5em; color: rgb(51, 51, 51); background: rgb(248, 248, 248) none repeat scroll 0% 0%; -moz-text-size-adjust: none;"><span class="hljs-keyword" style="color: rgb(51, 51, 51); font-weight: bold;">long</span> offset = < accessed offset >
MemorySegment wrapper = MemorySegment.ofAddress(offset).reinterpret(<span class="hljs-number" style="color: rgb(0, 128, 128);">8</span>); <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// 8 is a size that is "big enough" for all accesses. Using another value is probably ok too</span>
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">One might argue that
this code would end up allocating a lot more objects. While
scalarization would typically do a good job at addressing issues
such as this (as the allocated segment is discared immediately
after use), it is possible that scalarization might fail due to
the client code not being inlined. But here’s the trick: we can
move all that mechanical code <em>inside</em> the var handle
itself. This way, all the allocation occurs inside the var
handle bubble, which has much more lax inlining budget (as do
method handles). In practice I don’t think it should be possible
to observe allocation when using such an adapted var handle.</p>
<p style="margin: 0px 0px 1.2em !important;">So, how do we adapt a
plain memory segment var handle so that we can use it with any
offset (even negative ones!!) ?</p>
<p style="margin: 0px 0px 1.2em !important;">First, let’s define
some method that does the above adaptation of a long address
into a memory segment:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;">static MemorySegment ofAddressUnsafe(long address) {
return MemorySegment.ofAddress(address).reinterpret(8);
}
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">Then, let’s create a
method handle that points there:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;">static final MethodHandle OF_ADDRESS_UNSAFE = ...
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">Then, create the
memory segment var handle as follows:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code class="hljs language-java" style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;display: block; overflow-x: auto; padding: 0.5em; color: rgb(51, 51, 51); background: rgb(248, 248, 248) none repeat scroll 0% 0%; -moz-text-size-adjust: none;"><span class="hljs-keyword" style="color: rgb(51, 51, 51); font-weight: bold;">static</span> <span class="hljs-keyword" style="color: rgb(51, 51, 51); font-weight: bold;">final</span> VarHandle BYTE_HANDLE = adaptSegmentHandle(JAVA_BYTE.varHandle());
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">(note: this is not
limited to var handles using JAVA_BYTE, you can use any input
layout).</p>
<p style="margin: 0px 0px 1.2em !important;">Where <code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;">adaptSegmentHandle</code>
is defined as follows:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code class="hljs language-java" style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;display: block; overflow-x: auto; padding: 0.5em; color: rgb(51, 51, 51); background: rgb(248, 248, 248) none repeat scroll 0% 0%; -moz-text-size-adjust: none;"><span class="hljs-function"><span class="hljs-keyword" style="color: rgb(51, 51, 51); font-weight: bold;">static</span> VarHandle <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">adaptSegmentHandle</span><span class="hljs-params">(VarHandle handle)</span> </span>{
handle = MethodHandles.insertCoordinates(handle,<span class="hljs-number" style="color: rgb(0, 128, 128);">1</span>, <span class="hljs-number" style="color: rgb(0, 128, 128);">0</span>L);
handle = MethodHandles.filterCoordinates(handle, <span class="hljs-number" style="color: rgb(0, 128, 128);">0</span>, OF_ADDRESS_UNSAFE);
<span class="hljs-keyword" style="color: rgb(51, 51, 51); font-weight: bold;">return</span> handle;
}
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">What the above code
does is:</p>
<ol style="margin: 1.2em 0px;padding-left: 2em;">
<li style="margin: 0.5em 0px;">it injects 0L as the offset used
by the var handle</li>
<li style="margin: 0.5em 0px;">it wraps the incoming long
address into a segment (using OF_ADDRESS_UNSAFE), which is
then injected into the var handle</li>
</ol>
<p style="margin: 0px 0px 1.2em !important;">In other words, we
now have a var handle that takes a simple long parameter (no
more segments). This parameter (either positive, or negative)
will be wrapped in a fresh memory segment with a known big
enough bound. Since all the allocation occurs inside the var
handle, we expect the JIT to reliably scalarize all the memory
segment instances we create in the process.</p>
<p style="margin: 0px 0px 1.2em !important;">And, indeed, the
binary search benchmark does a remarkable jump towards
Unsafe-like performance:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;">Benchmark Mode Cnt Score Error Units
BinarySearch.binarySearch_panama avgt 30 27.871 ? 0.350 ns/op
BinarySearch.binarySearch_unsafe avgt 30 27.470 ? 0.145 ns/op
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">GC activity is zero
(everything is scalarized), and that should happen quite
reliably (e.g. not an artifact of using JMH).</p>
<p style="margin: 0px 0px 1.2em !important;">This approach should
also scale to memory copy and other static routines defined in
MemorySegment (I have not tried that). E.g. one could obtain a
method handle that points to MemorySegment::copy, and then adapt
the incoming MS parameters in the same way.</p>
<p style="margin: 0px 0px 1.2em !important;">Cheers<br>
Maurizio</p>
<p style="margin: 0px 0px 1.2em !important;">On 21/07/2023 18:08,
Maurizio Cimadamore wrote:</p>
<blockquote style="margin: 1.2em 0px;border-left: 4px solid
rgb(221, 221, 221); padding: 0px 1em; color: rgb(119, 119, 119);
quotes: none;">
<p style="margin: 0px 0px 1.2em !important;">Hi Brian, I have
been playing some more with the benchmark you<br>
provided here:</p>
<p style="margin: 0px 0px 1.2em !important;"><a href="https://gist.github.com/broneill/3a39051635e3cb758d0cca5a963c685e" class="moz-txt-link-freetext">https://gist.github.com/broneill/3a39051635e3cb758d0cca5a963c685e</a></p>
<p style="margin: 0px 0px 1.2em !important;">The following patch
seems to help quite significantly:</p>
<p style="margin: 0px 0px 1.2em !important;"><a href="https://cr.openjdk.org/~mcimadamore/panama/segment_normalize_offsets.patch" class="moz-txt-link-freetext">https://cr.openjdk.org/~mcimadamore/panama/segment_normalize_offsets.patch</a></p>
<p style="margin: 0px 0px 1.2em !important;">Here are the
benchmark results:</p>
<p style="margin: 0px 0px 1.2em !important;">(before)</p>
<p style="margin: 0px 0px 1.2em !important;">|Benchmark Mode Cnt
Score Error Units<br>
BinarySearch.binarySearch_panama avgt 30 61.404 ? 1.315 ns/op<br>
BinarySearch.binarySearch_unsafe avgt 30 28.099 ? 0.330 ns/op</p>
<p style="margin: 0px 0px 1.2em !important;">|</p>
<p style="margin: 0px 0px 1.2em !important;">(after)</p>
<p style="margin: 0px 0px 1.2em !important;">|Benchmark Mode Cnt
Score Error Units<br>
BinarySearch.binarySearch_panama avgt 30 38.103 ? 0.471 ns/op<br>
BinarySearch.binarySearch_unsafe avgt 30 27.556 ? 0.208 ns/op</p>
<p style="margin: 0px 0px 1.2em !important;">|</p>
<p style="margin: 0px 0px 1.2em !important;">We believe there
must be some C2 issue lurking in there - basically<br>
we’re forcing all offsets to be positive using this:</p>
<p style="margin: 0px 0px 1.2em !important;">|@ForceInline
private static long normalize(long offset) { if<br>
(offset < 0) { throw new IndexOutOfBoundsException(“Offset
is < 0”);<br>
} return offset & Long.MAX_VALUE; }</p>
<p style="margin: 0px 0px 1.2em !important;">|</p>
<p style="margin: 0px 0px 1.2em !important;">Now, surprisingly,
if the “& Long.MAX_VALUE” is dropped, performance<br>
dips again. I believe we lose track of the positivity here and
the<br>
above patch provides some kind of workaround.</p>
<p style="margin: 0px 0px 1.2em !important;">I’d be interested
to know if this patch helps the situation with<br>
your bigger benchmark, or if things stay the same (in which
case, we<br>
would have to conclude that this benchmark, while interesting,
is<br>
perhaps not reflective of the issues in your bigger codebase).</p>
<p style="margin: 0px 0px 1.2em !important;">Thanks Maurizio</p>
<p style="margin: 0px 0px 1.2em !important;">On 06/07/2023
23:19, Brian S O’Neill wrote:</p>
<blockquote style="margin: 1.2em 0px;border-left: 4px solid
rgb(221, 221, 221); padding: 0px 1em; color: rgb(119, 119,
119); quotes: none;">
<p style="margin: 0px 0px 1.2em !important;">When I use the
“ALL” MemorySegment instead of allocating<br>
MemorySegments on the fly for copies, the performance
regression<br>
drops to ~2%.<br>
</p>
</blockquote>
</blockquote>
<div title="MDH:SXQgb2NjdXJyZWQgdG8gbWUgdGhhdCwgcmF0aGVyIHRoYW4gc3BlZWRpbmcgdXAgYWNjZXNzIHVzaW5nIGFuIEFMTCBzZWdtZW50LCB3ZSBjb3VsZCB1c2UgYSB2ZXJ5IGRpZmZlcmVudCB0YWN0aWMu
PGJyPjxicj5JbnN0ZWFkIG9mIGhhdmluZyBhIHNlZ21lbnQgdGhhdCBlbmNvbXBhc3NlcyB0aGUg
ZW50aXJlIG5hdGl2ZSBoZWFwLCB3ZSBjb3VsZCBidWlsZCBhZC1ob2Mgc2VnbWVudHMgYXJvdW5k
IHRoZSBvZmZzZXQgdGhhdCBuZWVkcyB0byBiZSBhY2Nlc3NlZCwgbGlrZSB0aGlzOjxicj48YnI+
YGBgamF2YTxicj5sb25nIG9mZnNldCA9ICZsdDsgYWNjZXNzZWQgb2Zmc2V0ICZndDs8YnI+TWVt
b3J5U2VnbWVudCB3cmFwcGVyID0gTWVtb3J5U2VnbWVudC5vZkFkZHJlc3Mob2Zmc2V0KS5yZWlu
dGVycHJldCg4KTsgLy8gOCBpcyBhIHNpemUgdGhhdCBpcyAiYmlnIGVub3VnaCIgZm9yIGFsbCBh
Y2Nlc3Nlcy4gVXNpbmcgYW5vdGhlciB2YWx1ZSBpcyBwcm9iYWJseSBvayB0b288YnI+YGBgPGJy
Pjxicj5PbmUgbWlnaHQgYXJndWUgdGhhdCB0aGlzIGNvZGUgd291bGQgZW5kIHVwIGFsbG9jYXRp
bmcgYSBsb3QgbW9yZSBvYmplY3RzLiBXaGlsZSBzY2FsYXJpemF0aW9uIHdvdWxkIHR5cGljYWxs
eSBkbyBhIGdvb2Qgam9iIGF0IGFkZHJlc3NpbmcgaXNzdWVzIHN1Y2ggYXMgdGhpcyAoYXMgdGhl
IGFsbG9jYXRlZCBzZWdtZW50IGlzIGRpc2NhcmVkIGltbWVkaWF0ZWx5IGFmdGVyIHVzZSksIGl0
IGlzIHBvc3NpYmxlIHRoYXQgc2NhbGFyaXphdGlvbiBtaWdodCBmYWlsIGR1ZSB0byB0aGUgY2xp
ZW50IGNvZGUgbm90IGJlaW5nIGlubGluZWQuIEJ1dCBoZXJlJ3MgdGhlIHRyaWNrOiB3ZSBjYW4g
bW92ZSBhbGwgdGhhdCBtZWNoYW5pY2FsIGNvZGUgX2luc2lkZV8gdGhlIHZhciBoYW5kbGUgaXRz
ZWxmLiBUaGlzIHdheSwgYWxsIHRoZSBhbGxvY2F0aW9uIG9jY3VycyBpbnNpZGUgdGhlIHZhciBo
YW5kbGUgYnViYmxlLCB3aGljaCBoYXMgbXVjaCBtb3JlIGxheCBpbmxpbmluZyBidWRnZXQgKGFz
IGRvIG1ldGhvZCBoYW5kbGVzKS4gSW4gcHJhY3RpY2UgSSBkb24ndCB0aGluayBpdCBzaG91bGQg
YmUgcG9zc2libGUgdG8gb2JzZXJ2ZSBhbGxvY2F0aW9uIHdoZW4gdXNpbmcgc3VjaCBhbiBhZGFw
dGVkIHZhciBoYW5kbGUuPGJyPjxicj5TbywgaG93IGRvIHdlIGFkYXB0IGEgcGxhaW4gbWVtb3J5
IHNlZ21lbnQgdmFyIGhhbmRsZSBzbyB0aGF0IHdlIGNhbiB1c2UgaXQgd2l0aCBhbnkgb2Zmc2V0
IChldmVuIG5lZ2F0aXZlIG9uZXMhISkgPzxicj48YnI+Rmlyc3QsIGxldCdzIGRlZmluZSBzb21l
IG1ldGhvZCB0aGF0IGRvZXMgdGhlIGFib3ZlIGFkYXB0YXRpb24gb2YgYSBsb25nIGFkZHJlc3Mg
aW50byBhIG1lbW9yeSBzZWdtZW50Ojxicj48YnI+YGBgPGJyPnN0YXRpYyBNZW1vcnlTZWdtZW50
IG9mQWRkcmVzc1Vuc2FmZShsb25nIGFkZHJlc3MpIHs8YnI+wqDCoMKgwqAgcmV0dXJuIE1lbW9y
eVNlZ21lbnQub2ZBZGRyZXNzKGFkZHJlc3MpLnJlaW50ZXJwcmV0KDgpOzxicj59PGJyPmBgYDxi
cj48YnI+VGhlbiwgbGV0J3MgY3JlYXRlIGEgbWV0aG9kIGhhbmRsZSB0aGF0IHBvaW50cyB0aGVy
ZTo8YnI+PGJyPmBgYDxicj5zdGF0aWMgZmluYWwgTWV0aG9kSGFuZGxlIE9GX0FERFJFU1NfVU5T
QUZFID0gLi4uPGJyPmBgYDxicj48YnI+VGhlbiwgY3JlYXRlIHRoZSBtZW1vcnkgc2VnbWVudCB2
YXIgaGFuZGxlIGFzIGZvbGxvd3M6PGJyPjxicj5gYGBqYXZhPGJyPnN0YXRpYyBmaW5hbCBWYXJI
YW5kbGUgQllURV9IQU5ETEUgPSBhZGFwdFNlZ21lbnRIYW5kbGUoSkFWQV9CWVRFLnZhckhhbmRs
ZSgpKTs8YnI+YGBgPGJyPjxicj4obm90ZTogdGhpcyBpcyBub3QgbGltaXRlZCB0byB2YXIgaGFu
ZGxlcyB1c2luZyBKQVZBX0JZVEUsIHlvdSBjYW4gdXNlIGFueSBpbnB1dCBsYXlvdXQpLjxicj48
YnI+V2hlcmUgYGFkYXB0U2VnbWVudEhhbmRsZWAgaXMgZGVmaW5lZCBhcyBmb2xsb3dzOjxicj48
YnI+YGBgamF2YTxicj5zdGF0aWMgVmFySGFuZGxlIGFkYXB0U2VnbWVudEhhbmRsZShWYXJIYW5k
bGUgaGFuZGxlKSB7PGJyPsKgwqDCoMKgwqDCoMKgIGhhbmRsZSA9IE1ldGhvZEhhbmRsZXMuaW5z
ZXJ0Q29vcmRpbmF0ZXMoaGFuZGxlLDEsIDBMKTs8YnI+wqDCoMKgwqDCoMKgwqAgaGFuZGxlID0g
TWV0aG9kSGFuZGxlcy5maWx0ZXJDb29yZGluYXRlcyhoYW5kbGUsIDAsIE9GX0FERFJFU1NfVU5T
QUZFKTs8YnI+wqDCoMKgwqDCoMKgwqAgcmV0dXJuIGhhbmRsZTs8YnI+fTxicj5gYGA8YnI+PGJy
PldoYXQgdGhlIGFib3ZlIGNvZGUgZG9lcyBpczo8YnI+PGJyPjEuIGl0IGluamVjdHMgMEwgYXMg
dGhlIG9mZnNldCB1c2VkIGJ5IHRoZSB2YXIgaGFuZGxlPGJyPjIuIGl0IHdyYXBzIHRoZSBpbmNv
bWluZyBsb25nIGFkZHJlc3MgaW50byBhIHNlZ21lbnQgKHVzaW5nIE9GX0FERFJFU1NfVU5TQUZF
KSwgd2hpY2ggaXMgdGhlbiBpbmplY3RlZCBpbnRvIHRoZSB2YXIgaGFuZGxlPGJyPjxicj5JbiBv
dGhlciB3b3Jkcywgd2Ugbm93IGhhdmUgYSB2YXIgaGFuZGxlIHRoYXQgdGFrZXMgYSBzaW1wbGUg
bG9uZyBwYXJhbWV0ZXIgKG5vIG1vcmUgc2VnbWVudHMpLiBUaGlzIHBhcmFtZXRlciAoZWl0aGVy
IHBvc2l0aXZlLCBvciBuZWdhdGl2ZSkgd2lsbCBiZSB3cmFwcGVkIGluIGEgZnJlc2ggbWVtb3J5
IHNlZ21lbnQgd2l0aCBhIGtub3duIGJpZyBlbm91Z2ggYm91bmQuIFNpbmNlIGFsbCB0aGUgYWxs
b2NhdGlvbiBvY2N1cnMgaW5zaWRlIHRoZSB2YXIgaGFuZGxlLCB3ZSBleHBlY3QgdGhlIEpJVCB0
byByZWxpYWJseSBzY2FsYXJpemUgYWxsIHRoZSBtZW1vcnkgc2VnbWVudCBpbnN0YW5jZXMgd2Ug
Y3JlYXRlIGluIHRoZSBwcm9jZXNzLjxicj48YnI+QW5kLCBpbmRlZWQsIHRoZSBiaW5hcnkgc2Vh
cmNoIGJlbmNobWFyayBkb2VzIGEgcmVtYXJrYWJsZSBqdW1wIHRvd2FyZHMgVW5zYWZlLWxpa2Ug
cGVyZm9ybWFuY2U6PGJyPjxicj5gYGA8YnI+QmVuY2htYXJrwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIE1vZGXCoCBDbnTCoMKgIFNjb3JlwqDCoCBFcnJv
csKgIFVuaXRzPGJyPkJpbmFyeVNlYXJjaC5iaW5hcnlTZWFyY2hfcGFuYW1hwqAgYXZndMKgwqAg
MzDCoCAyNy44NzEgPyAwLjM1MMKgIG5zL29wPGJyPkJpbmFyeVNlYXJjaC5iaW5hcnlTZWFyY2hf
dW5zYWZlwqAgYXZndMKgwqAgMzDCoCAyNy40NzAgPyAwLjE0NcKgIG5zL29wPGJyPmBgYDxicj48
YnI+R0MgYWN0aXZpdHkgaXMgemVybyAoZXZlcnl0aGluZyBpcyBzY2FsYXJpemVkKSwgYW5kIHRo
YXQgc2hvdWxkIGhhcHBlbiBxdWl0ZSByZWxpYWJseSAoZS5nLiBub3QgYW4gYXJ0aWZhY3Qgb2Yg
dXNpbmcgSk1IKS48YnI+PGJyPlRoaXMgYXBwcm9hY2ggc2hvdWxkIGFsc28gc2NhbGUgdG8gbWVt
b3J5IGNvcHkgYW5kIG90aGVyIHN0YXRpYyByb3V0aW5lcyBkZWZpbmVkIGluIE1lbW9yeVNlZ21l
bnQgKEkgaGF2ZSBub3QgdHJpZWQgdGhhdCkuIEUuZy4gb25lIGNvdWxkIG9idGFpbiBhIG1ldGhv
ZCBoYW5kbGUgdGhhdCBwb2ludHMgdG8gTWVtb3J5U2VnbWVudDo6Y29weSwgYW5kIHRoZW4gYWRh
cHQgdGhlIGluY29taW5nIE1TIHBhcmFtZXRlcnMgaW4gdGhlIHNhbWUgd2F5Ljxicj48YnI+PGJy
PkNoZWVyczxicj5NYXVyaXppbzxicj48YnI+T24gMjEvMDcvMjAyMyAxODowOCwgTWF1cml6aW8g
Q2ltYWRhbW9yZSB3cm90ZTo8YnI+PHNwYW4gc3R5bGU9IndoaXRlLXNwYWNlOiBwcmUtd3JhcDsg
ZGlzcGxheTogYmxvY2s7IHdpZHRoOiA5OHZ3OyI+Jmd0OyA8YnI+Jmd0OyBIaSBCcmlhbiwgSSBo
YXZlIGJlZW4gcGxheWluZyBzb21lIG1vcmUgd2l0aCB0aGUgYmVuY2htYXJrIHlvdSA8YnI+Jmd0
OyBwcm92aWRlZCBoZXJlOjxicj4mZ3Q7IDxicj4mZ3Q7IGh0dHBzOi8vZ2lzdC5naXRodWIuY29t
L2Jyb25laWxsLzNhMzkwNTE2MzVlM2NiNzU4ZDBjY2E1YTk2M2M2ODVlPGJyPiZndDsgPGJyPiZn
dDsgVGhlIGZvbGxvd2luZyBwYXRjaCBzZWVtcyB0byBoZWxwIHF1aXRlIHNpZ25pZmljYW50bHk6
PGJyPiZndDsgPGJyPiZndDsgaHR0cHM6Ly9jci5vcGVuamRrLm9yZy9+bWNpbWFkYW1vcmUvcGFu
YW1hL3NlZ21lbnRfbm9ybWFsaXplX29mZnNldHMucGF0Y2g8YnI+Jmd0Ozxicj4mZ3Q7PGJyPiZn
dDs8YnI+Jmd0OyA8YnI+PC9zcGFuPkhlcmUgYXJlIHRoZSBiZW5jaG1hcmsgcmVzdWx0czo8YnI+
PHNwYW4gc3R5bGU9IndoaXRlLXNwYWNlOiBwcmUtd3JhcDsgZGlzcGxheTogYmxvY2s7IHdpZHRo
OiA5OHZ3OyI+Jmd0OyA8YnI+Jmd0OyAoYmVmb3JlKTxicj4mZ3Q7IDxicj4mZ3Q7IHxCZW5jaG1h
cmsgICAgICAgICAgICAgICAgICAgICAgICAgTW9kZSAgQ250ICAgU2NvcmUgICBFcnJvciAgVW5p
dHMgPGJyPiZndDsgQmluYXJ5U2VhcmNoLmJpbmFyeVNlYXJjaF9wYW5hbWEgIGF2Z3QgICAzMCAg
NjEuNDA0ID8gMS4zMTUgIG5zL29wIDxicj4mZ3Q7IEJpbmFyeVNlYXJjaC5iaW5hcnlTZWFyY2hf
dW5zYWZlICBhdmd0ICAgMzAgIDI4LjA5OSA/IDAuMzMwICBucy9vcDxicj4mZ3Q7IDxicj4mZ3Q7
IHw8YnI+Jmd0OyA8YnI+Jmd0OyAoYWZ0ZXIpPGJyPiZndDsgPGJyPiZndDsgfEJlbmNobWFyayAg
ICAgICAgICAgICAgICAgICAgICAgICBNb2RlICBDbnQgICBTY29yZSAgIEVycm9yICBVbml0cyA8
YnI+Jmd0OyBCaW5hcnlTZWFyY2guYmluYXJ5U2VhcmNoX3BhbmFtYSAgYXZndCAgIDMwICAzOC4x
MDMgPyAwLjQ3MSAgbnMvb3AgPGJyPiZndDsgQmluYXJ5U2VhcmNoLmJpbmFyeVNlYXJjaF91bnNh
ZmUgIGF2Z3QgICAzMCAgMjcuNTU2ID8gMC4yMDggIG5zL29wPGJyPiZndDsgPGJyPiZndDsgfDxi
cj4mZ3Q7IDxicj4mZ3Q7IFdlIGJlbGlldmUgdGhlcmUgbXVzdCBiZSBzb21lIEMyIGlzc3VlIGx1
cmtpbmcgaW4gdGhlcmUgLSBiYXNpY2FsbHkgPGJyPiZndDsgd2XigJlyZSBmb3JjaW5nIGFsbCBv
ZmZzZXRzIHRvIGJlIHBvc2l0aXZlIHVzaW5nIHRoaXM6PGJyPiZndDsgPGJyPiZndDsgfEBGb3Jj
ZUlubGluZSBwcml2YXRlIHN0YXRpYyBsb25nIG5vcm1hbGl6ZShsb25nIG9mZnNldCkgeyBpZjxi
cj4mZ3Q7IChvZmZzZXQgJmx0OyAwKSB7IHRocm93IG5ldyBJbmRleE91dE9mQm91bmRzRXhjZXB0
aW9uKCJPZmZzZXQgaXMgJmx0OyAwIik7PGJyPiZndDsgfSByZXR1cm4gb2Zmc2V0ICZhbXA7IExv
bmcuTUFYX1ZBTFVFOyB9PGJyPiZndDsgPGJyPiZndDsgfDxicj4mZ3Q7IDxicj4mZ3Q7IE5vdywg
c3VycHJpc2luZ2x5LCBpZiB0aGUg4oCcJmFtcDsgTG9uZy5NQVhfVkFMVUXigJ0gaXMgZHJvcHBl
ZCwgcGVyZm9ybWFuY2UgPGJyPiZndDsgZGlwcyBhZ2Fpbi4gSSBiZWxpZXZlIHdlIGxvc2UgdHJh
Y2sgb2YgdGhlIHBvc2l0aXZpdHkgaGVyZSBhbmQgdGhlIDxicj4mZ3Q7IGFib3ZlIHBhdGNoIHBy
b3ZpZGVzIHNvbWUga2luZCBvZiB3b3JrYXJvdW5kLjxicj4mZ3Q7IDxicj4mZ3Q7IEnigJlkIGJl
IGludGVyZXN0ZWQgdG8ga25vdyBpZiB0aGlzIHBhdGNoIGhlbHBzIHRoZSBzaXR1YXRpb24gd2l0
aDxicj4mZ3Q7IHlvdXIgYmlnZ2VyIGJlbmNobWFyaywgb3IgaWYgdGhpbmdzIHN0YXkgdGhlIHNh
bWUgKGluIHdoaWNoIGNhc2UsIHdlPGJyPiZndDsgd291bGQgaGF2ZSB0byBjb25jbHVkZSB0aGF0
IHRoaXMgYmVuY2htYXJrLCB3aGlsZSBpbnRlcmVzdGluZywgaXM8YnI+Jmd0OyBwZXJoYXBzIG5v
dCByZWZsZWN0aXZlIG9mIHRoZSBpc3N1ZXMgaW4geW91ciBiaWdnZXIgY29kZWJhc2UpLjxicj4m
Z3Q7IDxicj4mZ3Q7IFRoYW5rcyBNYXVyaXppbzxicj4mZ3Q7IDxicj4mZ3Q7IE9uIDA2LzA3LzIw
MjMgMjM6MTksIEJyaWFuIFMgT+KAmU5laWxsIHdyb3RlOjxicj4mZ3Q7IDxicj4mZ3Q7Jmd0OyBX
aGVuIEkgdXNlIHRoZSAiQUxMIiBNZW1vcnlTZWdtZW50IGluc3RlYWQgb2YgYWxsb2NhdGluZyA8
YnI+Jmd0OyZndDsgTWVtb3J5U2VnbWVudHMgb24gdGhlIGZseSBmb3IgY29waWVzLCB0aGUgcGVy
Zm9ybWFuY2UgcmVncmVzc2lvbiA8YnI+Jmd0OyZndDsgZHJvcHMgdG8gfjIlLjxicj4mZ3Q7IOKA
izwvc3Bhbj48YnI+" style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0;"></div>
</div>
</body>
</html>