<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>wency suo - blog</title>
      <link>https://wencysuo.com/blog/</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://wencysuo.com/blog/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Wed, 01 Apr 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>disguising my online traffic :)</title>
          <pubDate>Wed, 01 Apr 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://wencysuo.com/blog/disguising-my-online-traffic/</link>
          <guid>https://wencysuo.com/blog/disguising-my-online-traffic/</guid>
          <description xml:base="https://wencysuo.com/blog/disguising-my-online-traffic/">&lt;h3 id=&quot;intro&quot;&gt;intro&lt;&#x2F;h3&gt;
&lt;p&gt;One of my biggest headaches while traveling in China has always been deciding which VPN to use. To watch YouTube or access anything outside the Great Firewall (GFW) I would have to route my traffic through a server outside the country.&lt;&#x2F;p&gt;
&lt;p&gt;In the past I’ve tried a few paid services like Surfshark, NordVPN (their ads are everywhere) but none have worked consistently for me. Granted, there’s a legitimate reason why VPN reliability may degrade over time. VPN traffic can tend to have distinctive patterns that then get detected and blocked, providers have to push fixes to restore access for those on the service periodically.&lt;&#x2F;p&gt;
&lt;p&gt;As with any VPN service you also have to trust a 3rd party vendor with all of your network traffic so they can forward your encrypted requests to your final destination. I’m sure there’s a lot of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;VPN&#x2F;comments&#x2F;s5thfm&#x2F;trustworthiness_of_a_vpn_provider_vs&#x2F;&quot;&gt;discourse online about whether or not to trust VPNs&#x2F;ISPs&lt;&#x2F;a&gt; and the like but for me climbing subscription fees and data privacy is really just a pretense for learning how to set up my own private network!&lt;&#x2F;p&gt;
&lt;p&gt;It can’t be that hard right?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-does-the-internet-work-again&quot;&gt;how does the internet work again&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s start by reviewing some foundational networking concepts. Hypertext Transfer Protocol or HTTP is how data gets passed between machines to render websites. Assuming some familiarity with client&#x2F;server architectures (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cloudflare.com&#x2F;learning&#x2F;ddos&#x2F;glossary&#x2F;hypertext-transfer-protocol-http&#x2F;&quot;&gt;cloudflare has some decent materials&lt;&#x2F;a&gt;), you can think of the GFW as a monitor at the ISP level.&lt;&#x2F;p&gt;
&lt;p&gt;The GFW is smarter than just checking blacklisted URLs, it uses an advanced network security probing method called Deep Packet Inspection (DPI) to analyze the packet sizes, timing patterns, handshake signatures of your traffic. Even when content is encrypted, traffic can look like VPN traffic, which is exactly how VPN connections get detected and throttled. Your destination IP and requested domain name is also usually public and unencrypted since TLS encryption only protects the content of the client server communication, so everything after a handshake is established not before.&lt;&#x2F;p&gt;
&lt;p&gt;So our goal here isn’t necessarily encryption but actually making our traffic indistinguishable from regular HTTPS browsing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-options&quot;&gt;my options&lt;&#x2F;h2&gt;
&lt;p&gt;A few projects have emerged to fill this specific niche over the years. The most popular and well established tool that I came across is Shadowsocks, which introduced its own encrypted SOCKS5 proxy protocol. The protocol unfortunately has a detectable fingerprint, so the GFW has gotten pretty good at identifying it over time. For this reason I chose to keep looking for other tools. Granted, I haven’t tested it out myself so perhaps for my use case this would’ve been fine.&lt;&#x2F;p&gt;
&lt;p&gt;V2Ray adds a layer of obfuscation on top to disguise proxy traffic as WebSocket, gRPC, or HTTP. This gets pretty close to a convincing disguise! However, from what I can tell, V2Ray doesn’t fully hold up against active probing, where the GFW sends it’s own requests to servers it suspects might actually be proxies. To handle this properly, V2Ray requires custom fallback configs to respond properly to requests from unauthorized sources.&lt;&#x2F;p&gt;
&lt;p&gt;Trojan builds on this idea of mimicking regular web servers by actually running a proper web server with a valid TLS certificate, making proxy requests indistinguishable from valid HTTP requests. In the case of active probing, when a request is sent to a trojan server that isn’t a valid trojan request then the server, which also runs Nginx, gets served a real webpage. This does mean that in order to use Trojan I need to actually register a domain which is another upfront cost that might influence which tool you would pick to set up your own proxy server. This wasn’t an issue for me so I chose this option for my server.&lt;&#x2F;p&gt;
&lt;p&gt;It’s worth mentioning that ShadowSocks and V2Ray has some pretty great documentation in comparison to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;p4gefau1t&#x2F;trojan-go&quot;&gt;Trojan&lt;&#x2F;a&gt;. V2Ray is part of a broader ecosystem of tools that help you build your own privacy network called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.v2ray.com&#x2F;en&#x2F;&quot;&gt;Project V&lt;&#x2F;a&gt; and the community investment in maintain documentation shows. Working with Trojan was a little more difficult since there are several unmaintained repos that you have to sift through to find the most recent fork, though the setup instructions have remained consistent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deep-dive-into-trojan&quot;&gt;deep dive into trojan&lt;&#x2F;h2&gt;
&lt;p&gt;To understand what Trojan is doing, it helps to have a quick mental model of how a request travels from your browser to a server.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;adfa1d6f-371a-41f0-8ce8-c3bedf2af561_800x1064.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When you type a URL, your machine resolves the domain to an IP address via DNS, opens a TCP connection to that IP, and if it’s HTTPS, negotiates a TLS handshake before any application data gets exchanged. TCP is the transport layer and handles reliable ordered delivery of packets. HTTP sits on top and defines the structure of the actual request and response. TLS sits between them and handles encryption.&lt;&#x2F;p&gt;
&lt;p&gt;What legitimizes this request is the TLS and to complete the TLS handshake we need a properly signed certificate for our web server. When your browser connects to a server, the server presents a certificate signed by a trusted Certificate Authority (CA). Your browser checks that the signature is valid and that the certificate matches the domain you’re visiting to establish a chian of trust from the browser’s built-in root CA down to the certificate on the server.&lt;&#x2F;p&gt;
&lt;p&gt;Trojan requires that you obtain a real certificate for a real domain. That means your server looks exactly like any other HTTPS server. Same handshake, same certificate structure, same everything. There’s nothing synthetic for DPI or active probing to uncover.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;server-setup&quot;&gt;server setup&lt;&#x2F;h2&gt;
&lt;p&gt;I really spent some time thinking about where and how I was going to go about setting up my own server since it seemed like an easy excuse to get my hands on a Raspberry PI. I genuinely did want to explore this option since it would also give me full hardware control but I was on a time crunch before the trip, so this will probably have to wait until I get back.&lt;&#x2F;p&gt;
&lt;p&gt;I also briefly considered repurposing my home router. Routers are essentially small Linux computers that also have network switches so flashing it with tools like OpenWrt would’ve let me protect every device on my home network automatically at the router level with no per-device config needed. The problem is with my ISP which locks down the hardware :( so OpenWrt isn’t really an option, again future project once I get back.&lt;&#x2F;p&gt;
&lt;p&gt;So the easiest and cheapest solution to all of my problems is to secure a free VPS from Oracle. Using the free tier does mean that my server might not have the best uptime since I’m at the mercy of whatever OCI is doing with their servers but honestly I’m not confident that I would have better reliability running a Raspberry Pi out of my apartment anyway.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;managing-proxy-traffic&quot;&gt;managing proxy traffic&lt;&#x2F;h2&gt;
&lt;p&gt;Setting up the proxy is just the first step, to actually use my VPS I also have to connect my devices to my server and manage my proxy requests. The brute force approach is to route everything through the proxy. For this to work you would need to route all of your traffic, all the way down at the system level through my server. This is admittedly wasteful but it does work. The better approach here is to define a set of rules, maybe based on domain, device etc. so that you selectively use your proxy.&lt;&#x2F;p&gt;
&lt;p&gt;This is what proxy managers like Clash handle. You define rules in a config file and Clash decides how to route each request. It’s also possible to define multiple proxies and set fallback behavior if one goes down, which means you’re not betting everything on a single server. Hurrah Redundancy!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;keeping-the-server-running-with-systemctl&quot;&gt;keeping the server running with systemctl&lt;&#x2F;h2&gt;
&lt;p&gt;Right now I’m starting the Trojan-Go process manually over SSH, which is fine for testing and deeply not fine for anything else. If the server reboots, the process dies and stays dead until I notice and SSH back in.&lt;&#x2F;p&gt;
&lt;p&gt;The fix is turning it into a &lt;code&gt;systemd&lt;&#x2F;code&gt; service, which is Linux’s standard way of managing long-running processes. All I have to do is write a unit file that describes how to start the process, what user to run it as, and what to do on failure, then register it with &lt;code&gt;systemctl&lt;&#x2F;code&gt;. After that, &lt;code&gt;systemctl enable trojan-go&lt;&#x2F;code&gt; means it starts automatically on boot and &lt;code&gt;systemctl restart trojan-go&lt;&#x2F;code&gt; gives you a clean way to reload config changes. Logs go through &lt;code&gt;journald&lt;&#x2F;code&gt;, so &lt;code&gt;journalctl -u trojan-go&lt;&#x2F;code&gt; gives you a clean view of what the service is doing if anything goes wrong.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-to-do-from-here&quot;&gt;what to do from here&lt;&#x2F;h2&gt;
&lt;p&gt;I set things up in kind of a rush so that I can get a minimum viable product for my upcoming trip so there’s still a lot of work to be done. I can surf the open web but I still need to actually measure my performance and request latency. In another post I’ll explore profiling tools and optimizations i can make to my current system to achieve a smoother web browsing experience.&lt;&#x2F;p&gt;
&lt;p&gt;Until next time!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FastJet but faster? Part II</title>
          <pubDate>Thu, 05 Mar 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://wencysuo.com/blog/fastjet-but-faster-part-ii/</link>
          <guid>https://wencysuo.com/blog/fastjet-but-faster-part-ii/</guid>
          <description xml:base="https://wencysuo.com/blog/fastjet-but-faster-part-ii/">&lt;h3 id=&quot;tldr&quot;&gt;&lt;strong&gt;tldr&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We never set out to make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WencySuo&#x2F;fastjet-rs&quot;&gt;fastjet-rs&lt;&#x2F;a&gt; faster than FastJet.&lt;&#x2F;p&gt;
&lt;p&gt;But after some elbow grease, SIMD, and some not-so-micro optimizations we’ve managed to make our Rust simple jet finding algorithms a few microseconds faster than the original C++ implementations.&lt;&#x2F;p&gt;
&lt;p&gt;If you haven’t already gone over the basics of this project in part 1, all you need to know is that fastjet is a particle physics package that performs nearest neighbor calculations to merge subatomic particles into jets. We are trying to rewrite the original C++ as Rust for the language’s memory safety advantages.&lt;&#x2F;p&gt;
&lt;p&gt;The benchmarking results from a comparison of the simple O(n²) clustering algorithm found that our Rust implementation is roughly equivalent in performance to a Julia implementation, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;jetreconstruction.jl&#x2F;&quot;&gt;JetReconstruction.jl&lt;&#x2F;a&gt;, but ultimately slower than the original C++ version by ~130 microseconds… but that’s not the case anymore!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Jump to the end if you’re just interested in the results of our benchmarking.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;We’ll go over three major categories of code changes since our commit as of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@jesus.fileto&#x2F;part-1-rewriting-c-code-to-rust-a-fastjet-experiment-f8bc2fb43bd4&quot;&gt;Part 1&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tiling&quot;&gt;&lt;strong&gt;tiling&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;d9b73303-f255-4e85-9bb2-446771177ffc_2262x1146.png&quot; alt=&quot;Thanks JetReconstruction.jl team for this wonderful graphic!&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Thanks JetReconstruction.jl team for this wonderful graphic!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Fastjet and JetReconstruction.jl both implement a tiled clustering algorithm in addition to the simple n² clustering algorithm so we thought we should too. The key to this algorithm lies in the fact that clustering particles on an eta, phi coordinate system doesn’t change too much if we localize the clustering. In other words, instead of scanning and calculating distances between one jet and all other jets, we can define a tiled representation of our particles so that we only use the closest particles during distance calculations. Depending on how small we make our tiles, we essentially do the minimum amount of distance calculations for each jet at the expense of extra bookkeeping as we now need to store which jets are in which tiles and actively update it.&lt;&#x2F;p&gt;
&lt;p&gt;In practice, this means implementing a doubly linked list.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s introduce some new structs &lt;code&gt;Tile&lt;&#x2F;code&gt; and &lt;code&gt;TiledJet&lt;&#x2F;code&gt; to represent all of our jet information relationally.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F; The neighbourhood of a tile is set up as follows:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F;       LRR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F;       LXR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F;       LLR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F; tiles contains XLLLLRRRR with pointers&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F;                     |   \ RH_tiles&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;    &#x2F;&#x2F;&#x2F;                     \ surrounding_tiles&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Credit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; fastjet authors&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Choosing a doubly linked list gives us instant access to adjacent jets in both directions, making it easy to rescan neighbors around a merge. Flattening each tile makes traversal linear, all we have to do is a little bit of math to map the grid index of each neighboring jet in a tile to its flattened index. Looks pretty easy to implement right?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-can-doubly-linked-lists-be-unsafe-in-rust&quot;&gt;&lt;strong&gt;why can doubly linked lists be unsafe in Rust?&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Linked lists are a bit of a pain to implement in Rust.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; u32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; prev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; next&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since each node needs to hold references to both the prev node and the next node, any interior node will always have multiple simultaneous owners. The moment you try to mutate a node in the middle of the list the Rust borrowchecker will throw a fit since you’ll need to have write permissions to both the node itself, the previous node, and the next node.&lt;&#x2F;p&gt;
&lt;p&gt;Since each node needs to hold references to both the prev node and the next node, any interior node will always have multiple simultaneous owners. The moment you try to mutate a node in the middle of the list the Rust borrowchecker will throw a fit since you’ll need to have write permissions to both the node itself, the previous node, and the next node.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-refcell-is-essential&quot;&gt;&lt;strong&gt;why RefCell is ‘essential’&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Recall &lt;code&gt;RefCell&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; from part 1, paired with &lt;code&gt;Rc&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; they are the Rust sanctioned safe method of allowing shared mutability. If you wrap nodes with &lt;code&gt;Rc&amp;lt;RefCell&amp;lt;Node&amp;gt;&amp;gt;&lt;&#x2F;code&gt; ownership checks will now be performed at runtime instead of compile time. This is significant since the borrow checker, which runs at compile time is quite conservative so it ends up rejecting any borrowing patterns that can’t be proven safe statically. At runtime all of this changes since we can actually check if any simultaneous borrows conflict. Thus our doubly linked list node implementation looks more like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; u32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; prev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Rc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;RefCell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; next&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Rc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;RefCell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will compile successfully, but the problem comes when actually accessing any of our jets inside the linked list. Each time you want to read a field within an object of type Node you will need to call &lt;code&gt;.borrow()&lt;&#x2F;code&gt; or &lt;code&gt;.borrow_mut()&lt;&#x2F;code&gt;. Each call to either of these functions is a runtime borrow check, adding a significant overhead anytime we work with jets in our algorithm.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ok-but-what-if-we-don-t-want-to-always-have-runtime-borrow-checking&quot;&gt;&lt;strong&gt;ok but what if we don’t want to always have runtime borrow checking?&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A better solution is actually pretty elegant here if you are willing to trade in storage overhead for better runtime performance. The astute reader might have already noticed that we don’t actually need to store references to each jet object, we can just keep a well updated table of jets and use &lt;code&gt;usize&lt;&#x2F;code&gt; indices instead.&lt;&#x2F;p&gt;
&lt;p&gt;Our main clustering logic is implemented inside ClusterSequence so we can give it sole ownership over this table. This is actually a common design pattern more commonly known as &lt;strong&gt;arena-style bookkeeping&lt;&#x2F;strong&gt;. Instead of using pointers or references, we can store all of our objects in contiguous blocks of memory that we access via indices. However, since our clustering algorithm requires we merge quite a few jets the upkeep of marking jet indices stale was nontrivial in our implementation. Many bugs emerged due to an erroneous index to an already removed or merged jet. All of this still ended up being worth it in our case since our runtime performance did improve after removing several repeated runtime borrow checks.&lt;&#x2F;p&gt;
&lt;p&gt;So linked lists really aren’t all that easy to pull off in Rust without giving some thought to ownership models in your data structures. The reason why implementing linked lists might feel easier in other languages is simply because the memory management is abstracted away behind complex garbage collection cleanup that has its own hidden costs. From a pedagogical perspective, tiling our clustering algorithm turned out to be a great way to learn Rust.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For a more thorough explanation of all things linked lists in Rust we highly recommend checking out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-unofficial.github.io&#x2F;too-many-lists&#x2F;&quot;&gt;Learning Rust With Entirely Too Many Linked Lists&lt;&#x2F;a&gt; :)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-fallacy-of-simd&quot;&gt;&lt;strong&gt;the fallacy of SIMD&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Implementing a SIMD version of the simple n² clustering algorithm was the second design change we made to increase our performance.&lt;&#x2F;p&gt;
&lt;p&gt;SIMD stands for (Single Instruction Multiple Data) where the CPU groups together multiple data elements as vectors (defined as LANES) and performs the same instruction to all this data simultaneously. In Fastjet, there are multiple scenarios where our data is highly parallel, finding the distance metric for all jets at once, initializing them, and updating the nearest neighbors each time we remove&#x2F;add a new jet. Some other parts of the code are inherently sequential, such as finding which jet to remove and performing our updating logic on it since it relies on finding the minimum distance jet and then acting upon it. In these cases the latter is more ripe for SIMD than the former. In these cases SIMD can optimistically produce performance gains of &lt;strong&gt;3–4x&lt;&#x2F;strong&gt;, however, the compiler is a fickle thing and it is possible that SIMD can cause performance penalties because nothing beats the compiler.&lt;&#x2F;p&gt;
&lt;p&gt;Normally, SIMD requires fine tuning to specific CPU architectures and calling archaically named functions after scouring through CPU documentation. Thankfully for our purposes we can test drive &lt;code&gt;std::simd&lt;&#x2F;code&gt;, a feature of Rust nightly which has long awaited a stable release, that aims to provide a “universal” SIMD implementation. The initial setup was pretty straightforward other than actually needing to learn SIMD!&lt;&#x2F;p&gt;
&lt;p&gt;When it came to retrofitting our code for SIMD we faced a number of difficulties.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;simd-memory-requirements&quot;&gt;&lt;strong&gt;SIMD memory requirements&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;e6400cc4-5550-4b39-bb3b-f85a99acd798_850x283.webp&quot; alt=&quot;Ref: https:&#x2F;&#x2F;www.researchgate.net&#x2F;figure&#x2F;A-comparison-of-the-Array-of-Structs-AoS-and-Struct-of-Arrays-SoA-memory-layouts_fig2_259132352&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Ref: https:&#x2F;&#x2F;www.researchgate.net&#x2F;figure&#x2F;A-comparison-of-the-Array-of-Structs-AoS-and-Struct-of-Arrays-SoA-memory-layouts_fig2_259132352&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;First off, SIMD requires all the data to be contiguously located in memory, meaning that we had to vectorize our current paradigm where each Jet struct contains elements containing its &lt;code&gt;pt&lt;&#x2F;code&gt;, &lt;code&gt;eta&lt;&#x2F;code&gt;, &lt;code&gt;phi&lt;&#x2F;code&gt;, &lt;code&gt;NN_index&lt;&#x2F;code&gt;, etc. This means that every time we call a SIMD function we need to loop over all vectors and initialize arrays holding just &lt;code&gt;phi&lt;&#x2F;code&gt;, &lt;code&gt;eta&lt;&#x2F;code&gt;, and so on. Initializing this data each SIMD call would increase the overhead so much that the performance gain is unjustified, thus we must shift to running our entire algorithm using structures of arrays instead of arrays of structs.&lt;&#x2F;p&gt;
&lt;p&gt;For this reason, we were drawn to Rusts’ arena (&lt;em&gt;recall the importance of arena-style bookkeeping from earlier!&lt;&#x2F;em&gt;) since it allows for fast memory allocation given that we know the amount of Jets that we account for at a time will never be larger than the initial length of input particles. This required a large overhaul of our code, but it was all going to be worth it right?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;simd-and-primitives&quot;&gt;&lt;strong&gt;SIMD and primitives&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another issue we ran into was the fact that SIMD arrays only take primitives but our &lt;code&gt;NN_index&lt;&#x2F;code&gt; was encoded as an &lt;code&gt;Option&lt;&#x2F;code&gt; (to highlight jets not having NN) could not be imputed. Then thanks to (&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;jetreconstruction.jl&#x2F;&quot;&gt;JetReconstruction.jl&lt;&#x2F;a&gt;) we realized that we can actually modify &lt;code&gt;NN_index&lt;&#x2F;code&gt; to be set to a sentinel value of itself!&lt;&#x2F;p&gt;
&lt;p&gt;When creating new jets (whether merged or initialized) we already know its own index to access it, and most functions where we unwrap can instead just check its index. This mainly improves performance (removing unwrapping and wrapping) but also has the secondary effect of reducing data size, &lt;code&gt;f64&lt;&#x2F;code&gt; is 8 byte while an Option is double that (just to store the None value).&lt;&#x2F;p&gt;
&lt;p&gt;An interesting example to highlight was the following function since the &lt;code&gt;min(a, a) = a&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; _bj_dij&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; ProxyJet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E64553;&quot;&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;        *&lt;&#x2F;span&gt;&lt;span&gt; jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;kt2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;nn_jet_index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;kt2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;                 .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;unwrap_or&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt;MAX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;   }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; _bj_dij&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; ProxyJet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E64553;&quot;&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; kt2_a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;kt2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; kt2_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;nn_jet_index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;kt2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;        &#x2F;&#x2F; Bypasses the f64::min NaN checks, yielding pure hardware speed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; min_kt2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; if&lt;&#x2F;span&gt;&lt;span&gt; kt2_a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; kt2_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; kt2_a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt; kt2_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; min_kt2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;   }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now with all the setup out of the way we can examine a smaller SIMD function to highlight its technicalities. Since we showed the &lt;code&gt;_bj_dij&lt;&#x2F;code&gt; function earlier it seems right to highlight it parallelized.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;feature &lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; “simd”&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; _dij_simd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        kt2_arr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        nn_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        di_j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;    ) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; kt2_arr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        let mut&lt;&#x2F;span&gt;&lt;span&gt; j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;        &#x2F;&#x2F;  SIMD loop&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        while&lt;&#x2F;span&gt;&lt;span&gt; j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt; &#x2F;&#x2F; note: [j..j + LANES] does not include j + LANES&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; kt2_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Simd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;from_slice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;kt2_arr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; dist_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Simd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;from_slice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; indices&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Simd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;from_slice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;nn_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt; &#x2F;&#x2F; must store indices as u64 because compiler errors with other funcs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; indices_usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; indices&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;cast&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; kt2_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; Simd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;gather_or&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;kt2_arr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; indices_usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; kt2_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; kt2_min&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; kt2_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;simd_min&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;kt2_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; res&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; kt2_min&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; dist_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            res&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;copy_to_slice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; di_j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; LANES&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;        &#x2F;&#x2F; scalar handling for remainder of elems aka tail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        for&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; nn_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; as usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            di_j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; kt2_arr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;min&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;kt2_arr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Starting from the top, we gate SIMD use only when building with feature “simd” which prevents compiler errors from not being on nightly Rust as the code is basically “ignored” by the preprocessor.&lt;&#x2F;p&gt;
&lt;p&gt;Then when calling these SIMD functions we must pass a size parameter labeled LANES which as mentioned previously is how many elements we wish to pass to a SIMD instruction at a time. It is up to the user to check whether they handle the remainder of elements with the slower sequential instruction which we do in the final loop at the end. We can construct SIMD objects easily using &lt;code&gt;from_slice&lt;&#x2F;code&gt; and just passing a reference to our elements. Here the parallel action we must do is &lt;code&gt;gather_or&lt;&#x2F;code&gt; which retrieves all elements given indices. Funnily enough this is one of the slower operations since the bottleneck for these operations are not the CPU but instead the memory register which must fetch the vectors which are not contiguous and not guaranteed to be in the same cache line as the jets we have already taken in. However, we can see how we can use many other primary operators like inequalities, addition, multiplication in the same way and chain these operations together.&lt;&#x2F;p&gt;
&lt;p&gt;After overhauling all of our code we found that before optimizing our &lt;code&gt;simple_n2_cluster&lt;&#x2F;code&gt; algorithm that we achieved a &lt;strong&gt;45 percent performance improvement&lt;&#x2F;strong&gt;, and we were officially slightly faster than our C++ “competitor” 🥳.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;micro-optimizations-that-are-more-than-micro-in-practice&quot;&gt;&lt;strong&gt;micro optimizations that are more than micro in practice&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;However, it turns out that just a couple of changes in our previous naive implementation that &lt;em&gt;help&lt;&#x2F;em&gt; the compiler do it’s job also did wonders for performance.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;writing-clean-code-really-does-count&quot;&gt;&lt;strong&gt;writing clean code really does count!&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Part of the reason that Rust touts ‘C’ performance with large safety guarantees is through its aggressive use of optimizations to construct Zero-Cost Abstractions. This means more verbose Rust code to check indices in comparison to unsafe pointers, Vectors instead of arrays compile down to almost the same assembly instruction as C’s unsafe implementation.&lt;&#x2F;p&gt;
&lt;p&gt;This is important to note as more convoluted code has the effect of preventing the compiler from making such optimizations since user intent is unknown. This is why inlining most functions can result in faster performance since the compiler knows in what situations called functions are being used by the callee and can optimize accordingly. Take the following for loop:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;   &#x2F;&#x2F; some logic based on the value of nn_idx that only matters if jet_b != i&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;   &#x2F;&#x2F; if we find jet_b_idx is equal to i quit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;   if&lt;&#x2F;span&gt;&lt;span&gt; jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;      if&lt;&#x2F;span&gt;&lt;span&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;nn_jet_index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span&gt; tail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;set_nn_jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;min_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;      continue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;   }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;   &#x2F;&#x2F; update min dist for jet i and jet_b&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Given this function the compiler does not know that the if loop is only checking for one value, and one that is predetermined before the for loop even starts, since it is in the middle of the function. If we just place it on top, the compiler will split the function into the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;jet_idx_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt; &#x2F;&#x2F; do logic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;&#x2F;&#x2F; perform jet_b_idx == i case&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; jet_idx_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt; &#x2F;&#x2F; do logic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A small change like moving the if loop to the beginning of the for loop had around a &lt;em&gt;6 percent improvement&lt;&#x2F;em&gt; since this was a part of the code that was called very frequently with depth O(n²).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;performance-impact-of-muts&quot;&gt;&lt;strong&gt;performance impact of &lt;code&gt;mut&lt;&#x2F;code&gt;s&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The next example highlighted to me why &lt;code&gt;mut&lt;&#x2F;code&gt;s can actually decrease performance if not used sparingly especially in conjunction with the next example. When a jet is merged, we must set the NN for the old jets which previously pointed to its constituents, since we don’t have to update the other jets to point to it (since we removed them) as a NN we only need to update that jet. Here is the code to highlight this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;inline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; bj_set_nn_nocross&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; ProxyJet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;  &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;  curr_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;  head_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;  tail_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;  jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;)  {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;   let mut&lt;&#x2F;span&gt;&lt;span&gt; nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;r2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;   let mut&lt;&#x2F;span&gt;&lt;span&gt; nn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; curr_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;   for&lt;&#x2F;span&gt;&lt;span&gt; jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; head_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;curr_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;       let&lt;&#x2F;span&gt;&lt;span&gt; dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;_bj_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;curr_jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;jets_ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;       if&lt;&#x2F;span&gt;&lt;span&gt; dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           nn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;       }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;   }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;   for&lt;&#x2F;span&gt;&lt;span&gt; jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; curr_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;tail_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;       let&lt;&#x2F;span&gt;&lt;span&gt; dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;_bj_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;curr_jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;jets_ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;       if&lt;&#x2F;span&gt;&lt;span&gt; dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            nn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;       }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;   }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;curr_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;set_nn_jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;nn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;curr_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;set_nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The important things to watch are the function signature, and the last two lines. Since we need to modify &lt;code&gt;jets[curr_idx]&lt;&#x2F;code&gt; we pass the entire jets as mutable, we also can’t just pass &lt;code&gt;jets[curr_idx]&lt;&#x2F;code&gt; as mutable because then we would break Rust’s borrowing rules. Thus the only solution is to tell the callee function which element changes, and then modify that element. Here is how that following code looks like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;new_nn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; new_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;bj_set_nn_nocross&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; tail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; jet_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;jet_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;set_nn_jet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;new_nn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;jet_i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;set_nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;new_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; bj_set_nn_nocross&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; ProxyJet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #179299;&quot;&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D20F39;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        curr_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        head_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        tail_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;        jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;font-style: italic;&quot;&gt;        &#x2F;&#x2F; logic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;        (&lt;&#x2F;span&gt;&lt;span&gt;nn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; nn_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This allows us to avoid having to pass the entire jets collection as mutable preventing the need for the compiler to reload the entire jets collection after passing into the function. From the compiler’s eyes the &lt;em&gt;&lt;strong&gt;entire&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; vector could change. With this declaration we know that only &lt;code&gt;jets[i]&lt;&#x2F;code&gt; changes. This change resulted in a &lt;em&gt;6 percent performance improvement&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;eliding-compiler-bound-checks&quot;&gt;&lt;strong&gt;eliding compiler bound checks&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This last example is arguably the most important. Each time a vector element is indexed, Rust checks whether the element is out of bounds (OOB) which in C leads to a host of UB. Given that our entire code relies on &lt;code&gt;NN_index&lt;&#x2F;code&gt; and indexing our jets almost every time we require them, this becomes a large part of our performance penalty.&lt;&#x2F;p&gt;
&lt;p&gt;Iterator efficiency in Rust can also be explained by the same phenomenon, since iterators already check elements exist, the compiler does not need to perform auxiliary bounds checking!&lt;&#x2F;p&gt;
&lt;p&gt;For us, we can’t use iterators here because we need to pass the entire vector slice to internal functions alongside a jet from that slice. This doesn’t mean we have no options left though. Instead we can tell the compiler to stop bounds checking indexes by proving that we will never use an index that is OOB via assertions.&lt;&#x2F;p&gt;
&lt;p&gt;In the code highlighted in the first optimization example, there is a lot of indexing. Almost everything is being indexed since we also have &lt;code&gt;muts&lt;&#x2F;code&gt; sprinkled in the for loop. Thus before this entire loop we index according to &lt;code&gt;i&lt;&#x2F;code&gt; and &lt;code&gt;jet_b_idx&lt;&#x2F;code&gt; thus we add the following code before the loop.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; bj_jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; dijs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; = &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; di_j&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;assert!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;jet_b_idx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; jets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since &lt;code&gt;i&lt;&#x2F;code&gt; loops up to &lt;code&gt;tail&lt;&#x2F;code&gt;, and &lt;code&gt;jet_b&lt;&#x2F;code&gt; is less than the entire length of the jet, OOB cannot occur and the compiler removes all bounds checking for our code in this for loop and in our NN search due to the changes mentioned in the second example. &lt;em&gt;&lt;strong&gt;This led to an astonishing 20% percent from just 3 lines of code!&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Other not so micro optimizations that accounted for another 20% of performance improvements:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Knowing our elements were never &lt;code&gt;NaN&lt;&#x2F;code&gt; or &lt;code&gt;INF&lt;&#x2F;code&gt; which require another check for &lt;code&gt;f64&lt;&#x2F;code&gt; when sorting or min&lt;&#x2F;li&gt;
&lt;li&gt;Removing lazy init since just calculating these numbers are cheaper than all of the dereferencing and heap retrieval&lt;&#x2F;li&gt;
&lt;li&gt;Using Fused &lt;code&gt;add_mul&lt;&#x2F;code&gt; in math calculations&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Lastly, we found that testing different performance improvements with Rust was fairly easy as we didn’t have to worry about UB caused by our code changes that would normally be present in C++ pointer handling.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-how-much-faster-are-we-actually&quot;&gt;&lt;strong&gt;so how much faster are we actually?&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;criterion&quot;&gt;Criterion&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;benchmark?tab=readme-ov-file#installation&quot;&gt;Google Benchmarks&lt;&#x2F;a&gt; to benchmark our Rust functions &lt;code&gt;simple_n2_cluster&lt;&#x2F;code&gt;, &lt;code&gt;simple_n2_cluster_simd&lt;&#x2F;code&gt;, and &lt;code&gt;tiled_n2_cluster&lt;&#x2F;code&gt; against the original C++ versions of the code. We initially wanted to also compare our implementation against the Julia code as well but after discovering that we were actually faster than the C++ implementation we thought that deserved our full attention.&lt;&#x2F;p&gt;
&lt;p&gt;Previously, we just used the &lt;code&gt;single-event.dat&lt;&#x2F;code&gt; file located in one of the example data folders of Fastjet to benchmark all of our code. This was good for debugging since we could make sure that our final calculated jets were the same as Fastjet but for performance testing we wanted a more diverse dataset, with varying numbers of particles to process.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily for us, there’s a way to generate simulated events using a package called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pythia.org&#x2F;&quot;&gt;Pythia&lt;&#x2F;a&gt;. We spent some time generating a diverse dataset of events ranging from 50 to 1500+ particles per event and ran all 3 algorithms on it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;4e5a20fe-060e-400a-9e69-27f88898a6de_1400x1050.jpeg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let’s start with a macro view of our performance. Tiled clustering in C++ is still the fastest out of all the algorithms, however our tiling implementation is a pretty close second. The simple C++ implementation is also the slowest out of the algorithms with our SIMD and simple clustering consistently beating C++.&lt;&#x2F;p&gt;
&lt;p&gt;To us, this is pretty surprising! We assumed that Rust would at best match C++ performance with the added benefit of memory safety (which was already a huge win from our perspective).&lt;&#x2F;p&gt;
&lt;p&gt;Zooming in to a particular section gives us some more information too. Our tiled clustering algorithm has a slightly more unstable performance but the same points as the C++ code. We posit that this could be related to memory allocation and cache misses during reading tile neighbors? However, this would require some benchmarking with Memory profiling packages like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;dhat&#x2F;latest&#x2F;dhat&#x2F;&quot;&gt;dhat&lt;&#x2F;a&gt; that would allow us to observe the exact heap allocations made during each run.&lt;&#x2F;p&gt;
&lt;p&gt;Another note here is that the benefits from a lot of the optimizations have a constant factor overhead (SIMD, tiling) so their benefit is really only felt at large input sizes. In fact, you can see that for smaller inputs, the simple n2 algorithm often outperforms our simd or tiled implementations.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;a115a131-d58d-453c-8d45-bd439fbf0145_1400x1050.jpeg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We hope by now it’s clear that paying attention to how code gets compiled can really pay off compared to manually hand tuning for performance. Who knew all of these seemingly trivial changes could actually turn out to be so powerful? Just as important though, is the fact that we could actually measure and test the impact of these changes with &lt;code&gt;Criterion&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Never forget to benchmark your code folks!&lt;&#x2F;p&gt;
&lt;p&gt;Until next time then :)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Part 1: Rewriting C++ code to Rust (a FastJet experiment)</title>
          <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://wencysuo.com/blog/part-1-rewriting-c-code-to-rust-a/</link>
          <guid>https://wencysuo.com/blog/part-1-rewriting-c-code-to-rust-a/</guid>
          <description xml:base="https://wencysuo.com/blog/part-1-rewriting-c-code-to-rust-a/">&lt;h3 id=&quot;what-we-are-doing&quot;&gt;&lt;strong&gt;what we are doing&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;TLDR: This blog documents what we learned rewriting a scoped down &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fastjet.fr&#x2F;&quot;&gt;fastjet 3.5.1&lt;&#x2F;a&gt; from C++ to Rust as our first real Rust crate, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WencySuo&#x2F;fastjet-rs&quot;&gt;fastjet-rs&lt;&#x2F;a&gt;. We benchmark carefully hand-tuned work of physicists from 20 years ago and a more recent &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JuliaHEP&#x2F;JetReconstruction.jl&quot;&gt;Julia rewrite&lt;&#x2F;a&gt; against our Rust implementation. This project is largely educational, but we also wanted to produce a crate that could become a meaningful contribution to the Rust ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;One emerging area of Rust development is scientific high-performance computing. Workloads in computational biology or particle physics routinely handle petabytes of data and demand heavy optimization and parallelization to run. For this project, we took a particular interest in particle physics Rust crates since my collaborator started programming by analyzing lepton jets for CMS at the LHC (the world’s largest particle accelerator in CERN).&lt;&#x2F;p&gt;
&lt;p&gt;Scientific computing seemed like natural Rust territory, but some deep diving into &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;1jjf96y&#x2F;why_isnt_rust_used_more_for_scientific_computing&#x2F;&quot;&gt;reddit threads&lt;&#x2F;a&gt; revealed that adoption outside of ML and computational biology is still nascent. In fact, Fortran and C&#x2F;C++ still have a chokehold on HPC programs even when Rust is beginning to define modern programming elsewhere. The discussion boils down to three points:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Scientists aren’t programers and rust has a steep learning curve&lt;&#x2F;li&gt;
&lt;li&gt;C&#x2F;C++ code when written well is already performant&lt;&#x2F;li&gt;
&lt;li&gt;C&#x2F;C++ ecosystems have decades of niche library support that Rust can’t match yet&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Any memory bugs within applications like fastjet have been weeded out and corrected by the community over the past 20 years at the expense of many segfaults and memory leaks. When the community is already comfortable with a battle-tested Fortran or C program, fighting the borrow checker won’t always be worth it.&lt;&#x2F;p&gt;
&lt;p&gt;That said, particle physicists haven’t been standing still. In the past few years, Python has gained serious traction among scientists who value ease of use and development speed. Binding tools like pybind11, Cython, SWIG give user-friendly interfaces to the underlying C++ code. There is no doubt that this is a step in the right direction. But accumulating tech debt by layering abstractions over aging C++ code risks the same fate as Fortran, where supporting new features and maintenance becomes more similar to witchcraft than engineering. One new line of code breaks a thousand others without rhyme or reason.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;a661a5c1-1b94-4f16-8a75-eee01571c726_3456x1940.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Julia has emerged as one solution to this problem. Our hypothesis is that Rust could be another. Rust consistently matches or beats optimized C in benchmarks and its ownership model gives you fearless concurrency without a garbage collector. These are guarantees that Julia, for all its strengths, doesn’t offer at compile time.&lt;&#x2F;p&gt;
&lt;p&gt;Our goal is to test this hypothesis by benchmarking fastjet across C++, Rust, and Julia.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;some-basic-things-to-know-about-jets-and-the-problem-we-re-solving&quot;&gt;&lt;strong&gt;some basic things to know about jets and the problem we’re solving&lt;&#x2F;strong&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I had no physics background coming into this project, so we started with a crash course.&lt;&#x2F;p&gt;
&lt;iframe
  src=&quot;https:&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;HVxBdMxgVX0&quot;
  title=&quot;Introduction to particle jets&quot;
  loading=&quot;lazy&quot;
  referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
  allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot;
  allowfullscreen
&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Watching the two part series is not entirely necessary, although very entertaining. The production quality is peak 2006 internet and honestly that only makes them better.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;ok-so-with-that-what-are-jets&quot;&gt;Ok so with that, what are jets?&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;26839d7d-0f05-4409-9b76-64847922d526_1600x441.png&quot; alt=&quot;proton proton collision that produces quarks and gluons which get recorded by the detectors calorimeters as jets&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;proton proton collision that produces quarks and gluons which get recorded by the detectors calorimeters as jets&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;profmattstrassler.com&#x2F;articles-and-posts&#x2F;particle-physics-basics&#x2F;the-known-apparently-elementary-particles&#x2F;jets-the-manifestation-of-quarks-and-gluons&#x2F;&quot;&gt;Matt Strassler’s&lt;&#x2F;a&gt; explains, jets are sprays of high-energy subatomic particles (hence the name jets) composing the building blocks of the universe that we examine to understand the inner workings of protons, neutrons and other hadrons. The particles that produce jets are extremely short-lived and never reach our detectors, so jets are the only observable evidence of their existence.&lt;&#x2F;p&gt;
&lt;p&gt;Before we can analyze jets we need to reconstruct them from scattered event outputs collected by the calorimeters inside particle colliders. Historically, there are two methods for reconstructing particle collision events into jets: cone algorithms and hierarchical clustering. From the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fastjet.fr&#x2F;repo&#x2F;fastjet-doc-3.5.1.pdf&quot;&gt;fastjet manual&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Cone algorithms put together particles within specific conical angular regions, notably such that the momentum sum of the particles contained in a given cone coincides with the cone axis (a “stable cone”).&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;What the manual calls sequential recombination algorithms can be understood more broadly as hierarchical clustering. Pairs of particles bound by their distance are clustered repeatedly until all particles are merged into stable jets. This statistical pattern can be found everywhere today. For our purposes, we can approximate jet reconstruction as an application of the nearest-neighbor problem common in CS.&lt;&#x2F;p&gt;
&lt;p&gt;Both methods rely on a tunable distance parameter delta R to define either the radius of a cone from the origin particle or the furthest distance between two points that we would collimate into the same jet. Set delta R too low and we miss lower energy descendant particles (normally called “children”) or too high and background noise from other particles are included in our final jets.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-did-we-do&quot;&gt;&lt;strong&gt;what did we do?&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Fastjet’s has three core classes that configure how jets are reconstructed:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fastjet::PseudoJet&lt;&#x2F;code&gt; which defines jet objects as four-momenta vectors with additional values needed for different clustering algorithms&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;fastjet::JetDefinition&lt;&#x2F;code&gt; which contains the specifications for how jet cluster should be carried out&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;fastjet::ClusterSequence&lt;&#x2F;code&gt; which is the class that outputs the final jets after performing clustering as specified by JetDefinition on input PseudoJet objects&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It is important to us that we keep the functionality of fastjet-rs close to fastjet which meant reimplementing PseudoJet, JetDefinition, ClusterSequence code as best we could in a rust-y manner.&lt;&#x2F;p&gt;
&lt;p&gt;Heavy pointer manipulation and use of templates were the two characteristics of fastjet that made it difficult to translate to rust. You can read more about why raw pointer handling is an unsafe action in rust &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch20-01-unsafe-rust.html&quot;&gt;here&lt;&#x2F;a&gt;, but the tldr is that dereferencing raw pointers breaks rust’s memory safety guarantees. Of course, the advantage of pointer manipulation when done correctly lies in the performance benefits. So the main challenge is really just being explicit with memory safety while maintaining performance parity with fastjet’s unencumbered C++ implementations.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;writing-getters-for-pseudojet&quot;&gt;Writing getters for PseudoJet&lt;&#x2F;h3&gt;
&lt;p&gt;Each PseudoJet object is initialized by a four-momentum vector describing a particle’s x, y, z planar coordinates and an energy value e. These values are then used to derive the information we will need for clustering: kt2, rap, and phi. These values are expensive to calculate, and some jets may never need to call them so Fastjet initializes them lazily with an internal function &lt;code&gt;_finish_init()&lt;&#x2F;code&gt;. &lt;code&gt;_finish_init()&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Our first design of some of the PseudoJets get methods made them mutable. This seems fine at a glance but quickly complicated our code quite dramatically. During the clustering step we often need to calculate the distances between two jets using the following function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #4C4F69; background-color: #EFF1F5;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt; _bj_dist&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt;J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; ProxyJet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #04A5E5;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E64553;&quot;&gt;jet_a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E64553;&quot;&gt; jet_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;font-style: italic;&quot;&gt; J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; dphi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt; PI&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;abs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FE640B;&quot;&gt;PI&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DF8E1D;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;abs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;jet_a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;phi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;jet_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;phi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span&gt; deta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8839EF;&quot;&gt; f64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; jet_a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;eta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;jet_b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #1E66F5;font-style: italic;&quot;&gt;eta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        dphi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; dphi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; deta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #179299;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; deta&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7C7F93;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since we’re only accessing fields of &lt;code&gt;jet_a&lt;&#x2F;code&gt; and &lt;code&gt;jet_b&lt;&#x2F;code&gt;, theoretically we don’t actually need to pass in mutable references to &lt;code&gt;jet_a&lt;&#x2F;code&gt; or &lt;code&gt;jet_b&lt;&#x2F;code&gt;. However, since phi is a lazily initialized value with a mutable get method this aspect is passed onto the &lt;code&gt;_bj_dist&lt;&#x2F;code&gt; function. Furthermore, jets are stored in a vector so when calling &lt;code&gt;_bj_dist&lt;&#x2F;code&gt; we need to pass in two mutable references to elements of the same vector. This creates a major violation in rust since there cannot be multiple, simultaneous mutable references to the same object.&lt;&#x2F;p&gt;
&lt;p&gt;Even though we could guarantee that there would be no data races when running our code single threaded, the Rust compiler does not. Our temporary fix was to create a helper function which breaks our jet vector into two separate mutable components each time we had to call &lt;code&gt;_bj_dist&lt;&#x2F;code&gt;. You can probably see that this was a messy solution, not to mention a big performance hit to our naive implementation that we needed to fix.&lt;&#x2F;p&gt;
&lt;p&gt;Our second solution was to turn to unsafe Rust, since as stated earlier we guaranteed that we would never be calling &lt;code&gt;mut&lt;&#x2F;code&gt; on the same index, we could reduce the bloat of our function by calling the pointers directly. Unsafe code isn’t always bad, Rust just forces you to be deliberate about the abstractions that often lead to nasty memory bugs.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily, there is a much better way to solve this problem in Rust with a concept called interior mutability, a design pattern that allows you to mutate data even when there are immutable references to that data. I told you earlier that this is explicitly disallowed by rust, nothing has changed. Implementations of interior mutability like &lt;code&gt;OnceCell&lt;&#x2F;code&gt; are just safe APIs for unsafe code, making it easier for us to directly use helpful abstractions for unsafe code.&lt;&#x2F;p&gt;
&lt;p&gt;For lazy initialization specifically, the standard rust library has a type called &lt;code&gt;OnceCell&lt;&#x2F;code&gt; which we can use to obtain shared &lt;strong&gt;immutable references&lt;&#x2F;strong&gt; to uninitialized data that becomes initialized once written. Getting rid of our vector split workarounds improves the performance, readability of our code, and prepares us for properly implementing concurrency.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, the authors of fastjet ran into this very problem with these getter methods when multithreading their code. There are many preprocessing macros modifying how these getters work for the exact reason that they can’t prevent data races here. This is where the rust compiler’s foresight really pays off, by designing early with restrictive borrowing and ownership rules from the start we get “fearless concurrency” for free!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;benchmarking&quot;&gt;&lt;strong&gt;benchmarking&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;To compare our implementations, we timed and generated flamegraphs for the C++, Julia, and Rust versions of the simple n² algorithm using the basic clustering strategy.&lt;&#x2F;p&gt;
&lt;p&gt;Setting up profiling turned out to be more annoying than expected, especially on mac. We found that sample worked best for our purposes, but getting useful flamegraphs required some care. We had to reduce inlining (&lt;code&gt;-g&lt;&#x2F;code&gt; for C++, &lt;code&gt;#[inline(never)]&lt;&#x2F;code&gt; in Rust) and keep frame pointers intact so the profiler could actually record meaningful stack frames instead of collapsing everything into “the clustering function”.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;96b6964c-914f-40ba-ae59-c2b5eb2285e4_2048x396.png&quot; alt=&quot;C++ Flamegraph&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;C++ Flamegraph&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;6dccb2fa-a403-410c-8962-fb6644122bb6_1600x549.png&quot; alt=&quot;Rust Flamegraph&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Rust Flamegraph&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In our Rust flamegraph, we found that roughly half of the runtime was spent actual clustering while most of the remainder went to parsing event files. Some nice improvements can be seen when compared to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;share.firefox.dev&#x2F;4qTDrfY&quot;&gt;earlier flamegraphs&lt;&#x2F;a&gt;, calculations for &lt;code&gt;bj_dist&lt;&#x2F;code&gt; (which required calculating &lt;code&gt;eta&lt;&#x2F;code&gt; and &lt;code&gt;phi&lt;&#x2F;code&gt;) no longer take half the time that clustering currently takes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;226206cc-fdb3-4fad-ba01-b67a3b7ece2b_1600x773.png&quot; alt=&quot;Julia Flamegraph&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Julia Flamegraph&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is where things got interesting. We initially thought Julia was running significantly faster than our Rust implementation, but through observing the flamegraph it turned out the Julia benchmarks were only measuring jet creation not event parsing or output. Once we accounted for that, the playing field leveled out considerably:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;substack-post-media.s3.amazonaws.com&#x2F;public&#x2F;images&#x2F;babbd4dd-80f8-47ea-9cd4-77050a6dd4b3_309x400.jpeg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rust:&lt;&#x2F;strong&gt; 498.003 µs ± 0.687 µs&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Julia:&lt;&#x2F;strong&gt; 487.902 µs ± 22.156 µs&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;C++:&lt;&#x2F;strong&gt; 365.222 µs ± 0.901 µs&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Julia and Rust land in essentially the same ballpark, with C++ still comfortably ahead. Currently, Julia has similar performance to our Rust implementation because of SIMD (Single Instruction Multiple Data), which tells the compiler to vectorize loops in assembly. Rust currently only supports SIMD for floats in the &lt;code&gt;nightly&lt;&#x2F;code&gt; crate, so for this first iteration we wanted to avoid its use, with it we most likely will pull fairly ahead of Julia.&lt;&#x2F;p&gt;
&lt;p&gt;However since Julia is a runtime language, the first iteration of the code runs extremely slow, otherwise known as the ‘Time to First Plot” problem. Lengthened compile time in Rust is a deliberate sacrifice for faster runtime programs and guaranteed memory safety. Compared to C++, Rust loses on the compile time game, taking around 30 seconds to fully compile versus C++’s three seconds. Rust is thus a happy middle ground between Julia and C++ that balances speed and memory management.&lt;&#x2F;p&gt;
&lt;p&gt;So why is our Rust implementation slower than C++?&lt;&#x2F;p&gt;
&lt;p&gt;A few reasons most likely. Because we use indices to track nearest neighbors, we need an extra pass through all active particles after each merge to make sure NN pointers stay valid. That introduces an additional constant-factor loop inside the O(n²) main loop. One potential fix is wrapping each PseudoJet in an Rc, which would allow multiple references to mutable jet instances and eliminate the index bookkeeping. Although, there is a possibility that the reference counting overhead could end up costing more than the loop it replaces. We’re also paying a cost by passing entire vectors by reference into one of our distance functions, and we’re investigating whether leveraging const more aggressively in our Rust code could close the gap, since the C++ implementation makes heavy use of inline const templating.&lt;&#x2F;p&gt;
&lt;p&gt;That said, profiling and iterating paid off. With the help of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nnethercote.github.io&#x2F;perf-book&#x2F;&quot;&gt;Rust Performance Book&lt;&#x2F;a&gt; and some build configuration changes, we brought our runtime down from 1.4 milliseconds to 650 microseconds which is roughly a 2x improvement from relatively simple optimizations (lazy initialization with OnceCell and updating NN indices for only active particles rather than the full list.)!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;&lt;strong&gt;next steps&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Fastjet-rs is very much an ongoing project and there are many other features that still need to be implemented. To start, we want to increase the number of jet finding algorithms we support as we currently only have Anti-kt. Another major feature we are missing is an O(nlogn) Voronio clustering algorithm that leverages a fascinating geometry based optimization trick.&lt;&#x2F;p&gt;
&lt;p&gt;So if you’re interested in seeing what we learn from any of these small projects stay tuned for the next part :)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;&lt;strong&gt;references&lt;&#x2F;strong&gt;&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;https:&#x2F;&#x2F;fastjet.fr&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JuliaHEP&#x2F;JetReconstruction.jl?tab=readme-ov-file&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;JuliaHEP&#x2F;JetReconstruction.jl?tab=readme-ov-file&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discord.com&#x2F;blog&#x2F;why-discord-is-switching-from-go-to-rust&quot;&gt;https:&#x2F;&#x2F;discord.com&#x2F;blog&#x2F;why-discord-is-switching-from-go-to-rust&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.figma.com&#x2F;blog&#x2F;rust-in-production-at-figma&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.figma.com&#x2F;blog&#x2F;rust-in-production-at-figma&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;blogs&#x2F;opensource&#x2F;why-aws-loves-rust-and-how-wed-like-to-help&#x2F;&quot;&gt;https:&#x2F;&#x2F;aws.amazon.com&#x2F;blogs&#x2F;opensource&#x2F;why-aws-loves-rust-and-how-wed-like-to-help&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
    </channel>
</rss>
