<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8" -->

---
title: Optimizing Ruby&#x27;s JSON, Part 8 | daily.dev
description: Part 8 of an ongoing series documenting performance improvements to Ruby&#x27;s JSON gem. Since version 2.9.0, the parser is now 2.68x faster and the generator...
canonical: https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Optimizing Ruby&#x27;s JSON, Part 8 | daily.dev
og:description: Part 8 of an ongoing series documenting performance improvements to Ruby&#x27;s JSON gem. Since version 2.9.0, the parser is now 2.68x faster and the generator...
og:url: https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8
og:image: https://api.daily.dev/og/posts/sHvmZrvW8.png
og:image:alt: Optimizing Ruby&#x27;s JSON, Part 8
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.

# Optimizing Ruby's JSON, Part 8

**[RUBYLAND](https://daily.dev/sources/rubyla)** · 20 min read · 1 upvotes · 0 comments

## Summary

Part 8 of an ongoing series documenting performance improvements to Ruby's JSON gem. Since version 2.9.0, the parser is now 2.68x faster and the generator 3.59x faster than the original 2.7.2 baseline on the twitter.json benchmark, with canada.json parsing nearly 10x faster. Key optimizations covered include: packing extra metadata into lookup tables to reduce branching for multi-byte UTF-8 characters in the generator, adding SIMD (SSE2 and NEON) routines for string escaping contributed by an external contributor, and replacing the 1400-line Ragel-generated parser with a clean ~600-line recursive descent parser in C, which also enabled further lookup-table optimizations for whitespace skipping and string scanning.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://byroot.github.io/ruby/json/2026/07/26/optimizing-ruby-json-part-8.html>

## Questions this post answers

### Why did Ruby's json gem replace its Ragel-generated parser with a hand-written recursive descent parser?

The Ragel-generated parser was replaced because it was harder to maintain and optimize than hand-written code, even though it worked. Kevin Newton prototyped a recursive descent version at roughly 200 lines that eventually grew to under 600 lines after refactoring, replacing 1400 lines of Ragel code, and became a better basis for future performance gains despite initially mixed benchmark results (faster on twitter.json and citm_catalog.json, 8% slower on activitypub.json).

_Developers weighing generated parsers against hand-written ones can compare real trade-offs like this on daily.dev._

### How much faster is JSON parsing and generation in the latest Ruby json gem compared to version 2.7.2?

Parsing the twitter.json benchmark is now 2.68x faster and generating it is 3.59x faster than version 2.7.2, while parsing the float-heavy canada.json benchmark is nearly 9.67x faster. These gains came from packing codepoint length and escape flags into a single lookup table byte, adding SIMD-based escaping routines for SSE2 and NEON, and rewriting the parser as a hand-written recursive descent implementation.

_Ruby developers tracking json gem performance gains across versions can follow deep technical breakdowns like this on daily.dev._

### How does SIMD-based string escaping work when generating JSON in C?

SIMD escaping loads 16 bytes at once into a vector register and compares all of them simultaneously against constants like the space character, backslash, and double-quote, producing a bitmask of which bytes need escaping. Ruby's json gem uses SSE2 intrinsics on x86_64 and NEON intrinsics on arm64 for this, trading the branch-heavy scalar approach for amortized per-byte cost across the vector, yielding roughly a 1.24x speedup on the twitter.json benchmark.

_Developers optimizing hot string-processing paths can dig into SIMD techniques like these on daily.dev._

## Similar posts on daily.dev

- [Inside Ruby’s JSON Library: Complete Deep Dive](https://daily.dev/posts/inside-ruby-s-json-library-complete-deep-dive-wqsvaegjk) · Ruby Flow · 0 upvotes · 0 comments
- [How to Parse JSON in Ruby: A Comprehensive Guide 2026](https://daily.dev/posts/how-to-parse-json-in-ruby-a-comprehensive-guide-2026-uzjdoikfx) · RUBYLAND · 1 upvotes · 0 comments
- [Simdjson Shows More Speed-Ups Possible For SIMD In JSON Parsing: Another 30% Boost](https://daily.dev/posts/simdjson-shows-more-speed-ups-possible-for-simd-in-json-parsing-another-30-boost-gm3xesadz) · Phoronix · 0 upvotes · 0 comments
- [Ruby and Rails Performance Roundup: The Backlog Edition](https://daily.dev/posts/ruby-and-rails-performance-roundup-the-backlog-edition-cmpjsw8ud) · RUBYLAND · 0 upvotes · 0 comments

---

Tags: [#performance](https://daily.dev/tags/performance), [#ruby](https://daily.dev/tags/ruby)

[View this post on daily.dev](https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8)

```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":"Optimizing Ruby's JSON, Part 8","url":"https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8"},"datePublished":"2026-07-26T18:59:27.690Z","dateModified":"2026-09-14T08:04:00.268Z","description":"Part 8 of an ongoing series documenting performance improvements to Ruby's JSON gem. Since version 2.9.0, the parser is now 2.68x faster and the generator...","image":"https://media.daily.dev/image/upload/s--foaA6JGU--/f_auto/v1722860399/public/Placeholder%2004","thumbnailUrl":"https://media.daily.dev/image/upload/s--foaA6JGU--/f_auto/v1722860399/public/Placeholder%2004","isAccessibleForFree":true,"articleSection":"RUBYLAND","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":"RUBYLAND","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/rubyla","url":"https://daily.dev/sources/rubyla"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"performance,ruby","timeRequired":"PT20M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"RUBYLAND","item":"https://daily.dev/sources/rubyla"},{"@type":"ListItem","position":3,"name":"Optimizing Ruby's JSON, Part 8"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/optimizing-ruby-s-json-part-8-shvmzrvw8#faq","mainEntity":[{"@type":"Question","name":"Why did Ruby's json gem replace its Ragel-generated parser with a hand-written recursive descent parser?","acceptedAnswer":{"@type":"Answer","text":"The Ragel-generated parser was replaced because it was harder to maintain and optimize than hand-written code, even though it worked. Kevin Newton prototyped a recursive descent version at roughly 200 lines that eventually grew to under 600 lines after refactoring, replacing 1400 lines of Ragel code, and became a better basis for future performance gains despite initially mixed benchmark results (faster on twitter.json and citm_catalog.json, 8% slower on activitypub.json). Developers weighing generated parsers against hand-written ones can compare real trade-offs like this on daily.dev."}},{"@type":"Question","name":"How much faster is JSON parsing and generation in the latest Ruby json gem compared to version 2.7.2?","acceptedAnswer":{"@type":"Answer","text":"Parsing the twitter.json benchmark is now 2.68x faster and generating it is 3.59x faster than version 2.7.2, while parsing the float-heavy canada.json benchmark is nearly 9.67x faster. These gains came from packing codepoint length and escape flags into a single lookup table byte, adding SIMD-based escaping routines for SSE2 and NEON, and rewriting the parser as a hand-written recursive descent implementation. Ruby developers tracking json gem performance gains across versions can follow deep technical breakdowns like this on daily.dev."}},{"@type":"Question","name":"How does SIMD-based string escaping work when generating JSON in C?","acceptedAnswer":{"@type":"Answer","text":"SIMD escaping loads 16 bytes at once into a vector register and compares all of them simultaneously against constants like the space character, backslash, and double-quote, producing a bitmask of which bytes need escaping. Ruby's json gem uses SSE2 intrinsics on x86_64 and NEON intrinsics on arm64 for this, trading the branch-heavy scalar approach for amortized per-byte cost across the vector, yielding roughly a 1.24x speedup on the twitter.json benchmark. Developers optimizing hot string-processing paths can dig into SIMD techniques like these on daily.dev."}}]}
```

