<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Romain Guy</title><link>https://www.romainguy.dev/posts/</link><description>Recent content in Posts on Romain Guy</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 07 Dec 2025 13:17:08 -0800</lastBuildDate><atom:link href="https://www.romainguy.dev/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Tap Detection on Arbitrary Shapes with Compose</title><link>https://www.romainguy.dev/posts/2025/arbitrary-shape-tap-detection/</link><pubDate>Sun, 07 Dec 2025 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2025/arbitrary-shape-tap-detection/</guid><description>&lt;p&gt;Detecting whether a user tapped on an arbitrary shape represented by a
&lt;a href="https://developer.android.com/reference/android/graphics/Path" class="external-link" target="_blank" rel="noopener"&gt;Path&lt;/a&gt; is an unfortunately difficult
task on Android. The &lt;code&gt;Path&lt;/code&gt; class does not offer any API to do this, and the workaround I have seen
folks use (representing the tap as a small rectangle/circle and computing the intersection with
the test shape) is inefficient and sometimes fails.&lt;/p&gt;
&lt;p&gt;Thankfully, Jetpack Compose a simple way to perform this task. The video below shows how the Compose
APIs can be used to detect taps, or generally perform any test requiring to check whether a specific
2D coordinate is contained inside a &lt;code&gt;Path&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Finger Shadows in Compose</title><link>https://www.romainguy.dev/posts/2025/finger-shadows/</link><pubDate>Sat, 29 Nov 2025 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2025/finger-shadows/</guid><description>&lt;p&gt;I recently realized that I have never discussed graphics programming on this blog, and decided
it was time to correct this. A few years ago, Android 13 introduced the
&lt;a href="https://developer.android.com/reference/android/graphics/RuntimeShader" class="external-link" target="_blank" rel="noopener"&gt;RuntimeShader&lt;/a&gt; API
that allows you to write custom GPU shaders and apply them to UI elements. In this blog post
I will show you how this API can be used to simulate shadows projected by the user&amp;rsquo;s finger (or
stylus/pointing device) onto your UI.&lt;/p&gt;</description></item><item><title>Merge Your Computations</title><link>https://www.romainguy.dev/posts/2025/merge-your-computations/</link><pubDate>Thu, 15 May 2025 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2025/merge-your-computations/</guid><description>&lt;p&gt;There is a simple but often overlooked technique to optimize performance-sensitive code: merging
(or manually inlining) functions. We often build series of low-level functions that execute various
computations that we then combine to perform higher-level tasks. When taken in isolation, each of
those functions does exactly what it should and might even be perfectly optimized. However, when a
series of thosefunctions work together, unnecessary or duplicated work might appear.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at a concrete example taken from the Jetpack Compose code base. To apply the various
geometric transforms that may affect a layer, Compose needs to build a matrix that combines all
the transformations exposed by its APIs:&lt;/p&gt;</description></item><item><title>Eliminating Array Bounds Checks</title><link>https://www.romainguy.dev/posts/2025/eliminating-array-bounds-checks/</link><pubDate>Tue, 13 May 2025 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2025/eliminating-array-bounds-checks/</guid><description>&lt;p&gt;The Android Runtime (ART) offers a nice memory safety feature when accessing the content of an
array. The indices you use are automatically checked against the bounds of the array to prevent
unsafe memory accesses. To achieve this, ART generates extra machine instructions to throw an
&lt;code&gt;ArrayIndexOutOfBoundsException&lt;/code&gt; when the index is invalid.&lt;/p&gt;
&lt;p&gt;Here is a simple Kotlin example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-1"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#c678dd"&gt;fun&lt;/span&gt; &lt;span style="color:#61afef;font-weight:bold"&gt;scaleZ&lt;/span&gt;(&lt;span style="color:#e06c75"&gt;values&lt;/span&gt;: &lt;span style="color:#e06c75"&gt;FloatArray&lt;/span&gt;, &lt;span style="color:#e06c75"&gt;scale&lt;/span&gt;: &lt;span style="color:#e06c75"&gt;Float&lt;/span&gt;) = &lt;span style="color:#e06c75"&gt;values&lt;/span&gt;[&lt;span style="color:#d19a66"&gt;2&lt;/span&gt;] * &lt;span style="color:#e06c75"&gt;scale&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After translation to arm64 assembly, we obtain the following result:&lt;/p&gt;</description></item><item><title>Naming is Hard</title><link>https://www.romainguy.dev/posts/2024/naming-is-hard/</link><pubDate>Thu, 19 Dec 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/naming-is-hard/</guid><description>&lt;p&gt;Before we dive into today&amp;rsquo;s topic, I would like to make it clear that what follows is specific to
how Android, and more precisely the Android RunTime (ART), works. Some of what follows applies to
other environments as well, but the main twist is about Android.&lt;/p&gt;
&lt;p&gt;If you have read my previous articles, you should know by now that seemingly small or irrelevant
changes can have a large impact on performance. So let&amp;rsquo;s look at another one! We&amp;rsquo;ll start our
journey with a class containing a bunch of fields. What they are and what they mean isn&amp;rsquo;t really
relevant for now, let&amp;rsquo;s just imagine we are dealing with a fairly large object&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;</description></item><item><title>The Path Not Taken</title><link>https://www.romainguy.dev/posts/2024/the-path-not-taken/</link><pubDate>Mon, 16 Dec 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/the-path-not-taken/</guid><description>&lt;p&gt;In the last post, we saw that
&lt;a href="../you-are-going-to-need-it" &gt;benchmarks don&amp;rsquo;t always measure what we think they measure&lt;/a&gt;.
Let&amp;rsquo;s look at another instance of this problem today, starting with this rather simple benchmark:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-1"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#61afef"&gt;@RunWith&lt;/span&gt;(&lt;span style="color:#e06c75"&gt;AndroidJUnit4&lt;/span&gt;&lt;span style="color:#56b6c2"&gt;::&lt;/span&gt;&lt;span style="color:#c678dd"&gt;class&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-2"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#c678dd"&gt;class&lt;/span&gt; &lt;span style="color:#e5c07b"&gt;DataBenchmark&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-3"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#61afef"&gt;@get&lt;/span&gt;:&lt;span style="color:#e06c75"&gt;Rule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-4"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#c678dd"&gt;val&lt;/span&gt; &lt;span style="color:#e06c75"&gt;benchmarkRule&lt;/span&gt; = &lt;span style="color:#e06c75"&gt;BenchmarkRule&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-5"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-6"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#7f848e"&gt;// Generate data in [0..255]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-7"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-7"&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#7f848e"&gt;&lt;/span&gt; &lt;span style="color:#c678dd"&gt;private&lt;/span&gt; &lt;span style="color:#c678dd"&gt;val&lt;/span&gt; &lt;span style="color:#e06c75"&gt;data&lt;/span&gt; = &lt;span style="color:#e06c75"&gt;IntArray&lt;/span&gt;(&lt;span style="color:#d19a66"&gt;65&lt;/span&gt;&lt;span style="color:#e06c75"&gt;_536&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-8"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-8"&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#c678dd"&gt;it&lt;/span&gt; % &lt;span style="color:#d19a66"&gt;256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-9"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-9"&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-10"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-10"&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-11"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-11"&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#61afef"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-12"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-12"&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#c678dd"&gt;fun&lt;/span&gt; &lt;span style="color:#61afef;font-weight:bold"&gt;processData&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-13"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-13"&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#c678dd"&gt;var&lt;/span&gt; &lt;span style="color:#e06c75"&gt;sum&lt;/span&gt; = &lt;span style="color:#d19a66"&gt;0f&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-14"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-14"&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#e06c75"&gt;benchmarkRule&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;measureRepeated&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-15"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-15"&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#c678dd"&gt;for&lt;/span&gt; (&lt;span style="color:#e06c75"&gt;d&lt;/span&gt; &lt;span style="color:#c678dd"&gt;in&lt;/span&gt; &lt;span style="color:#c678dd"&gt;data&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-16"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-16"&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#c678dd"&gt;if&lt;/span&gt; (&lt;span style="color:#e06c75"&gt;d&lt;/span&gt; &amp;lt; &lt;span style="color:#d19a66"&gt;128&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-17"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-17"&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#e06c75"&gt;sum&lt;/span&gt; &lt;span style="color:#56b6c2"&gt;+=&lt;/span&gt; &lt;span style="color:#e06c75"&gt;d&lt;/span&gt; / &lt;span style="color:#d19a66"&gt;128f&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-18"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-18"&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-19"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-19"&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-20"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-20"&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-21"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-21"&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#e5c07b"&gt;BlackHole&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;consume&lt;/span&gt;(&lt;span style="color:#e06c75"&gt;sum&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-22"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-22"&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-23"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-23"&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This benchmark tests an algorithm that processes an array of values between 0 and 255 (8-bit pixels
for instance), and computes the sum of the normalized values for pixels less than 128.&lt;/p&gt;</description></item><item><title>You Are Going to Need It</title><link>https://www.romainguy.dev/posts/2024/you-are-going-to-need-it/</link><pubDate>Fri, 13 Dec 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/you-are-going-to-need-it/</guid><description>&lt;p&gt;Optimizing code can be a difficult task because there are so many traps you need to avoid at every
step of the way. Today I want to focus on one of the (numerous) benchmarking traps, which you may
have run into, and that I myself encounter regularly.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s imagine you are trying to optimize code, and you
&lt;a href="https://bsky.app/profile/romainguy.dev/post/3lbxlo5h4qk2v" class="external-link" target="_blank" rel="noopener"&gt;notice&lt;/a&gt; the use of a &lt;code&gt;value.pow(2f)&lt;/code&gt;. One
obvious way to optimize this is to replace the function call with a multiplication (&lt;code&gt;value * value&lt;/code&gt;),
but is it worth it? Since you are a diligent engineer, you decide to write a microbenchmark to
compare before and after:&lt;/p&gt;</description></item><item><title>Optimization, Step by Step</title><link>https://www.romainguy.dev/posts/2024/optimization-step-by-step/</link><pubDate>Mon, 25 Nov 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/optimization-step-by-step/</guid><description>&lt;p&gt;&lt;a href="https://blurha.sh/" class="external-link" target="_blank" rel="noopener"&gt;BlurHash&lt;/a&gt; is a compact representation of placeholders for images. A blur hash
is encoded as a short string that can be rendered to a bitmap at runtime to display a &amp;ldquo;blurry&amp;rdquo;
version of the source image. The way it works remind me of &lt;a href="https://google.github.io/filament/Filament.html#lighting/imagebasedlights/processinglightprobes" class="external-link" target="_blank" rel="noopener"&gt;how spherical harmonics are used&lt;/a&gt;
in 3D rendering engines to efficiently encode irradiance.&lt;/p&gt;
&lt;p&gt;I recently remembered that I had been meaning to look at the Kotlin implementation of BlurHash to
see if there was a way to make it faster. I looked up the
&lt;a href="https://github.com/vanniktech/blurhash/" class="external-link" target="_blank" rel="noopener"&gt;KMP port&lt;/a&gt; and after a few changes, I was able to make
decoding a blur hash up to 4.35x faster on a Pixel 6 running Android 14. For all the measurements
mentioned in this article, I decoded a fixed blur hash to a 384x384 image. The original code took
24.4 ms to do this when running at full speed, and 41.5 ms when locking the CPU clocks. This is way
too long considering the low resolution of the image.&lt;/p&gt;</description></item><item><title>A Micro-optimization You Will Never Need</title><link>https://www.romainguy.dev/posts/2024/a-micro-optimization-you-will-never-need/</link><pubDate>Sun, 10 Nov 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/a-micro-optimization-you-will-never-need/</guid><description>&lt;p&gt;Today I would like to show you a micro-optimization I recently used more for the fun of it than for
its real impact. It&amp;rsquo;s an interesting trick that you should never bother to use, nor worry about.&lt;/p&gt;
&lt;p&gt;It starts from a piece of Kotlin code that looked a bit like this (the original version used named
constants, but I replaced them with their actual values for clarity in this context):&lt;/p&gt;</description></item><item><title>Down Another Rabbit Hole</title><link>https://www.romainguy.dev/posts/2024/down-another-rabbit-hole/</link><pubDate>Mon, 27 May 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/down-another-rabbit-hole/</guid><description>&lt;p&gt;&lt;a href="https://jakewharton.com/" class="external-link" target="_blank" rel="noopener"&gt;Jake Wharton&lt;/a&gt; recently caused me to go down yet another silly
optimization rabbit hole when he nonchalantly linked to
&lt;a href="https://github.com/square/okio/blob/c90794f83170d770eb247c56ade80c0a6661a5d6/okio/src/commonMain/kotlin/okio/internal/Buffer.kt#L464-L520" class="external-link" target="_blank" rel="noopener"&gt;a piece of code&lt;/a&gt;
used to count the number of digits in a &lt;code&gt;Long&lt;/code&gt; during a Slack conversation about Kotlin&amp;rsquo;s lack
of ternary operator. This of course triggered folks like &lt;a href="https://x.com/madisp" class="external-link" target="_blank" rel="noopener"&gt;Madis Pink&lt;/a&gt; and
me to want to optimize it…&lt;/p&gt;
&lt;h1 id="counting-digits"&gt;
 Counting digits
 &lt;a class="heading-link" href="#counting-digits"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;
 &lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The simplest way to count the number of digits would be to compute &lt;code&gt;log10(n).toInt() + 1&lt;/code&gt;, where &lt;code&gt;n&lt;/code&gt;
