<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv" -->

---
title: Manual reification on the JVM | daily.dev
description: A deep technical exploration of implementing a Miller-like CLI data pipeline tool in Java, achieving high-performance AVX-512 code generation from the JVM's...
canonical: https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Manual reification on the JVM | daily.dev
og:description: A deep technical exploration of implementing a Miller-like CLI data pipeline tool in Java, achieving high-performance AVX-512 code generation from the JVM's...
og:url: https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv
og:image: https://api.daily.dev/og/posts/3oqZLzTmv.png
og:image:alt: Manual reification on the JVM
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Manual reification on the JVM

**[Awesome Java Newsletter](https://daily.dev/sources/java_libhunt)** · 37 min read · 8 upvotes · 0 comments

## Summary

A deep technical exploration of implementing a Miller-like CLI data pipeline tool in Java, achieving high-performance AVX-512 code generation from the JVM's JIT compiler without custom bytecode generation. The author introduces 'manual reification' — a technique using annotation processors and runtime class generation (via Cojen Maker) to hoist runtime values into static final fields, enabling the JIT to fully inline, devirtualize, and vectorize code that would otherwise suffer from polymorphic dispatch. Key techniques include: using Object[] register files with constant indices for scalar replacement on GraalVM, cache-fitted batch sizing using L1d topology, runtime speculative specialization for low-cardinality grouped reductions, and whole-pipeline stage fusion. The post benchmarks against Miller (Go) and Polars, showing the warmed-up JVM implementation can surpass both for large datasets, while also honestly discussing JVM startup overhead and the fragility of the fused pipeline approach.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://farnoy.dev/posts/manual-reification-on-the-jvm>

## Questions this post answers

### Why does adding a second interpreter implementation cause Java code that previously vectorized to suddenly lose vectorization?

Introducing a second receiver class at a call site makes it bimorphic (or polymorphic), and even though the JIT can still inline both branches, the resulting control flow prevents loop vectorization. Storing the interpreter tree in a static final field restores constant-receiver dispatch, letting the compiler fully devirtualize and re-enable vectorization, recovering peak performance.

_Developers chasing JIT vectorization regressions can compare notes on call-site polymorphism fixes via daily.dev._

### How can I get scalar replacement of an Object[] array used as a mutable register file in a hot loop on the JVM?

Scalar replacement of an Object[] register file works reliably on GraalVM's Graal JIT (not C2) when indices are constant (annotated as @Constant int slotIx), each slot consistently stores a single type via cast-on-read/write patterns, all slots are initialized before first use, and slots are explicitly zeroed after their final use to avoid unwanted spills to the stack. Forced inlining across all functions touching the array is also required.

_Engineers tuning Graal-based hot loops for register allocation can track these scalar-replacement patterns on daily.dev._

### How much faster is a fused whole-pipeline JIT-compiled implementation compared to a batch-oriented one for vectorized data processing?

Fusing pipeline stages into a single loop that passes data through registers instead of memory made a sample pipeline twice as fast compared to the batch-oriented version, well below the theoretical 5x advantage vector register bandwidth has over L1 cache load bandwidth, because the fused code ended up latency-bound by data dependencies rather than throughput-bound, and estimated headroom from improved instruction-level parallelism was around 3x.

_Anyone weighing pipeline fusion against batch execution for JIT-friendly SIMD code can dig into these tradeoffs on daily.dev._

## Similar posts on daily.dev

- [JVM Workhorses - Stubs and Intrinsics](https://daily.dev/posts/jvm-workhorses---stubs-and-intrinsics-aavy8p5q4) · SoftwareMill · 0 upvotes · 0 comments
- [jank now has its own custom IR](https://daily.dev/posts/jank-now-has-its-own-custom-ir-xb0nzvh5o) · Lobsters · 0 upvotes · 0 comments
- [EP211: How the JVM Works](https://daily.dev/posts/ep211-how-the-jvm-works-e0xrzznoc) · ByteByteGo · 8 upvotes · 1 comments

---

Tags: [#java](https://daily.dev/tags/java), [#jvm](https://daily.dev/tags/jvm), [#compiler-optimization](https://daily.dev/tags/compiler-optimization)

[View this post on daily.dev](https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Manual reification on the JVM","url":"https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv"},"datePublished":"2026-08-05T07:19:45.354Z","dateModified":"2026-09-14T07:29:46.030Z","description":"A deep technical exploration of implementing a Miller-like CLI data pipeline tool in Java, achieving high-performance AVX-512 code generation from the JVM's...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/4d6c6e52c4120f4d65ecd07f78f785e4?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/4d6c6e52c4120f4d65ecd07f78f785e4?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Awesome Java Newsletter","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Awesome Java Newsletter","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/7729929f2ca649d7b13f6b04aa94d300","url":"https://daily.dev/sources/java_libhunt"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":8},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"java,jvm,compiler-optimization","timeRequired":"PT37M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Awesome Java Newsletter","item":"https://daily.dev/sources/java_libhunt"},{"@type":"ListItem","position":3,"name":"Manual reification on the JVM"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/manual-reification-on-the-jvm-3oqzlztmv#faq","mainEntity":[{"@type":"Question","name":"Why does adding a second interpreter implementation cause Java code that previously vectorized to suddenly lose vectorization?","acceptedAnswer":{"@type":"Answer","text":"Introducing a second receiver class at a call site makes it bimorphic (or polymorphic), and even though the JIT can still inline both branches, the resulting control flow prevents loop vectorization. Storing the interpreter tree in a static final field restores constant-receiver dispatch, letting the compiler fully devirtualize and re-enable vectorization, recovering peak performance. Developers chasing JIT vectorization regressions can compare notes on call-site polymorphism fixes via daily.dev."}},{"@type":"Question","name":"How can I get scalar replacement of an Object[] array used as a mutable register file in a hot loop on the JVM?","acceptedAnswer":{"@type":"Answer","text":"Scalar replacement of an Object[] register file works reliably on GraalVM's Graal JIT (not C2) when indices are constant (annotated as @Constant int slotIx), each slot consistently stores a single type via cast-on-read/write patterns, all slots are initialized before first use, and slots are explicitly zeroed after their final use to avoid unwanted spills to the stack. Forced inlining across all functions touching the array is also required. Engineers tuning Graal-based hot loops for register allocation can track these scalar-replacement patterns on daily.dev."}},{"@type":"Question","name":"How much faster is a fused whole-pipeline JIT-compiled implementation compared to a batch-oriented one for vectorized data processing?","acceptedAnswer":{"@type":"Answer","text":"Fusing pipeline stages into a single loop that passes data through registers instead of memory made a sample pipeline twice as fast compared to the batch-oriented version, well below the theoretical 5x advantage vector register bandwidth has over L1 cache load bandwidth, because the fused code ended up latency-bound by data dependencies rather than throughput-bound, and estimated headroom from improved instruction-level parallelism was around 3x. Anyone weighing pipeline fusion against batch execution for JIT-friendly SIMD code can dig into these tradeoffs on daily.dev."}}]}
```

