<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Syed Mannan Saood</title>
    <description>The latest articles on DEV Community by Syed Mannan Saood (@mannansaood_83).</description>
    <link>https://dev.to/mannansaood_83</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1113444%2F2b503855-c073-4cd2-8ab1-5e702f333230.png</url>
      <title>DEV Community: Syed Mannan Saood</title>
      <link>https://dev.to/mannansaood_83</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mannansaood_83"/>
    <language>en</language>
    <item>
      <title>OpenTelemetry eBPF Instrumentation: Zero-Code Observability Becomes a Standard</title>
      <dc:creator>Syed Mannan Saood</dc:creator>
      <pubDate>Tue, 28 Jul 2026 14:40:00 +0000</pubDate>
      <link>https://dev.to/mannansaood_83/opentelemetry-ebpf-instrumentation-zero-code-observability-becomes-a-standard-26h5</link>
      <guid>https://dev.to/mannansaood_83/opentelemetry-ebpf-instrumentation-zero-code-observability-becomes-a-standard-26h5</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; For twenty years, getting distributed traces out of an application meant adding an SDK, a language agent, or a sidecar code changes, restarts, dependency upgrades, and per-language maintenance. OpenTelemetry eBPF Instrumentation (OBI), which reached alpha in November 2025 and beta at KubeCon EU 2026, captures HTTP/gRPC/SQL/Redis/Kafka traces directly from the kernel with zero code changes, by attaching to a running process from outside it entirely. It's the OpenTelemetry project's formal successor to Grafana Beyla, now co-developed with Splunk, Coralogix, and Odigos. The catch: doing this from outside the process requires kernel privileges most security teams have spent years trying to eliminate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Instrumentation Problem OBI Is Solving
&lt;/h2&gt;

&lt;p&gt;Every observability approach before eBPF required getting &lt;em&gt;into&lt;/em&gt; the process somehow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual instrumentation:&lt;/strong&gt; developers add SDK calls around every operation they want traced. Accurate, but it's ongoing engineering work multiplied across every service, every language, every internal library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-instrumentation agents:&lt;/strong&gt; a language-specific agent (Java agent, Python auto-instrumentor, Node.js require-in-the-middle hook) attaches at startup and monkey-patches known libraries. Zero code changes, but it's still &lt;em&gt;in-process&lt;/em&gt;; it adds a runtime dependency, requires a restart to attach, and if that dependency has a vulnerability or a memory leak, it's now your application's problem too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service mesh sidecars:&lt;/strong&gt; move instrumentation to a proxy sitting next to the container. Solves the in-process dependency problem, but you're now running an extra container per pod, and the proxy still only sees traffic at the network boundary, not internal function calls.&lt;/p&gt;

&lt;p&gt;Every one of these approaches has the same shape: something has to be added to, or deployed alongside, the thing you want to observe.&lt;/p&gt;

&lt;p&gt;eBPF-based instrumentation breaks that pattern. It attaches from &lt;em&gt;outside&lt;/em&gt; the process, at the kernel level, without touching the application's code, dependencies, or container image at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  What OBI Actually Is
&lt;/h2&gt;

&lt;p&gt;OBI's official description: it "runs out-of-process and instruments at the protocol level, rather than at the library level." That phrasing matters; it's not patching a specific HTTP client library's send function. It's watching the actual bytes on the wire and in relevant kernel/library data structures, and reconstructing HTTP/gRPC/SQL/Redis/Kafka semantics from that.&lt;/p&gt;

&lt;p&gt;The practical claims from the OpenTelemetry project's own announcement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No restarts, no code changes, no configuration changes; telemetry capture starts on an already-running process&lt;/li&gt;
&lt;li&gt;No new application dependencies, so no new dependency-introduced vulnerabilities&lt;/li&gt;
&lt;li&gt;Minimal CPU/memory footprint even at high request rates, because the heavy lifting happens in the kernel&lt;/li&gt;
&lt;li&gt;Automatic W3C trace context propagation across every supported language, without each language needing its own context-propagation implementation&lt;/li&gt;
&lt;li&gt;Protocol coverage: HTTP/HTTPS, HTTP/2, gRPC, SQL, Redis, MongoDB, Kafka, GraphQL, Elasticsearch/OpenSearch, AWS S3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It detects when an application is &lt;em&gt;already&lt;/em&gt; instrumented with a proper OpenTelemetry SDK and avoids duplicating those signals, meaning it's explicitly designed to coexist with, not replace, existing instrumentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lineage: From Beyla to OBI
&lt;/h2&gt;

&lt;p&gt;OBI didn't start from nothing. It's the direct continuation of &lt;strong&gt;Grafana Beyla&lt;/strong&gt;, first released by Grafana Labs in September 2023, built specifically to auto-instrument compiled languages like Go and Rust where you can't easily inject a runtime agent the way you can with Java or Python.&lt;/p&gt;

&lt;p&gt;Earlier in 2025, Grafana Labs donated Beyla to the OpenTelemetry project. The first alpha release under the new name OpenTelemetry eBPF Instrumentation landed November 3, 2025, co-authored by maintainers from Grafana Labs and Splunk, with Coralogix and Odigos also contributing. Splunk then announced the beta at KubeCon + CloudNativeCon Europe 2026 in Amsterdam, alongside general availability of the Splunk Operator for Kubernetes.&lt;/p&gt;

&lt;p&gt;Why does the donation-and-rename matter architecturally, not just organizationally? Before OBI, the eBPF observability space was fragmented by vendor: Pixie was tied to New Relic, Beyla skewed toward Grafana Cloud, and Cilium Hubble covered network flows (L3/L4) but stopped before application-level tracing. OBI is explicitly positioned as the vendor-neutral convergence point; a CNCF Observability TAG survey found 67% of production Kubernetes clusters were already running at least one eBPF-based observability tool by early 2026, which is the adoption curve that made a standard layer worth building.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture: Two Spaces, One Pipeline
&lt;/h2&gt;

&lt;p&gt;OBI's system architecture (per the project's own DeepWiki-documented internals) splits cleanly into kernel space and user space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kernel space:&lt;/strong&gt; eBPF programs attached via kprobes, uprobes, and TC (traffic control) hooks capture network and application events, storing intermediate state in BPF maps and the shared memory structures that let kernel-space eBPF programs and user-space processes exchange data safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User space:&lt;/strong&gt; a discovery subsystem identifies which running processes should be instrumented (based on configurable criteria process name, port, Kubernetes labels), a tracer component controls the eBPF program lifecycle (loading, attaching, unloading), reader components consume events out of the BPF maps, processing layers enrich and normalize that raw data into OpenTelemetry's semantic conventions, and exporters ship the final metrics/traces/spans to a backend.&lt;/p&gt;