is our input number. Unfortunately logarithmic functions like
&lt;a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/log10.html#kotlin.math$log10%28kotlin.Double%29" class="external-link" target="_blank" rel="noopener"&gt;Kotlin&amp;rsquo;s log10&lt;/a&gt;
are only defined for floating point numbers. If our input is an &lt;code&gt;Int&lt;/code&gt; or a &lt;code&gt;Long&lt;/code&gt;, we could first
convert to &lt;code&gt;Double&lt;/code&gt; and then call &lt;code&gt;log10&lt;/code&gt;, but not all &lt;code&gt;Long&lt;/code&gt; values can be stored in a &lt;code&gt;Double&lt;/code&gt;
(any value above 2^53), and we would have to special case &lt;code&gt;0&lt;/code&gt;. We must therefore find a different
solution&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;</description></item><item><title>Down a Rabbit Hole</title><link>https://www.romainguy.dev/posts/2024/down-a-rabbit-hole/</link><pubDate>Sat, 18 May 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/down-a-rabbit-hole/</guid><description>&lt;p&gt;I &lt;a href="https://www.romainguy.dev/posts/2024/readability-of-optimized-kotlin-code/" &gt;recently discussed an optimization&lt;/a&gt;
that I worked on following &lt;a href="http://intelligiblebabble.com/" class="external-link" target="_blank" rel="noopener"&gt;Leland&lt;/a&gt;&amp;rsquo;s successful nerd snipe. That,
however, was not the end of it. He also needed to test for intersecting/overlapping rectangles. The
most obvious way to achieve this is pretty straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-1"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#7f848e"&gt;// A rectangle is defined by its left (l), top (t),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-2"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#7f848e"&gt;// right (r), and bottom (b) coordinates
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-3"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-3"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#7f848e"&gt;&lt;/span&gt;&lt;span style="color:#c678dd"&gt;data&lt;/span&gt; &lt;span style="color:#c678dd"&gt;class&lt;/span&gt; &lt;span style="color:#e5c07b"&gt;Rect&lt;/span&gt;(&lt;span style="color:#c678dd"&gt;val&lt;/span&gt; &lt;span style="color:#e06c75"&gt;l&lt;/span&gt;: &lt;span style="color:#e06c75"&gt;Int&lt;/span&gt;, &lt;span style="color:#c678dd"&gt;val&lt;/span&gt; &lt;span style="color:#e06c75"&gt;t&lt;/span&gt;: &lt;span style="color:#e06c75"&gt;Int&lt;/span&gt;, &lt;span style="color:#c678dd"&gt;val&lt;/span&gt; &lt;span style="color:#e06c75"&gt;r&lt;/span&gt;: &lt;span style="color:#e06c75"&gt;Int&lt;/span&gt;, &lt;span style="color:#c678dd"&gt;val&lt;/span&gt; &lt;span style="color:#e06c75"&gt;b&lt;/span&gt;: &lt;span style="color:#e06c75"&gt;Int&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-4"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-4"&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#c678dd"&gt;fun&lt;/span&gt; &lt;span style="color:#61afef;font-weight:bold"&gt;overlaps&lt;/span&gt;(&lt;span style="color:#e06c75"&gt;other&lt;/span&gt;: &lt;span style="color:#e06c75"&gt;Rect&lt;/span&gt;) =
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-5"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-5"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#e06c75"&gt;l&lt;/span&gt; &amp;lt; &lt;span style="color:#e06c75"&gt;other&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;r&lt;/span&gt; &lt;span style="color:#56b6c2"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#e06c75"&gt;other&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;l&lt;/span&gt; &amp;lt; &lt;span style="color:#e06c75"&gt;r&lt;/span&gt; &lt;span style="color:#56b6c2"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#e06c75"&gt;t&lt;/span&gt; &amp;lt; &lt;span style="color:#e06c75"&gt;other&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;b&lt;/span&gt; &lt;span style="color:#56b6c2"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#e06c75"&gt;other&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;t&lt;/span&gt; &amp;lt; &lt;span style="color:#e06c75"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-6"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-6"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The source code is nice and tidy, but the generated assembly is less than ideal (as always,
the code was optimized with R8 first):&lt;/p&gt;</description></item><item><title>Practical Optimizations at Android Makers 2024</title><link>https://www.romainguy.dev/posts/2024/practical-optimizations-at-android-makers-2024/</link><pubDate>Mon, 06 May 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/practical-optimizations-at-android-makers-2024/</guid><description>&lt;p&gt;I recently gave a talk called &lt;a href="https://www.youtube.com/watch?v=5cxw_fdpnoA" class="external-link" target="_blank" rel="noopener"&gt;Practical Optimizations&lt;/a&gt;
at &lt;a href="https://androidmakers.droidcon.com/" class="external-link" target="_blank" rel="noopener"&gt;Android Makers 2024&lt;/a&gt;. The talk is focused on
some of the low-level optimizations we implemented in
&lt;a href="https://developer.android.com/develop/ui/compose" class="external-link" target="_blank" rel="noopener"&gt;Jetpack Compose&lt;/a&gt;, their impact on performance,
and the programming techniques behind them. You will likely find this talk interesting if you&amp;rsquo;ve been
enjoying the articles about &lt;a href="https://www.romainguy.dev/tags/performance/" &gt;performance and optimizations&lt;/a&gt; on
this blog.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/5cxw_fdpnoA?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;</description></item><item><title>Readability of Optimized Kotlin Code</title><link>https://www.romainguy.dev/posts/2024/readability-of-optimized-kotlin-code/</link><pubDate>Fri, 03 May 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/readability-of-optimized-kotlin-code/</guid><description>&lt;p&gt;&lt;a href="http://intelligiblebabble.com/" class="external-link" target="_blank" rel="noopener"&gt;Leland&lt;/a&gt; and I were recently discussing how to best implement
a new data structure to speed up a specific aspect of Jetpack Compose. He came up with a great
idea, and &lt;a href="https://xkcd.com/356/" class="external-link" target="_blank" rel="noopener"&gt;nerd sniped&lt;/a&gt; me in the process. The problem was to efficiently
encode the occupancy of an 8x8 grid represented as a &lt;code&gt;Long&lt;/code&gt; (each bit representing a cell in the
grid).&lt;/p&gt;
&lt;p&gt;After coming up with the bit twiddling code that quickly &amp;ldquo;rasterizes&amp;rdquo; a rectangle into the grid
as a bitfield, I found myself thinking about how incredibly helpful Kotlin can be at making
low-level/micro-optimized code easy to read&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; for users of an API.&lt;/p&gt;</description></item><item><title>Speeding up isBlank()</title><link>https://www.romainguy.dev/posts/2024/speeding-up-isblank/</link><pubDate>Mon, 05 Feb 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/speeding-up-isblank/</guid><description>&lt;p&gt;I was recently optimizing a small part of the Jetpack Compose runtime when I stumbled upon
a seemingly harmless API,
&lt;a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/is-blank.html" class="external-link" target="_blank" rel="noopener"&gt;isBlank()&lt;/a&gt;. This API
return &lt;code&gt;true&lt;/code&gt; if the string it&amp;rsquo;s called on is empty or consists solely of whitespace characters.&lt;/p&gt;
&lt;p&gt;But is it truly harmless? Let&amp;rsquo;s look at the JVM implementation to get a better sense of what
it does:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-1"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style="color:#c678dd"&gt;public&lt;/span&gt; &lt;span style="color:#c678dd"&gt;actual&lt;/span&gt; &lt;span style="color:#c678dd"&gt;fun&lt;/span&gt; &lt;span style="color:#61afef;font-weight:bold"&gt;CharSequence&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;isBlank&lt;/span&gt;(): &lt;span style="color:#e06c75"&gt;Boolean&lt;/span&gt; =
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f" id="hl-0-2"&gt;&lt;a style="outline:none;text-decoration:none;color:inherit" href="#hl-0-2"&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style="color:#e06c75"&gt;length&lt;/span&gt; &lt;span style="color:#56b6c2"&gt;==&lt;/span&gt; &lt;span style="color:#d19a66"&gt;0&lt;/span&gt; &lt;span style="color:#56b6c2"&gt;||&lt;/span&gt; &lt;span style="color:#e06c75"&gt;indices&lt;/span&gt;.&lt;span style="color:#e06c75"&gt;all&lt;/span&gt; { &lt;span style="color:#c678dd"&gt;this&lt;/span&gt;[&lt;span style="color:#c678dd"&gt;it&lt;/span&gt;].&lt;span style="color:#e06c75"&gt;isWhitespace&lt;/span&gt;() }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Simple and straight to the point. Unfortunately the bytecode tells a very different story:&lt;/p&gt;</description></item><item><title>Micro-optimizations in Kotlin — 3</title><link>https://www.romainguy.dev/posts/2024/micro-optimizations-in-kotlin-3/</link><pubDate>Wed, 31 Jan 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/micro-optimizations-in-kotlin-3/</guid><description>&lt;p&gt;The Kotlin standard library is a wonderful set of APIs, but it sometimes hides… interesting
surprises. So today let&amp;rsquo;s took at the innocent looking &lt;code&gt;minOf()&lt;/code&gt; and &lt;code&gt;maxOf()&lt;/code&gt; functions.&lt;/p&gt;
&lt;p&gt;These functions let you perform a min/max on a series of values, instead of just two with the
more common &lt;code&gt;min()&lt;/code&gt; and &lt;code&gt;max()&lt;/code&gt; functions. Both &lt;code&gt;min()&lt;/code&gt; and &lt;code&gt;max()&lt;/code&gt; are straightforward and
simply delegate — on Android and JVM — to &lt;code&gt;Math.min()&lt;/code&gt;/&lt;code&gt;Math.max()&lt;/code&gt; respectively, which you
can assume to be implemented with intrinsics (and are on Android).&lt;/p&gt;</description></item><item><title>Going Old School</title><link>https://www.romainguy.dev/posts/2024/going-old-school/</link><pubDate>Thu, 25 Jan 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/going-old-school/</guid><description>&lt;p&gt;While looking for optimization opportunities in various parts of Jetpack Compose, I recently
discovered that calling the cube root function was taking a non-neligible amount of times in
two areas of the Toolkit: when evaluating
&lt;a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/core/CubicBezierEasing" class="external-link" target="_blank" rel="noopener"&gt;cubic Bézier easing curves&lt;/a&gt;
and when &lt;a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/graphics/package-summary#lerp%28androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,kotlin.Float%29" class="external-link" target="_blank" rel="noopener"&gt;interpolating colors&lt;/a&gt; through the OkLab color space.&lt;/p&gt;
&lt;p&gt;Working on &lt;a href="https://github.com/google/filament" class="external-link" target="_blank" rel="noopener"&gt;graphics projects&lt;/a&gt; taught me that approximations
can often be powerful optimization tools, so I decided to come up with an approximation of
&lt;code&gt;cbrt()&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;</description></item><item><title>A Better Hash Map — 1</title><link>https://www.romainguy.dev/posts/2024/a-better-hashmap/</link><pubDate>Sat, 20 Jan 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/a-better-hashmap/</guid><description>&lt;p&gt;Hash maps are extremely common data structures, and Jetpack Compose unsurprisingly takes advantage
of them for various tasks. Kotlin makes it easy to create a new mutable hash map by calling the
&lt;code&gt;mutableMapOf()&lt;/code&gt; function. Most developer will — and should — stop there, and use the map however
they need to. Things are however a bit different when working on a toolkit like Jetpack Compose as
we need to ensure the toolkit&amp;rsquo;s behavior is as unintrusive as possible for the app.&lt;/p&gt;</description></item><item><title>Micro-optimizations in Kotlin — 2</title><link>https://www.romainguy.dev/posts/2024/micro-optimizations-in-kotlin-2/</link><pubDate>Tue, 16 Jan 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/micro-optimizations-in-kotlin-2/</guid><description>&lt;p&gt;In the &lt;a href="https://www.romainguy.dev/posts/2024/micro-optimizations-in-kotlin-1/" &gt;previous post&lt;/a&gt;, we saw how we could
micro-optimize &lt;code&gt;Int.sign&lt;/code&gt; to save a few instructions. We are now going to turn to &lt;code&gt;Float.sign&lt;/code&gt;
(and by extension &lt;code&gt;Double.sign&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/sign.html" class="external-link" target="_blank" rel="noopener"&gt;&lt;code&gt;Float.sign&lt;/code&gt;&lt;/a&gt; returns the sign
of single-precision float value as a single-precision float value. While similar to &lt;code&gt;Int.sign&lt;/code&gt;, this
API must handle a special cases: Not-a-Number (&lt;code&gt;NaN&lt;/code&gt;). The exact behavior of the API is that it will
return:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-1.0f&lt;/code&gt; if the value is negative&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+/-0.0f&lt;/code&gt; if the value is zero (floats can encode both positive and negative zero)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1.0f&lt;/code&gt; if the value is positive&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NaN&lt;/code&gt; if the value is &lt;code&gt;NaN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An easy way to implement this API ourselves is to return the input when the input equals &lt;code&gt;0.0f&lt;/code&gt; or
&lt;code&gt;NaN&lt;/code&gt;, and to return the input&amp;rsquo;s sign copied onto &lt;code&gt;1.0f&lt;/code&gt; otherwise. Translated to code, we can
write:&lt;/p&gt;</description></item><item><title>Micro-optimizations in Kotlin — 1</title><link>https://www.romainguy.dev/posts/2024/micro-optimizations-in-kotlin-1/</link><pubDate>Mon, 15 Jan 2024 00:00:00 +0000</pubDate><guid>https://www.romainguy.dev/posts/2024/micro-optimizations-in-kotlin-1/</guid><description>&lt;p&gt;While my work responsibilities do not leave me much time to write code nowadays, I have managed
to make a few small contributions to &lt;a href="https://developer.android.com/jetpack/compose" class="external-link" target="_blank" rel="noopener"&gt;Jetpack Compose&lt;/a&gt;
in the last few months, mostly focusing on performance.&lt;/p&gt;
&lt;p&gt;If you are an Android app developer, your performance concerns probably start and stop at a fairly
high level &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. I find working on large scale libraries like Compose fascinating because you need to
worry about performance not only at a macro level, but also at a micro level. Since parts of the
libraries can be invoked frequently (many times per frame for instance), even micro-optimizations
can make a difference &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;</description></item></channel></rss>