&lt;p&gt;The mechanism for capturing HTTP/gRPC data specifically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. OBI inspects the target binary to identify language/framework
2. Attaches uprobes at entry/return points of relevant functions
   (e.g., Go's net/http handler functions, gRPC stream methods)
3. Attaches to TLS library functions (e.g., OpenSSL) to read
   plaintext BEFORE encryption / AFTER decryption
4. Captures request/response metadata directly from kernel
   and userspace data structures at those hook points
5. Correlates request/response pairs, computes duration,
   status code, route assembles a span
6. Injects/reads W3C traceparent headers for cross-service correlation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3 is worth pausing on: because a uprobe sits at the function boundary inside the process's own address space, OBI can read HTTP payloads &lt;em&gt;before&lt;/em&gt; TLS encrypts them on the way out, and &lt;em&gt;after&lt;/em&gt; TLS decrypts them on the way in without ever touching the TLS handshake or terminating encryption anywhere. This is a meaningfully different approach from a network proxy, which only ever sees encrypted bytes unless it's doing active TLS termination.&lt;/p&gt;




&lt;h2&gt;
  
  
  The uprobe-only Pivot
&lt;/h2&gt;

&lt;p&gt;Here's a detail that didn't make it into the launch announcements but is architecturally significant: OBI &lt;strong&gt;removed kprobe-based collection in favour of a uprobe-only approach&lt;/strong&gt; (tracked in PR #752 on the project's GitHub).&lt;/p&gt;

&lt;p&gt;Why this matters: kprobes attach to kernel functions and syscalls, meaning a kprobe-based collector sits in the network datapath, observing every packet that crosses a syscall boundary, regardless of which process it belongs to. Uprobes attach to &lt;em&gt;specific user-space functions inside a specific process&lt;/em&gt;, meaning the instrumentation only fires when that particular application code path executes.&lt;/p&gt;

&lt;p&gt;The trade-off is explicit, and it's the same one that shows up in Cilium Hubble vs. Tetragon vs. Beyla comparisons generally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kprobe/network-datapath approach:&lt;/strong&gt; broader visibility (every packet, every process), but adds per-packet latency risk and a larger blast radius if something in the hot path misbehaves&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;uprobe-only approach:&lt;/strong&gt; narrower, application-scoped visibility, lower risk to network connectivity and packet forwarding, but you lose the ability to see traffic OBI wasn't specifically told to watch for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OBI's maintainers chose the narrower, safer default. If you're building or evaluating a network-observability tool that does sit in the datapath packet inspection, flow tracking, that category, this is precisely the design fork you're navigating, and OBI's choice tells you which side of the safety/coverage trade-off the OpenTelemetry community landed on for &lt;em&gt;application&lt;/em&gt; tracing specifically. It doesn't mean the datapath approach is wrong; Cilium Hubble and Tetragon exist specifically because network-layer and security-enforcement use cases need that broader vantage point. It means OBI scoped itself deliberately to stay out of that territory.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Zero-Code Actually Costs: Kernel Privileges
&lt;/h2&gt;

&lt;p&gt;Here's the part vendor announcements gloss over. To load eBPF programs, attach uprobes to arbitrary processes, and resolve symbols for accurate function-boundary hooking, OBI needs real kernel privileges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CAP_BPF&lt;/code&gt;&lt;/strong&gt; load and manage eBPF programs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CAP_PERFMON&lt;/code&gt;&lt;/strong&gt; open perf event buffers, attach kprobes/uprobes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CAP_SYS_PTRACE&lt;/code&gt;&lt;/strong&gt; read process memory for symbol resolution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CAP_NET_ADMIN&lt;/code&gt;&lt;/strong&gt; required specifically for TC eBPF socket-level hooks (kernel 5.8+)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical result, as one security-focused writeup on Beyla's model puts it plainly: this is "a process that can read arbitrary kernel structures, load unrestricted eBPF programs, and attach to any process anywhere on the node." That's not a criticism of implementation quality; it's the structural cost of a tool whose entire value proposition is "see everything without being told about it in advance."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The data-scope consequence:&lt;/strong&gt; because OBI captures full HTTP request paths, query strings, and response codes directly from kernel-level interception, any PII embedded in a URL user IDs, session tokens, search parameters, record identifiers flows straight into trace output. This isn't a bug; it's the same category of exposure any full-request-capture tool has, but the "zero code, drop it in and go" pitch makes it easy to deploy before anyone's reviewed what's actually landing in your trace backend.&lt;/p&gt;

&lt;p&gt;For production deployment, the security-hardening path is to replace the common &lt;code&gt;privileged: true&lt;/code&gt; DaemonSet config with the specific minimal capability set above, plus &lt;code&gt;allowPrivilegeEscalation: false&lt;/code&gt; and a read-only root filesystem, none of which is the default in most getting-started guides.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where OBI Genuinely Struggles
&lt;/h2&gt;

&lt;p&gt;The OpenTelemetry project's own release notes are unusually candid about current limitations, which is worth taking at face value rather than reading as false modesty:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distributed tracing quality varies sharply by language/runtime.&lt;/strong&gt; OBI works well for Go (HTTP and gRPC), Node.js (HTTP), Python (HTTP), NGINX (HTTP), and PHP (HTTP/FPM). It currently does &lt;strong&gt;not&lt;/strong&gt; handle distributed tracing well for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reactive programming frameworks&lt;/li&gt;
&lt;li&gt;Java virtual threads&lt;/li&gt;
&lt;li&gt;Complex thread pools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The underlying reason is structural, not a missing feature: uprobe-based correlation relies on being able to associate an incoming request with the specific execution context that handles it. Reactive frameworks and virtual threads deliberately decouple "the thread that received the request" from "the thread that ultimately processes it", exactly the assumption uprobe-based request/response pairing depends on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's additive, not a replacement.&lt;/strong&gt; The project's own guidance is direct: if you've successfully instrumented a service with a proper OpenTelemetry SDK or agent, there's rarely a reason to rip that out for OBI, unless you're hitting specific performance or cost problems the SDK approach caused. OBI's stated best use cases are (a) getting &lt;em&gt;any&lt;/em&gt; telemetry out of currently-uninstrumented services, especially compiled binaries where SDK instrumentation is awkward, and (b) covering libraries that don't have official OpenTelemetry support at all: legacy versions, unmaintained packages, anything nobody's written an instrumentation for.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 2026 Roadmap
&lt;/h2&gt;

&lt;p&gt;The OBI SIG's stated 2026 priorities, per their published goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stable 1.0 release&lt;/strong&gt; the flagship goal, requiring documentation completeness, configuration standardisation, and production-readiness validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aligning network attributes&lt;/strong&gt; with OpenTelemetry semantic conventions, and updating all semantic convention usage to current versions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An OpenTelemetry Collector distribution with OBI as a receiver&lt;/strong&gt;, meaning OBI becomes a pluggable data source inside the standard Collector pipeline, rather than a standalone agent with its own export path&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with the OpenTelemetry eBPF profiler&lt;/strong&gt; for unified observability (metrics, traces, and continuous profiling from the same kernel-level vantage point)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime metrics directly from OBI&lt;/strong&gt;, without needing a separate metrics exporter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Collector-receiver integration is the one worth watching most closely; it's the difference between OBI as a standalone tool you deploy and manage separately, versus OBI as one interchangeable input into whatever OpenTelemetry pipeline you're already running.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architectural Question This Raises
&lt;/h2&gt;

&lt;p&gt;If you're building or evaluating any eBPF-based tooling, whether that's application tracing, network flow visibility, or packet-level inspection, OBI's trajectory says something concrete about where the ecosystem is converging:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vendor-specific eBPF tools are consolidating into CNCF-governed standards.&lt;/strong&gt; The Beyla → OBI donation, following Cilium's earlier CNCF graduation, suggests this is the pattern going forward rather than a one-off.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The uprobe-vs-kprobe scoping decision is a real architectural fork&lt;/strong&gt;, not an implementation detail, and it's one every eBPF observability or security tool has to make explicitly, trading breadth of visibility against blast radius and privilege requirements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;"Zero-code" doesn't mean "zero operational cost."&lt;/strong&gt; It relocates the cost from application-code maintenance to kernel-privilege management and PII-in-traces governance a different problem, not a solved one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Coexistence, not replacement, is the honest framing.&lt;/strong&gt; OBI fills gaps around existing SDK instrumentation rather than obsoleting it, and the project says so explicitly rather than overselling.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Zero-code instrumentation is real and works well for a specific slice of the problem&lt;/strong&gt; HTTP/gRPC/SQL tracing for mainstream frameworks in Go, Python, Node.js, PHP, and NGINX, especially for services you can't or don't want to modify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The uprobe-only architecture is a deliberate safety trade-off&lt;/strong&gt;, not a limitation; it trades broader kprobe-level visibility for lower blast radius in the network datapath, which is the right call for application tracing even though it means OBI can't see traffic patterns a network-layer tool would.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kernel privilege requirements are substantial and often under-hardened by default&lt;/strong&gt; CAP_BPF, CAP_PERFMON, CAP_SYS_PTRACE, and CAP_NET_ADMIN together grant broad node-level access that most getting-started guides deploy with &lt;code&gt;privileged: true&lt;/code&gt; rather than the minimal capability set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distributed tracing correlation breaks down exactly where execution context decouples from the receiving thread&lt;/strong&gt; reactive frameworks, virtual threads, complex pools because uprobe-based pairing fundamentally assumes a stable request-to-thread relationship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is consolidation, not novelty.&lt;/strong&gt; OBI's significance isn't a new technical capability; Beyla already did most of this it's that the OpenTelemetry Governance Committee now owns it, which is what turns a good vendor tool into infrastructure other tools build on top of.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"OpenTelemetry eBPF Instrumentation Marks the First Release" OpenTelemetry Blog, November 2025&lt;/li&gt;
&lt;li&gt;"OpenTelemetry eBPF Instrumentation 2026 Goals" OpenTelemetry Blog, January 2026&lt;/li&gt;
&lt;li&gt;OBI GitHub repository: &lt;code&gt;open-telemetry/opentelemetry-ebpf-instrumentation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Grafana Beyla documentation and original 2023 announcement blog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Related:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cilium Hubble (L3/L4 network flow visibility) and Tetragon (real-time eBPF security enforcement), the adjacent tools OBI explicitly doesn't try to replace&lt;/li&gt;
&lt;li&gt;CNCF Observability TAG Q1 2026 survey on eBPF observability adoption&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>sre</category>
      <category>cloudnative</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>QUIC-Based NAT Traversal: How PUNCH_ME_NOW Frames Are Standardizing Hole Punching</title>
      <dc:creator>Syed Mannan Saood</dc:creator>
      <pubDate>Fri, 17 Jul 2026 07:55:17 +0000</pubDate>
      <link>https://dev.to/mannansaood_83/quic-based-nat-traversal-how-punchmenow-frames-are-standardizing-hole-punching-2969</link>
      <guid>https://dev.to/mannansaood_83/quic-based-nat-traversal-how-punchmenow-frames-are-standardizing-hole-punching-2969</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; NAT traversal has historically relied on STUN/TURN/ICE running as an external signalling layer bolted onto whatever transport protocol you choose. A 2023 IETF draft by Marten Seemann (&lt;code&gt;draft-seemann-quic-nat-traversal&lt;/code&gt;) proposes something different: use QUIC's own path validation mechanism, extended with three new frames (&lt;code&gt;ADD_ADDRESS&lt;/code&gt;, &lt;code&gt;PUNCH_ME_NOW&lt;/code&gt;, &lt;code&gt;REMOVE_ADDRESS&lt;/code&gt;), to punch holes and migrate to direct paths natively. A 2024 measurement study confirms the theory that QUIC hole punching completes in 2–2.5 RTTs versus TCP's 2.5–3 RTTs, and connection migration for recovery saves 2–3 RTTs over re-punching. This piece breaks down the mechanism and what it means for anyone building peer-to-peer or relay-based networking tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Hole Punching Exists
&lt;/h2&gt;

&lt;p&gt;NAT was never designed for peer-to-peer communication. It was a patch for IPv4 exhaustion map a private address behind a router to a public one, kept a session table, and only let traffic back in if it matched an existing outbound flow.&lt;/p&gt;

&lt;p&gt;That works fine for client-server traffic. It breaks P2P by default, because neither peer can accept an unsolicited inbound connection.&lt;/p&gt;

&lt;p&gt;The scale of this problem is bigger than most people assume. Research on the PPLive P2P streaming system found that roughly 80% of end nodes sit behind NAT. A Bitcoin network study found a small subset of publicly reachable nodes carries 89% of transaction propagation precisely because most peers can't be dialled directly. NAT doesn't just add friction; it concentrates load onto whichever nodes happen to be reachable.&lt;/p&gt;

&lt;p&gt;Hole punching is the workaround: get both peers to send outbound packets toward each other at roughly the same time, so each NAT's session table records the other side's address as "already talked to," and inbound traffic slips through as if it were a reply.&lt;/p&gt;

&lt;p&gt;The mechanics of &lt;em&gt;why&lt;/em&gt; this works depend entirely on the NAT's mapping behaviour.&lt;/p&gt;




&lt;h2&gt;
  
  
  NAT Mapping Rules: The Foundation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Endpoint-Independent Mapping:&lt;/strong&gt; A private node's mapping to a public address stays the same regardless of which external peer it's talking to. If P is mapped to &lt;code&gt;nodeP&lt;/code&gt; when talking to N1, it's still &lt;code&gt;nodeP&lt;/code&gt; when talking to N2. This is what makes hole punching reliable: a relay server can hand out P's public mapping to any peer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Address and Port-Dependent Mapping:&lt;/strong&gt; The mapping changes per destination. P talking to N1 gets &lt;code&gt;nodeP1&lt;/code&gt;; P talking to N2 gets &lt;code&gt;nodeP2&lt;/code&gt;. A mapping learned via one connection is useless for punching a hole to a different peer.&lt;/p&gt;

&lt;p&gt;This distinction matters because it's the difference between hole punching working reliably and not working at all. Symmetric (address/port-dependent) NATs are the primary reason traversal fails in the real world, regardless of which transport protocol you use.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Classic Hole Punching Sequence
&lt;/h2&gt;

&lt;p&gt;With a relay server S and two clients A and B, both behind NAT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. A and B each connect to S and register their public and private addresses.
2. A requests B's address from S. S exchanges A's and B's public addresses.
3. A sends a connection request to B.
   → Hits NAT-B, gets dropped (no session entry exists yet on NAT-B for A→B)
   → But creates a session entry on NAT-A for A→B
4. B sends a connection request to A.
   → Hits NAT-A, and now MATCHES the session entry NAT-A just created
   → Passes through, reaching A
5. The "hole" is now open in both directions. Direct communication begins.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sequence is protocol-agnostic in concept. What differs is &lt;em&gt;how expensive&lt;/em&gt; each step is, depending on whether you're running TCP or QUIC underneath.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where TCP Hole Punching Breaks Down
&lt;/h2&gt;

&lt;p&gt;TCP hole punching carries three structural penalties:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Port multiplexing is mandatory.&lt;/strong&gt; TCP sockets are one-to-one; a local port can only be bound to a single socket. To simultaneously listen for an inbound connection and initiate an outbound one on the same port (required for hole punching), you need &lt;code&gt;SO_REUSEADDR&lt;/code&gt;/&lt;code&gt;SO_REUSEPORT&lt;/code&gt; tricks and careful socket lifecycle management. QUIC has no such restriction; multiple "connections" can share a UDP socket natively, since QUIC demultiplexes by Connection ID, not by the OS socket layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Handshake cost stacks with TLS.&lt;/strong&gt; TCP's three-way handshake is 1.5 RTTs before you've sent a single encrypted byte. TLS 1.3 on top adds another handshake because TCP operates in kernel space and can't natively fold TLS negotiation into its own handshake. QUIC integrates TLS 1.3 directly into the transport handshake, so encryption setup and connection setup happen in the same round trips.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Recovery means starting over.&lt;/strong&gt; If a punched TCP connection drops because a mobile client switched networks or a NAT session timed out, there is no lightweight recovery path. You re-punch from scratch: new relay round trip, new three-way handshake, new TLS negotiation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The IETF Draft: &lt;code&gt;draft-seemann-quic-nat-traversal&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Authored by Marten Seemann (Protocol Labs and the author of &lt;code&gt;quic-go&lt;/code&gt;), this draft defines a QUIC extension that turns the protocol's existing &lt;strong&gt;path validation&lt;/strong&gt; machinery into a traversal primitive without requiring a separate STUN stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  The core idea
&lt;/h3&gt;

&lt;p&gt;QUIC already has a mechanism for verifying a new network path is viable: &lt;strong&gt;path validation&lt;/strong&gt;, using &lt;code&gt;PATH_CHALLENGE&lt;/code&gt; and &lt;code&gt;PATH_RESPONSE&lt;/code&gt; frames (RFC 9000, §8.2). This exists for connection migration, proving a new IP: port pair actually works before switching the active connection to it.&lt;/p&gt;

&lt;p&gt;The draft's insight: if a client can trigger path validation on an &lt;em&gt;unverified&lt;/em&gt; address, one that hasn't been active on the connection yet, and the server does the same on its end simultaneously, the packets sent during validation create the NAT bindings needed for a direct path. Hole punching, essentially, for free, using the infrastructure QUIC already has.&lt;/p&gt;

&lt;p&gt;Critically, RFC 9000 assumes servers can already receive packets on a path without needing to create a NAT binding first. This draft explicitly extends path validation to work on the &lt;em&gt;server&lt;/em&gt; side too, specifically to accommodate NAT traversal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three new frames
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ADD_ADDRESS&lt;/code&gt;&lt;/strong&gt; server → client only. Advertises an address candidate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;ADD_ADDRESS&lt;/span&gt; &lt;span class="n"&gt;Frame&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x3d7e90&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mh"&gt;0x3d7e91&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;IPv4&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;IPv6&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sent incrementally as candidates are discovered, mirroring Trickle ICE (RFC 8838), rather than waiting for full candidate gathering to complete. Address matching (pairing local and remote candidates) happens entirely client-side, using ICE's pairing algorithm (RFC 8445 §5.1) as a reference, though implementations are free to diverge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;PUNCH_ME_NOW&lt;/code&gt;&lt;/strong&gt; client → server only. Requests that the server begin path validation on a specific candidate pair.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;PUNCH_ME_NOW&lt;/span&gt; &lt;span class="n"&gt;Frame&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x3d7e92&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mh"&gt;0x3d7e93&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Paired&lt;/span&gt; &lt;span class="n"&gt;With&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt; &lt;span class="n"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;IPv4&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;IPv6&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Round&lt;/code&gt; field batches punching attempts; a new round immediately cancels all in-flight probes from the previous one, giving both sides a way to reprioritise without waiting for timeouts. Concurrency is capped by the server's advertised limit, communicated via a new transport parameter (&lt;code&gt;nat_traversal&lt;/code&gt;, codepoint &lt;code&gt;0x3d7e9f0bca12fea6&lt;/code&gt;) during the handshake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;REMOVE_ADDRESS&lt;/code&gt;&lt;/strong&gt; server → client only. Invalidates a previously advertised candidate, e.g., when a network interface goes down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this is architecturally different from ICE
&lt;/h3&gt;

&lt;p&gt;Traditional ICE requires a signalling channel entirely separate from the media/data transport SDP offer/answer, typically over a WebSocket or SIP channel, coordinating STUN checks that happen independently.&lt;/p&gt;

&lt;p&gt;This draft collapses signalling and traversal into the same QUIC connection. If the two nodes already have a proxied QUIC connection to the relay (e.g., via &lt;code&gt;CONNECT-UDP-LISTEN&lt;/code&gt;), they can start exchanging application data over the relay &lt;em&gt;immediately&lt;/em&gt;, then upgrade to a direct path via connection migration once a punched route is validated, with the application never seeing an interruption.&lt;/p&gt;

&lt;p&gt;There's also a real security tradeoff called out explicitly in the draft: extending path validation to the server side means a malicious client can direct the server to send validation traffic toward a third-party target IP, structurally similar to the amplification risk that connection-establishment address validation was designed to prevent. The draft's answer is rate limiting on unverified paths, though the amplification mitigation section is still marked as a TODO in the current revision.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Measurement Data Actually Shows
&lt;/h2&gt;

&lt;p&gt;The IETF draft is a mechanism proposal. A 2024 paper, &lt;em&gt;"Implementing NAT Hole Punching with QUIC"&lt;/em&gt; (Liang, Xu, Wang, Yang, Zhang), independently measured whether the theoretical advantage holds up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experimental setup
&lt;/h3&gt;

&lt;p&gt;Two Docker-simulated LANs, each behind a NAT enforcing &lt;strong&gt;endpoint-independent mapping&lt;/strong&gt; via &lt;code&gt;iptables&lt;/code&gt; SNAT rules, are connected through a relay server. The team used Linux &lt;code&gt;netem&lt;/code&gt; and Traffic Control (TC) to inject controlled RTT (20/100/200ms) and packet loss (0%/1%/1.5%/2%) across 12 combinations, running 100 trials each.&lt;/p&gt;

&lt;h3&gt;
  
  
  The RTT math
&lt;/h3&gt;

&lt;p&gt;Ideal-case hole-punching time, derived analytically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;QUIC:&lt;/strong&gt; 1 RTT (address exchange via relay) + 1 RTT (QUIC handshake) = &lt;strong&gt;2 RTTs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TCP:&lt;/strong&gt; 1 RTT (address exchange) + 1.5 RTTs (TCP three-way handshake) = &lt;strong&gt;2.5 RTTs&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Accounting for the race condition where one side's first punch packet arrives at the peer's NAT before the peer's own punch packet creates the matching session entry (forcing a discard-and-retry), the &lt;em&gt;realistic&lt;/em&gt; range extends to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QUIC: 2–2.5 RTTs&lt;/li&gt;
&lt;li&gt;TCP: 2.5–3 RTTs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Measured results (0% packet loss)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RTT&lt;/th&gt;
&lt;th&gt;QUIC (measured)&lt;/th&gt;
&lt;th&gt;TCP (measured)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20ms&lt;/td&gt;
&lt;td&gt;~55ms&lt;/td&gt;
&lt;td&gt;~56ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100ms&lt;/td&gt;
&lt;td&gt;~213ms&lt;/td&gt;
&lt;td&gt;~256ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200ms&lt;/td&gt;
&lt;td&gt;~416ms&lt;/td&gt;
&lt;td&gt;~505ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At low RTT, QUIC's advantage is marginal; the paper attributes this to QUIC's user-space processing overhead eating into its theoretical edge. At higher RTT, the gap widens meaningfully: QUIC completes roughly 15–18% faster than TCP as latency increases.&lt;/p&gt;

&lt;h3&gt;
  
  
  The packet loss finding that matters more
&lt;/h3&gt;

&lt;p&gt;Under 1% packet loss, the gap becomes dramatic not because of RTT math, but because of &lt;strong&gt;retransmission timeout design&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QUIC's retransmission timer (per &lt;code&gt;draft-ietf-quic-recovery&lt;/code&gt;): fixed at &lt;strong&gt;200ms&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;TCP's RTO (per RFC 6298... the paper cites RFC 6289, likely a typo for 6298): minimum &lt;strong&gt;1 second&lt;/strong&gt;, calculated from smoothed RTT and variance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the RTT/loss levels tested (max 200ms RTT), TCP's RTO floor of 1 second dominates a single lost packet during hole punching costs TCP roughly 5x what it costs QUIC. This is the paper's most citable finding: &lt;strong&gt;QUIC's advantage in hole punching isn't primarily about handshake efficiency; it's about how much cheaper packet loss is during the punch itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bandwidth, across unlimited/10Gbps/100Mbps/1Mbps conditions, showed negligible effect on hole punching time, unsurprising, since punching exchanges small control packets, not bulk data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connection Migration as a Recovery Mechanism
&lt;/h2&gt;

&lt;p&gt;The second half of the measurement paper addresses a problem the IETF draft doesn't fully resolve: &lt;strong&gt;what happens when a successfully punched connection breaks&lt;/strong&gt; a client switches from WiFi to cellular, a NAT session times out, a device roams to a new network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why you can't just reconnect directly
&lt;/h3&gt;

&lt;p&gt;If A's address changes, A cannot simply send a connection migration probe directly to B. NAT-B has no session entry for A's &lt;em&gt;new&lt;/em&gt; address, so the probe gets dropped. The relay server has to be involved again to re-establish the path, but &lt;em&gt;how&lt;/em&gt; it's involved determines the cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: QUIC connection migration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. A → S: "My address changed; tell B, and ask B to send me data"
2. S → B: forwards A's new public address
3. B → A: sends data (dropped by NAT-A, no session entry yet,
           but this creates a NAT-B session entry for B→A traffic)
4. A → B: sends a connection migration request
           (now passes through NAT-B, since step 3 created the matching entry)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total: &lt;code&gt;T_migrate = T(A→S) + T(S→B) + T(B→A) + T(A→B)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Because this reuses the &lt;strong&gt;existing QUIC connection's Connection ID&lt;/strong&gt;, QUIC identifies a connection by CID, not by the IP: port 5-tuple, no new handshake, no new TLS negotiation. The connection ID persists across the address change, and &lt;code&gt;PATH_CHALLENGE&lt;/code&gt;/&lt;code&gt;PATH_RESPONSE&lt;/code&gt; frames confirm the new path is live before the application-layer connection formally migrates to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Re-punching from scratch (QUIC or TCP)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. A ↔ S: re-establish an entirely new QUIC/TCP connection
2. A → S: request S forward A's new address to B, ask B to send data
3. S → B: forwards A's address
4. B → A: sends data (same drop-then-record pattern as above)
5. A → B: new connection request (passes through via the fresh NAT-B entry)
6. Connection established, data flows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total: &lt;code&gt;T_re-punch = T(A↔S handshake) + T(A→S) + T(S→B) + T(B→A) + T(A→B handshake) + T(A→B)&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The delta
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Δt = T_re-punching - T_migrate = T(A↔S handshake) + T(A→B handshake)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The paper's result: &lt;strong&gt;connection migration saves 2 RTTs versus QUIC re-punching and 3 RTTs versus TCP re-punching&lt;/strong&gt; because it eliminates both the A-S and A-B handshake steps entirely. The connection literally never tore down at the protocol level; only the underlying path changed.&lt;/p&gt;

&lt;p&gt;For anything running on mobile networks where WiFi-to-cellular handoffs are routine, not exceptional, this is the difference between a visible reconnection stall and a seamless path switch that the application layer never observes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means Architecturally
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For relay-based P2P tools:&lt;/strong&gt; if your relay-and-punch architecture is built on TCP today, the migration path to QUIC isn't just "swap the transport"; it changes what's &lt;em&gt;possible&lt;/em&gt;. Port multiplexing code disappears. TLS setup collapses into the transport handshake. And connection migration gives you a recovery primitive that TCP structurally cannot offer without an application-layer reconnection protocol bolted on top.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For observability tooling watching this traffic:&lt;/strong&gt; QUIC's traversal packets look different at the kernel/network level than TCP's. Path validation probes on previously unseen 5-tuples, &lt;code&gt;PATH_CHALLENGE&lt;/code&gt;/&lt;code&gt;PATH_RESPONSE&lt;/code&gt; exchanges, and Connection-ID-based flow correlation (rather than 5-tuple correlation) all mean that eBPF-based flow tracking built assuming TCP semantics will misattribute or fragment what is, at the QUIC layer, a single continuous connection surviving multiple address changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest caveat:&lt;/strong&gt; none of this works if you're behind a symmetric (address/port-dependent) NAT which the measurement paper explicitly excludes from its test environment because "testing revealed NAT devices in our current real network follow this rule," i.e., symmetric NAT is what they actually encountered, and they had to &lt;em&gt;force&lt;/em&gt; endpoint-independent mapping via &lt;code&gt;iptables&lt;/code&gt; to get clean results. That's worth sitting with: the protocol-level advantages of QUIC hole punching are real, but they don't solve the hardest traversal cases; those still fall back to relay (TURN-equivalent) traffic regardless of transport.&lt;/p&gt;




&lt;h2&gt;
  
  
  Current Status
&lt;/h2&gt;

&lt;p&gt;The draft (&lt;code&gt;draft-seemann-quic-nat-traversal-01&lt;/code&gt;) expired in April 2024 per the IETF's standard six-month draft lifecycle, which is normal for active work-in-progress documents. It doesn't mean the effort was abandoned, but it does mean this is pre-standardisation, not an RFC. The IANA considerations and amplification-attack mitigation sections are explicitly marked incomplete in the current text. Implementations exist in research/prototype form (the measurement paper builds directly on &lt;code&gt;quic-go&lt;/code&gt;), but there is no ratified RFC number yet, and production libraries implementing this exact frame set are not widely deployed as of this writing.&lt;/p&gt;

&lt;p&gt;The libp2p ecosystem, where Seemann's co-author on the related DCUtR (Direct Connection Upgrade through Relay) work also operates, is the most likely first production consumer of this mechanism, given the direct lineage from libp2p's existing hole-punching service.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;QUIC's advantage in hole punching is real but modest at the handshake level&lt;/strong&gt; 2 RTTs versus TCP's 2.5, narrowing further once you account for user-space processing overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bigger win is under packet loss&lt;/strong&gt;, where QUIC's 200ms retransmission timer beats TCP's 1-second RTO floor by roughly 5x and hole punching, happening over lossy, unestablished paths, is exactly the scenario where this matters most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection migration is the more significant architectural advantage&lt;/strong&gt; 2-3 RTT savings on recovery, and more importantly, a recovery model that doesn't require tearing down and re-establishing application state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symmetric NAT is still the hard problem&lt;/strong&gt; that this doesn't solve. No transport-level trick changes the fundamental traversal math when NAT mappings are destination-dependent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is pre-standard.&lt;/strong&gt; Useful to understand and prototype against, not yet something to build production reliability guarantees on top of.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;draft-seemann-quic-nat-traversal-01&lt;/code&gt; (IETF, October 2023)&lt;/li&gt;
&lt;li&gt;"Implementing NAT Hole Punching with QUIC" — Liang, Xu, Wang, Yang, Zhang (arXiv:2408.01791, 2024)&lt;/li&gt;
&lt;li&gt;"Decentralized Hole Punching" — Seemann, Inden, Vyzovitis (ICDCSW 2022)&lt;/li&gt;
&lt;li&gt;RFC 9000 (QUIC Transport), RFC 8445 (ICE), RFC 8838 (Trickle ICE)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Related:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;draft-ietf-quic-multipath&lt;/code&gt; — using multiple validated paths simultaneously rather than migrating between them&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;draft-ietf-masque-connect-udp-listen&lt;/code&gt; — the proxying mechanism this draft assumes for the relay phase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Discussion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're running relay infrastructure today, is QUIC migration worth the rewrite over TCP re-punching?&lt;br&gt;
Anyone tested this against symmetric NAT in the wild?&lt;/p&gt;

</description>
      <category>network</category>
      <category>networking</category>
      <category>infrastructure</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>RISC-V Vector Extension (RVV): SIMD for the Open ISA</title>
      <dc:creator>Syed Mannan Saood</dc:creator>
      <pubDate>Tue, 26 May 2026 09:18:19 +0000</pubDate>
      <link>https://dev.to/mannansaood_83/risc-v-vector-extension-rvv-simd-for-the-open-isa-3aon</link>
      <guid>https://dev.to/mannansaood_83/risc-v-vector-extension-rvv-simd-for-the-open-isa-3aon</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; RISC-V’s Vector Extension (RVV) brings length-agnostic SIMD to the open ISA. Unlike x86’s fixed-width AVX or ARM’s NEON, RVV uses a variable-length vector model where software writes to abstract vector registers, and hardware executes with any physical width. This enables code portability across implementations—from tiny embedded cores to massive supercomputers—without recompilation. RVV 1.0 is ratified, shipping in real silicon, and positioned to dominate edge AI, HPC, and custom accelerators.&lt;/p&gt;




&lt;h2&gt;
  
  
  The SIMD Landscape Problem
&lt;/h2&gt;

&lt;p&gt;Modern processors need SIMD (Single Instruction Multiple Data) for performance. Processing one data element per instruction is too slow for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image/video processing&lt;/li&gt;
&lt;li&gt;Machine learning inference&lt;/li&gt;
&lt;li&gt;Scientific computing&lt;/li&gt;
&lt;li&gt;Signal processing&lt;/li&gt;
&lt;li&gt;Compression/encryption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every major architecture has SIMD extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;x86:&lt;/strong&gt; SSE → AVX → AVX-512 (128-bit → 256-bit → 512-bit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ARM:&lt;/strong&gt; NEON (128-bit) → SVE/SVE2 (variable, 128-2048 bits)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RISC-V:&lt;/strong&gt; RVV (variable, application-agnostic)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But there’s a fundamental problem with how x86 and early ARM approached this.&lt;/p&gt;




&lt;h2&gt;
  
  
  The x86 SIMD Evolution Disaster
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Compatibility Nightmare
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;x86’s SIMD history:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1999: SSE (128-bit, 4 × FP32)
      __m128 vec = _mm_add_ps(a, b);

2011: AVX (256-bit, 8 × FP32)  
      __m256 vec = _mm256_add_ps(a, b);  // New instruction!

2017: AVX-512 (512-bit, 16 × FP32)
      __m512 vec = _mm512_add_ps(a, b);  // Yet another instruction!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Each generation requires &lt;strong&gt;completely new instructions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code compiled for AVX-512:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;process_avx512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;__m512&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm512_loadu_ps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm512_mul_ps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_mm512_storeu_ps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Won’t run on AVX2 processors.&lt;/strong&gt; Different width = different code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Libraries ship multiple code paths (SSE, AVX, AVX-512)&lt;/li&gt;
&lt;li&gt;Runtime detection needed (CPUID checks)&lt;/li&gt;
&lt;li&gt;Binary bloat (3-4× code size)&lt;/li&gt;
&lt;li&gt;Maintenance nightmare&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production example (FFmpeg):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Actual FFmpeg code pattern&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cpu_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;AV_CPU_FLAG_AVX512&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ff_process_avx512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cpu_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;AV_CPU_FLAG_AVX2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ff_process_avx2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cpu_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;AV_CPU_FLAG_SSE4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ff_process_sse4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ff_process_scalar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every function duplicated 4 times!&lt;/p&gt;

&lt;h3&gt;
  
  
  The Market Fragmentation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;x86 processors in 2025:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low-power laptops: 128-bit SIMD only&lt;/li&gt;
&lt;li&gt;Desktop CPUs: 256-bit AVX2&lt;/li&gt;
&lt;li&gt;High-end servers: 512-bit AVX-512&lt;/li&gt;
&lt;li&gt;Some servers: AVX-512 disabled (heat/cost)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Your optimized AVX-512 code?&lt;/strong&gt; Runs on &amp;lt;20% of x86 CPUs.&lt;/p&gt;




&lt;h2&gt;
  
  
  ARM SVE: The Right Idea, Complex Execution
&lt;/h2&gt;

&lt;p&gt;ARM learned from x86’s mistakes with &lt;strong&gt;Scalable Vector Extension (SVE)&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SVE’s Variable-Length Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SVE code - vector length agnostic!&lt;/span&gt;
&lt;span class="n"&gt;svfloat32_t&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;svld1_f32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;svmul_f32_z&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;svst1_f32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key innovation:&lt;/strong&gt; Same code runs on 128-bit, 256-bit, 512-bit, or 2048-bit hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How:&lt;/strong&gt; Predication and variable-length registers.&lt;/p&gt;

&lt;h3&gt;
  
  
  But SVE Has Issues
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex predicate registers&lt;/li&gt;
&lt;li&gt;Steep learning curve&lt;/li&gt;
&lt;li&gt;Limited compiler support initially&lt;/li&gt;
&lt;li&gt;ARM-specific (vendor lock-in)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Adoption:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fujitsu A64FX (HPC): 512-bit SVE&lt;/li&gt;
&lt;li&gt;AWS Graviton3: 256-bit SVE&lt;/li&gt;
&lt;li&gt;Consumer ARM: Still mostly NEON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Market fragmentation:&lt;/strong&gt; Different ARM vendors choose different widths.&lt;/p&gt;




&lt;h2&gt;
  
  
  RISC-V’s Solution: RVV
&lt;/h2&gt;

&lt;p&gt;RISC-V Vector Extension takes SVE’s length-agnostic concept and simplifies it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Philosophy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Write once, run anywhere—regardless of hardware vector width.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Software writes:     Hardware executes:
┌──────────────┐    ┌──────────────┐
│ vadd.vv v1,  │    │ 128-bit impl │
│   v2, v3     │ → │ 256-bit impl │
│              │    │ 512-bit impl │
└──────────────┘    │ 1024-bit impl│
                    └──────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All execute the same binary.&lt;/strong&gt; No recompilation needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vector Register Model
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;32 vector registers:&lt;/strong&gt; v0-v31&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key concept:&lt;/strong&gt; Each register has a &lt;strong&gt;logical length&lt;/strong&gt; independent of physical width.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Logical view (programmer sees):
v1 = [0, 1, 2, 3, ..., VL-1]  (VL = vector length)

Physical implementations:
128-bit: Processes 4 FP32 per cycle
256-bit: Processes 8 FP32 per cycle  
512-bit: Processes 16 FP32 per cycle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Same instruction, different throughput.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Vector Length (AVL)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The key abstraction:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Request to process 100 elements
li a0, 100           # Application vector length (AVL)
vsetvli t0, a0, e32  # Set vector length, element width = 32 bits

# t0 now contains actual VL (hardware-dependent)
# On 128-bit: VL = 4 (processes 4 × FP32)
# On 512-bit: VL = 16 (processes 16 × FP32)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Loop automatically adapts:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process_loop:
    vsetvli t0, a0, e32    # Get VL for remaining elements
    vle32.v v1, (a1)        # Load VL elements
    vadd.vv v1, v1, v2      # Add VL elements
    vse32.v v1, (a1)        # Store VL elements

    sub a0, a0, t0          # Remaining -= VL
    slli t1, t0, 2          # Advance pointer by VL*4 bytes
    add a1, a1, t1
    bnez a0, process_loop   # Loop if elements remain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Beautiful:&lt;/strong&gt; Same code works on any vector width. Hardware fills VL appropriately.&lt;/p&gt;




&lt;h2&gt;
  
  
  RVV Architecture Deep-Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vector Configuration (vsetvl)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Three parameters control vector execution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;vsetvli&lt;/span&gt; &lt;span class="n"&gt;rd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rs1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vtypei&lt;/span&gt;

&lt;span class="n"&gt;rd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;Destination&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receives&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="n"&gt;VL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rs1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AVL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vtypei&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vector&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LMUL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;vtypei encoding:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bits: [vlmul | vsew | vta | vma]

vsew: Element width
  e8:  8-bit elements
  e16: 16-bit elements
  e32: 32-bit elements
  e64: 64-bit elements

vlmul: Logical register grouping
  m1: Use 1 register
  m2: Use 2 registers as one (2× capacity)
  m4: Use 4 registers
  m8: Use 8 registers

vta: Tail agnostic (don't care about tail elements)
vma: Mask agnostic (don't care about masked elements)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vsetvli t0, a0, e32, m1, ta, ma
#              │   │   │   │   └─ Mask agnostic
#              │   │   │   └───── Tail agnostic  
#              │   │   └───────── LMUL = 1 register
#              │   └───────────── Element size = 32 bits
#              └───────────────── AVL from a0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  LMUL: Register Grouping
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Processing wide data types or increasing throughput.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Group registers together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LMUL=1 (m1):
v1 = single register

LMUL=2 (m2):  
v2 = {v2, v3} grouped as one logical register (2× capacity)

LMUL=4 (m4):
v4 = {v4, v5, v6, v7} (4× capacity)

LMUL=8 (m8):
v8 = {v8, v9, ..., v15} (8× capacity)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Process 64-bit doubles, need more capacity
vsetvli t0, a0, e64, m2, ta, ma  # Use register pairs
vle64.v v2, (a1)                  # Loads into v2+v3
vfmul.vv v2, v2, v4               # Multiply (v2,v3) × (v4,v5)
vse64.v v2, (a1)                  # Store from v2+v3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; More capacity, fewer independent vectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fractional LMUL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For small element widths:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LMUL=1/2 (mf2): Use half a register
LMUL=1/4 (mf4): Use quarter register  
LMUL=1/8 (mf8): Use eighth register
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Process 8-bit pixels efficiently
vsetvli t0, a0, e8, mf2, ta, ma  # 8-bit elements, half register
vle8.v v1, (a1)                   # Load pixels
vadd.vi v1, v1, 5                 # Add constant
vse8.v v1, (a1)                   # Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; More independent vectors for narrow data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vector Instruction Categories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vsetvli rd, rs1, vtypei    # Set VL by AVL
vsetivli rd, uimm, vtypei  # Set VL by immediate
vsetvl rd, rs1, rs2        # Set VL, type from register
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Load/Store
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Unit-stride (contiguous):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vle32.v v1, (a0)     # Load 32-bit elements
vse32.v v1, (a0)     # Store 32-bit elements
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strided (fixed stride):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vlse32.v v1, (a0), a1  # Load with stride a1
vsse32.v v1, (a0), a1  # Store with stride a1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Indexed (gather/scatter):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vlxei32.v v1, (a0), v2  # Load indexed by v2
vsxei32.v v1, (a0), v2  # Store indexed by v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Segment (structure-of-arrays):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vlseg3e32.v v1, (a0)  # Load 3-element structures
                      # v1 = {x0, x1, x2, ...}
                      # v2 = {y0, y1, y2, ...}
                      # v3 = {z0, z1, z2, ...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Arithmetic
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Integer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vadd.vv v1, v2, v3     # Vector + vector
vadd.vx v1, v2, a0     # Vector + scalar
vadd.vi v1, v2, 5      # Vector + immediate
vsub.vv v1, v2, v3     # Subtract
vmul.vv v1, v2, v3     # Multiply
vdiv.vv v1, v2, v3     # Divide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Floating-point:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vfadd.vv v1, v2, v3    # FP add
vfmul.vv v1, v2, v3    # FP multiply
vfmadd.vv v1, v2, v3   # FP fused multiply-add: v1 = v1 + v2*v3
vfdiv.vv v1, v2, v3    # FP divide
vfsqrt.v v1, v2        # FP square root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Widening operations:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vwmul.vv v2, v1, v3    # Multiply e32 → e64
                       # v1,v3 are 32-bit
                       # v2 is 64-bit result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Logical/Shift
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vand.vv v1, v2, v3     # Bitwise AND
vor.vv v1, v2, v3      # Bitwise OR
vxor.vv v1, v2, v3     # Bitwise XOR
vsll.vv v1, v2, v3     # Shift left logical
vsra.vv v1, v2, v3     # Shift right arithmetic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Comparison &amp;amp; Masking
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vmseq.vv v0, v1, v2    # Set mask: v1 == v2
vmslt.vv v0, v1, v2    # Set mask: v1 &amp;lt; v2
vmsle.vv v0, v1, v2    # Set mask: v1 &amp;lt;= v2

# Use mask in operations
vadd.vv v3, v1, v2, v0.t  # Add only where mask is true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Permutations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vslideup.vi v1, v2, 5   # Slide up by 5 positions
vslidedown.vi v1, v2, 3 # Slide down by 3 positions
vrgather.vv v1, v2, v3  # Gather elements by index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Reductions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vredsum.vs v3, v1, v2   # Sum reduction
                        # v3[0] = v2[0] + sum(v1)
vredmax.vs v3, v1, v2   # Max reduction
vredmin.vs v3, v1, v2   # Min reduction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Code Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Example 1: SAXPY (y = a*x + y)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;C code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;saxpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RISC-V RVV assembly:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;saxpy:
    vsetvli zero, zero, e32, m1, ta, ma  # Set max VL for e32

loop:
    vsetvli t0, a3, e32, m1, ta, ma      # VL = min(AVL, VLMAX)
    vle32.v v0, (a1)                      # Load x[i:i+VL]
    vle32.v v1, (a2)                      # Load y[i:i+VL]
    vfmacc.vf v1, fa0, v0                 # v1 = v1 + a * v0
    vse32.v v1, (a2)                      # Store y[i:i+VL]

    sub a3, a3, t0                        # Remaining -= VL
    slli t1, t0, 2                        # Offset = VL * 4 bytes
    add a1, a1, t1                        # x += offset
    add a2, a2, t1                        # y += offset
    bnez a3, loop                         # Loop if remaining &amp;gt; 0

    ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Portable:&lt;/strong&gt; Works on 128-bit, 256-bit, 512-bit, 1024-bit implementations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Dot Product
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;C code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;dot_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RVV assembly:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dot_product:
    vsetvli zero, zero, e32, m1, ta, ma
    vmv.v.i v2, 0                         # v2 = accumulator = 0

loop:
    vsetvli t0, a2, e32, m1, ta, ma
    vle32.v v0, (a0)                      # Load a[i:i+VL]
    vle32.v v1, (a1)                      # Load b[i:i+VL]
    vfmacc.vv v2, v0, v1                  # v2 += v0 * v1

    sub a2, a2, t0
    slli t1, t0, 2
    add a0, a0, t1
    add a1, a1, t1
    bnez a2, loop

    # Reduce v2 to scalar
    vfmv.s.f v3, ft0                      # v3[0] = 0.0
    vfredusum.vs v3, v2, v3               # v3[0] = sum(v2)
    vfmv.f.s fa0, v3                      # Return in fa0

    ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 3: RGB to Grayscale
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;C code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;rgb_to_gray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RVV assembly (simplified):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rgb_to_gray:
    vsetvli zero, zero, e8, m1, ta, ma

loop:
    vsetvli t0, a2, e8, m1, ta, ma
    vlseg3e8.v v0, (a0)       # Load R,G,B into v0,v1,v2
                               # v0 = {r0, r1, r2, ...}
                               # v1 = {g0, g1, g2, ...}
                               # v2 = {b0, b1, b2, ...}

    # Widen to 16-bit for multiplication
    vwmulu.vx v4, v0, 77      # v4 = r * 77 (16-bit)
    vwmaccu.vx v4, v1, 150    # v4 += g * 150
    vwmaccu.vx v4, v2, 29     # v4 += b * 29

    # Shift right by 8, narrow to 8-bit
    vnsrl.wi v3, v4, 8        # v3 = v4 &amp;gt;&amp;gt; 8 (narrow to 8-bit)

    vse8.v v3, (a1)           # Store grayscale

    sub a2, a2, t0
    li t1, 3
    mul t2, t0, t1            # RGB offset = VL * 3
    add a0, a0, t2
    add a1, a1, t0
    bnez a2, loop

    ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Compiler Support
&lt;/h2&gt;

&lt;h3&gt;
  
  
  GCC Intrinsics
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RVV intrinsics follow a pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;riscv_vector.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Naming: v&amp;lt;op&amp;gt;_&amp;lt;type&amp;gt;&amp;lt;mode&amp;gt;_&amp;lt;config&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="nf"&gt;vadd_vv_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;vs2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                            &lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;vs1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example: SAXPY&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;saxpy_rvv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;vl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vsetvl_e32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Set VL&lt;/span&gt;
        &lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vle32_v_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Load x&lt;/span&gt;
        &lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vle32_v_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Load y&lt;/span&gt;
        &lt;span class="n"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vfmacc_vf_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// y += a*x&lt;/span&gt;
        &lt;span class="n"&gt;vse32_v_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                  &lt;span class="c1"&gt;// Store y&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto-Vectorization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Modern compilers can auto-vectorize:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;add_arrays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GCC with &lt;code&gt;-march=rv64gcv -O3&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generates RVV vector instructions automatically!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Works best with:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple loops&lt;/li&gt;
&lt;li&gt;No dependencies&lt;/li&gt;
&lt;li&gt;Aligned data&lt;/li&gt;
&lt;li&gt;Hint with pragmas if needed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Performance Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Theoretical Speedup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scalar code (1 FP32/cycle):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1000 elements → 1000 cycles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;128-bit RVV (4 FP32/cycle):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1000 elements → 250 cycles (4× speedup)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;256-bit RVV (8 FP32/cycle):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1000 elements → 125 cycles (8× speedup)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;512-bit RVV (16 FP32/cycle):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1000 elements → 63 cycles (16× speedup)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Same binary.&lt;/strong&gt; Different hardware, different throughput.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Benchmarks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Matrix multiplication (GEMM):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;th&gt;Performance (GFLOPS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scalar C&lt;/td&gt;
&lt;td&gt;0.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RVV (128-bit)&lt;/td&gt;
&lt;td&gt;3.2 (4× speedup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RVV (256-bit)&lt;/td&gt;
&lt;td&gt;6.4 (8× speedup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RVV (512-bit)&lt;/td&gt;
&lt;td&gt;12.8 (16× speedup)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Image convolution:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter Size&lt;/th&gt;
&lt;th&gt;Scalar&lt;/th&gt;
&lt;th&gt;RVV 128-bit&lt;/th&gt;
&lt;th&gt;RVV 256-bit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3×3&lt;/td&gt;
&lt;td&gt;45ms&lt;/td&gt;
&lt;td&gt;12ms (3.7×)&lt;/td&gt;
&lt;td&gt;6ms (7.5×)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5×5&lt;/td&gt;
&lt;td&gt;120ms&lt;/td&gt;
&lt;td&gt;32ms (3.75×)&lt;/td&gt;
&lt;td&gt;16ms (7.5×)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Close to theoretical speedup&lt;/strong&gt; with good algorithm design.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hardware Implementations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Commercial Silicon (2025)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Alibaba T-Head:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XuanTie C910: 128-bit RVV 0.7.1&lt;/li&gt;
&lt;li&gt;XuanTie C920: 256-bit RVV 1.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SiFive:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;P670: 256-bit RVV 1.0&lt;/li&gt;
&lt;li&gt;X280: 512-bit RVV 1.0 (HPC-focused)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Andes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AX65: 128-bit RVV 1.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SpacemiT:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;K1: 128-bit RVV 1.0 (8-core, consumer SBC)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  VLEN (Vector Register Length)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Common implementations:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VLEN&lt;/th&gt;
&lt;th&gt;FP32 Elements&lt;/th&gt;
&lt;th&gt;Target Market&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;128-bit&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Embedded, IoT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;256-bit&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;General purpose, edge AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;512-bit&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;HPC, servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1024-bit&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;Supercomputing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;All run the same binaries.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  RVV vs ARM SVE vs x86 AVX
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Code Portability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RVV:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// One code path, works on all VLEN&lt;/span&gt;
&lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vadd_vv_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ARM SVE:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// One code path, works on all SVE lengths&lt;/span&gt;
&lt;span class="n"&gt;svfloat32_t&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;svadd_f32_z&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;x86 AVX:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Different code per width&lt;/span&gt;
&lt;span class="cp"&gt;#ifdef __AVX512F__
&lt;/span&gt;    &lt;span class="n"&gt;__m512&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm512_add_ps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 512-bit&lt;/span&gt;
&lt;span class="cp"&gt;#elif __AVX2__
&lt;/span&gt;    &lt;span class="n"&gt;__m256&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm256_add_ps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 256-bit&lt;/span&gt;
&lt;span class="cp"&gt;#else
&lt;/span&gt;    &lt;span class="n"&gt;__m128&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mm_add_ps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// 128-bit&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Winner:&lt;/strong&gt; RVV and SVE (length-agnostic)&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplicity
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RVV:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple mask model (single mask register v0)&lt;/li&gt;
&lt;li&gt;Straightforward vsetvl configuration&lt;/li&gt;
&lt;li&gt;32 vector registers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SVE:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex predicate registers (p0-p15)&lt;/li&gt;
&lt;li&gt;Governing predicates + first-fault loads&lt;/li&gt;
&lt;li&gt;32 vector registers + 16 predicates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;x86 AVX:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No length abstraction&lt;/li&gt;
&lt;li&gt;Different instruction sets per width&lt;/li&gt;
&lt;li&gt;Mask registers (AVX-512) add complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Winner:&lt;/strong&gt; RVV (simpler model)&lt;/p&gt;

&lt;h3&gt;
  
  
  Ecosystem
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;x86 AVX:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mature compiler support&lt;/li&gt;
&lt;li&gt;Extensive libraries&lt;/li&gt;
&lt;li&gt;Decades of optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ARM SVE:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Growing compiler support&lt;/li&gt;
&lt;li&gt;ARM-specific (vendor lock)&lt;/li&gt;
&lt;li&gt;Limited consumer hardware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;RVV:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compiler support improving rapidly&lt;/li&gt;
&lt;li&gt;Open standard (no vendor lock-in)&lt;/li&gt;
&lt;li&gt;Growing hardware ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Winner:&lt;/strong&gt; x86 (today), RVV (trajectory)&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Length-agnostic is the right model&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One binary, any vector width&lt;/li&gt;
&lt;li&gt;Future-proof code&lt;/li&gt;
&lt;li&gt;Hardware flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Simpler than ARM SVE&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to learn and use&lt;/li&gt;
&lt;li&gt;Straightforward mask model&lt;/li&gt;
&lt;li&gt;Good compiler target&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Open standard advantage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No vendor lock-in&lt;/li&gt;
&lt;li&gt;Custom extensions possible&lt;/li&gt;
&lt;li&gt;Growing ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Not a drop-in x86 replacement (yet)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ecosystem still maturing&lt;/li&gt;
&lt;li&gt;Limited consumer hardware&lt;/li&gt;
&lt;li&gt;But trajectory is strong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Ideal for specialized domains&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edge AI (custom VLEN for models)&lt;/li&gt;
&lt;li&gt;HPC (large VLEN for throughput)&lt;/li&gt;
&lt;li&gt;Embedded (small VLEN for power)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started with RVV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Emulation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;QEMU:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install QEMU with RISC-V support&lt;/span&gt;
qemu-riscv64 &lt;span class="nt"&gt;-cpu&lt;/span&gt; rv64,v&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,vlen&lt;span class="o"&gt;=&lt;/span&gt;256 ./my_rvv_program
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Spike (RISC-V ISA Simulator):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spike &lt;span class="nt"&gt;--isa&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rv64gcv ./my_rvv_program
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Development Boards
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;SpacemiT K1:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;8-core RISC-V&lt;/li&gt;
&lt;li&gt;128-bit RVV 1.0&lt;/li&gt;
&lt;li&gt;Linux support&lt;/li&gt;
&lt;li&gt;~$100&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SiFive HiFive Unmatched:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;U74 cores (no RVV yet)&lt;/li&gt;
&lt;li&gt;Waiting for P670 upgrade&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cross-Compilation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;GCC toolchain:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;riscv64-unknown-linux-gnu-gcc &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-march&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rv64gcv &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-O3&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; program &lt;span class="se"&gt;\&lt;/span&gt;
    program.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Intrinsics example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;riscv_vector.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;vector_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;vl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vsetvl_e32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;va&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vle32_v_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;vb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vle32_v_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;vfloat32m1_t&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vfadd_vv_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;va&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;vse32_v_f32m1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;vc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;RISC-V Vector Extension brings length-agnostic SIMD to the open ISA ecosystem. By learning from x86’s fixed-width mistakes and ARM SVE’s complexity, RVV offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Portable code across any vector width&lt;/li&gt;
&lt;li&gt;Simpler programming model&lt;/li&gt;
&lt;li&gt;Open standard flexibility&lt;/li&gt;
&lt;li&gt;Growing hardware and software ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While still maturing compared to x86 AVX’s decades of optimization, RVV’s trajectory is strong. For edge AI, custom accelerators, and eventually general-purpose computing, RVV represents the future of portable high-performance vector processing.&lt;/p&gt;

&lt;p&gt;The question isn’t if RISC-V vectors will be ubiquitous, but when.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Specifications:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RISC-V Vector Extension 1.0 Specification&lt;/li&gt;
&lt;li&gt;RISC-V ISA Manual (Volume 2: Privileged)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implementations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SiFive P670/X280 documentation&lt;/li&gt;
&lt;li&gt;Alibaba T-Head XuanTie documentation&lt;/li&gt;
&lt;li&gt;Andes AX65 documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GCC RISC-V Vector Intrinsics Guide&lt;/li&gt;
&lt;li&gt;LLVM RISC-V Backend Documentation&lt;/li&gt;
&lt;li&gt;QEMU RISC-V Emulation Guide&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Communities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RISC-V International Vector SIG&lt;/li&gt;
&lt;li&gt;RISC-V Software mailing lists&lt;/li&gt;
&lt;li&gt;RISC-V Exchange forums&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Next in the series: vLLM’s PagedAttention - memory management for LLM serving&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Discussion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What are your thoughts on RISC-V’s approach to vectors?&lt;br&gt;
Have you worked with ARM SVE or x86 AVX?&lt;br&gt;
What applications would benefit most from RVV?&lt;/p&gt;

&lt;p&gt;Share your thoughts &lt;/p&gt;

</description>
      <category>architecture</category>
      <category>simd</category>
      <category>riscv</category>
      <category>vectorprocessing</category>
    </item>
  </channel>
</rss>
