惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
IT之家
IT之家
T
The Blog of Author Tim Ferriss
V
V2EX
博客园 - 聂微东
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
U
Unit 42
Vercel News
Vercel News
L
LangChain Blog
博客园 - 司徒正美
H
Help Net Security
Recent Announcements
Recent Announcements
Recorded Future
Recorded Future
V
Visual Studio Blog
Jina AI
Jina AI
Microsoft Azure Blog
Microsoft Azure Blog
GbyAI
GbyAI
Y
Y Combinator Blog
C
Check Point Blog
博客园 - 三生石上(FineUI控件)
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks
The Register - Security
The Register - Security
The GitHub Blog
The GitHub Blog
B
Blog RSS Feed
F
Fortinet All Blogs
B
Blog
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
云风的 BLOG
云风的 BLOG
爱范儿
爱范儿
MongoDB | Blog
MongoDB | Blog
F
Full Disclosure
有赞技术团队
有赞技术团队
罗磊的独立博客
博客园_首页
MyScale Blog
MyScale Blog
aimingoo的专栏
aimingoo的专栏
Google DeepMind News
Google DeepMind News
M
MIT News - Artificial intelligence
N
Netflix TechBlog - Medium
Engineering at Meta
Engineering at Meta
量子位
I
InfoQ
小众软件
小众软件
P
Proofpoint News Feed

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
From Strings to Silicon: How I Optimized a Java Word Count by 7X 🚀
elias mohamm · 2026-05-20 · via DEV Community

Problem and Motivation

After years away, I decided to return to my favorite programming language: Java. My main goal was to deepen my understanding of concurrency and threading at the operating system level — especially to see how concepts like goroutines in Go or coroutines in Kotlin work under the hood and how they can be improved.

To start my journey, I picked up the book Operating Systems: Three Easy Pieces. After reading through the key sections, I felt it was time to put these concepts to the test with some practical coding. Instead of building a basic counter (which felt too trivial), I searched for a more realistic and challenging problem.

That’s when I came across the 1 Billion Row Challenge on YouTube. It inspired me to create my own version: counting the occurrence of 200 million words in a text file.

Why This Challenge is Exciting 🤔

  1. There’s an obvious solution to get you started quickly.

  2. The file size—about 1.5GB—isn’t impossible to handle, but it’s large enough to see real performance impacts as the code improves.

  3. You can apply advanced concepts like multithreading and parallel processing.

  4. You’ll get hands-on experience with Java’s nio package, Streams, and efficient file reading techniques.

Step 1: The Proof of Concept (POC) 🛠️

Before diving into complex optimizations, the first step is to implement a robust Proof of Concept (POC). This provides a baseline, allowing me to measure performance and verify the accuracy of the output after each iteration of improvement.

The initial implementation strategy is straightforward:

  1. Read all lines from the file.
  2. Split strings using the space delimiter.
  3. Aggregate word counts using a HashMap<String, Int>.
    Path dir = Path.of("./src");  
    Path file = dir.resolve("words.txt");
    List<String> lines = Files.readAllLines(file);

    for (String line : lines) {  
            calc(line);  
        }

Enter fullscreen mode Exit fullscreen mode

void calc(String line) {  
    if (line == null) return;  
    String[] parts = line.trim().split(" ");  
    for (String part : parts) {  
        if (this.wordsMap.containsKey(part) ) {  
            this.wordsMap.put(part, this.wordsMap.get(part) + 1);  
        } else {  
            this.wordsMap.put(part, 1);  
        }  
    }  
}

Enter fullscreen mode Exit fullscreen mode

in this step I learned : how to work with nio package (File, Path). it is very simple and like NodeJS fs module.

The performance result was:

Scenario Time(s) Memory(Kb)
readAllLines 7.6 1829121

so I decided to use Stream, to learn it and how will affects on performance

try (Stream<String> lines = Files.lines(file)) {  
           lines.forEach(wordCounter::calc);  
}

Enter fullscreen mode Exit fullscreen mode

Stream doesn't load whole of files, therefore consuming memory reduced incredibly.

Scenario Time(s) Memory(Kb)
Stream 7.2 661

Step 2: Scaling with Concurrency: Applying OS Fundamentals 🧵

Now, it’s time to apply the core concepts from Operating Systems: Three Easy Pieces: Implementing Multithreading.

To improve performance, I decided to partition the workload across a fixed pool of 8 threads. Each thread is responsible for a specific segment of the data, performing its calculations independently before merging the final results. This is a classic “Thread-local” or “Divide and Conquer” strategy.

The “Shared-Nothing” Advantage

In this approach, each thread operates within its own boundaries. By using thread-local aggregation, we achieve several key benefits:

  • No Contention: We avoid locking shared memory during the heavy lifting.
  • Isolation: Each sub-task is isolated, preventing synchronization overhead.
  • CPU Efficiency: By reducing the need for locks, the process becomes truly CPU-bound rather than stalling on memory barriers or I/O waits.

The goal here was to maximize CPU utilization while keeping the implementation clean and scalable. 🚀

// Create 8 Task for 8 Threads
List<WordCounterTask> tasks = new ArrayList<>();  
int PARTITION_SIZE = 8; // near to available Thread  
for (int i = 0; i < PARTITION_SIZE; i++) {  

    int from = index * i;  
    int to = from + index;  
    if (i == PARTITION_SIZE - 1) {  
        to = lines.size();  
    }  
    List<String> subListLines = lines.subList(from, to);  
    WordCounterTask task = new WordCounterTask(subListLines);  
    tasks.add(task);  
}

// Starting 8 Thread
for (int i = 0; i < PARTITION_SIZE; i++) {  
    Thread t = new Thread(tasks.get(i));  
    threads.add(t);  
    t.start();  
}  
for (Thread t : threads) {  
    t.join();  
}

// Aggergate the results in to a HashMap<String, Integer>
HashMap<String, Integer> results = new HashMap<>();  
tasks.forEach(task -> {  
    task.getWordsMap().forEach((key, value) -> {  
        if(results.containsKey(key)) {  
            results.put(key, results.get(key) + value);  
        } else {  
            results.put(key, value);  
        }  
    });  
});

Enter fullscreen mode Exit fullscreen mode

and the result was which I expect: reducing the calculation time

Scenario Time(s) Memory(Kb)
Multi-Thread + Merging result(Avoiding Lock) 2.315 1829121

Step 3: Eliminating split() and Regex — Reducing String Overhead 🚀

The next optimization target was string allocation overhead.

In the previous implementations, I relied on String.split() (which internally uses regex) and stored String objects as keys in a HashMap. While convenient, this approach is expensive:

  • split() uses regular expressions, which add unnecessary overhead.
  • Each word becomes a new String object.
  • Each String allocates a char[] internally.
  • Massive GC pressure is created when processing hundreds of millions of words.

For a 1.5GB file with ~200 million words, this becomes a serious bottleneck.

Before

for (String line : this.lines) {  
    String[] parts = line.trim().split(" ");// Heavy operation in hot path loop
    for (String part : parts) {  
        if (this.wordsMap.containsKey(part)) {  
            this.wordsMap.put(part, this.wordsMap.get(part) + 1);  
        } else {  
            this.wordsMap.put(part, 1);  
        }  
    }  
}

Enter fullscreen mode Exit fullscreen mode

After

StringBuilder key = new StringBuilder();  
for (int i = 0; i < line.length(); i++) {  
    // O(n)  
    if (line.charAt(i) == ' ' && !key.isEmpty()) {  
        updateWords(key.toString());  
        key.setLength(0);  
        continue;  
    }  
    key.append(line.charAt(i));  
}  
if (!key.isEmpty()) {  
    updateWords(key.toString());  
}

void updateWords(String key) {  
    // in the first solution I used containsKey + get  
    // it is 2 lookups -> 1 for containsKey and 1 for get --> slow    
    // The fasted way is as below    Integer value = wordsMap.get(key);  
    if (value != null) {  
        this.wordsMap.put(key, value + 1);  
    } else {  
        this.wordsMap.put(key, 1);  
    }  
}

Enter fullscreen mode Exit fullscreen mode

and the result:

Scenario Time(s)
string + 8 thread 2.682
stringBuilder + no split() + 8 thread 1.4 (1.4 ~ 1.7)

Step 4: Going String-less — Optimizing for the Hardware 🏎️

Even StringBuilder—while efficient—still contributes to object churn. To truly unlock performance, I set a new goal: a zero-string allocation solution.

This journey pushed me into the weeds of systems architecture, forcing me to grapple with:

  • Bitwise Operations: Understanding how data is stored at the lowest level.
  • SIMD & SWAR (SIMD Within A Register): Leveraging CPU-level parallelism.
  • Cache Locality: Optimizing for L1/L2 cache hits to keep the CPU fed.
  • Instruction Pipelines: How the CPU executes code and why “branchy” code kills performance.

The realization: In performance-critical Java, if you are creating objects, you are losing.


Shifting from Strings to Bytes ⚡

Instead of relying on the high-level String abstraction, I pivoted to processing raw byte[] buffers.

By treating the input as a stream of bytes, I can:

  1. Reduce GC Pressure: No more transient object allocations for every word.
  2. Use Memory Directly: Accessing bytes directly from a MappedByteBuffer allows for zero-copy-like mechanics.
  3. Optimize Parsing: Identifying delimiters (like spaces or newlines) becomes a simple comparison of byte values rather than a heavy regex invocation.
byte[] bufferRef = line.getBytes()

Enter fullscreen mode Exit fullscreen mode

Each word is represented by a range in the underlying buffer. For example, the word “apple” may occupy bytes from index 0 to 5. Since I’m using a thread-local approach, I can build a hash directly from the buffer reference, the word’s starting offset, and its length.

With this approach, I no longer need to convert the bytes into a String or compare characters one by one. Instead, I compare raw bytes, which is significantly cheaper. I also generate a long hash value, which is much more efficient than using a String as the key.

class Word {  
    public byte[] bufferRef;  
    public int start;  
    public int end;  
    private final int hash;  
    public int length;  
    public Word(byte[] buffer, int start, int end) {  
       //......
    }  

    static int computeHash(byte[] buffer, int start, int end) {  
        int hash = 1;  
        for (int i = start; i < end; i++) {  
            hash = 31 * hash + buffer[i];  
        }  
        return hash;  
    }  

    @Override  
    public int hashCode() {  
        return hash;  
    }  

    @Override  
    public boolean equals(Object obj) {  
        if (obj == null) return false;  
        if (obj == this) return true;  
        if (obj.getClass() != this.getClass()) return false;  
        Word other = (Word) obj;  
        if (this.length != other.length) return false;  
        for (int i = 0; i < this.length; i++) {  
            if (this.bufferRef[i + start] != other.bufferRef[i + other.start]) {
                return false;
            }  
        }  
        return true;  
    }  

    @Override  
    public String toString() {  
        StringBuilder sb = new StringBuilder();  
        for (int i = start; i < end; i++) {  
            sb.append((char)bufferRef[i]);  
        }  
        return sb.toString();  
    }  
}

Enter fullscreen mode Exit fullscreen mode

and the calculation method changed to:

byte[] bufferRef = line.getBytes();  
int start = 0;  
for (int i = 0; i < bufferRef.length; i++) {  
    if (bufferRef[i] == 0x20) {  
        Word word = new Word(bufferRef, start, i);  
        wordsMap.merge(word, 1, Integer::sum);  
        start = i+1;  
    }  
}  
if ((bufferRef.length) - start > 0) {  
    Word word = new Word(bufferRef, start, bufferRef.length);  
    wordsMap.merge(word, 1, Integer::sum);  
}

Enter fullscreen mode Exit fullscreen mode

As you can see the sting operation is gone, and results:

Scenario Time(s)
string less (bytes + offset + length) as hash 1.1~1.2

Amazing 🤩

Step 5: Optimizing Memory Usage with MappedByteBuffer 🧩

One major challenge in handling huge files is keeping memory consumption under control.

What Is MappedByteBuffer?

A MappedByteBuffer is a specialized subclass of ByteBuffer. It allows you to map a region of a file directly into the operating system’s virtual memory space—enabling zero-copy file access and letting the OS handle paging behind the scenes. This dramatically reduces heap usage in your Java application.


Memory-Efficient File Splitting

Instead of splitting the file by lines, I segmented the file based on its byte size. For example, with 8 threads, the file is divided into 8 regions (one per thread). Each thread processes its own file segment independently using a dedicated MappedByteBuffer.

However, segmenting by size introduces a practical problem: lines may be split across chunks. To ensure data integrity, I expanded each chunk slightly as needed so that no line was cut in half. While that means the buckets might not be perfectly equal in size, each thread still gets a clean, processable region of data.

try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {  

    long fileSize = channel.size(); // ~1.5GB  
    long chunks = fileSize / PARTITION_SIZE;  
    long from = 0;  
    for (int i = 0; i < PARTITION_SIZE; i++) {  
        long to = findNewLinePosition(channel, from + chunks);  
        if (i == PARTITION_SIZE - 1) {  
            to = fileSize;  
        }  

        System.out.println(from + " -> " + to);  
        long chunkSize = to - from;  
        MappedByteBuffer buffer = channel.map(  
                FileChannel.MapMode.READ_ONLY, from, chunkSize  
        );  

        tasks.add(new OptimizedWordCounterTask3(buffer, chunkSize));  
        from = to;  
    }  
}

Enter fullscreen mode Exit fullscreen mode

And calculation method:

    int start = 0;  
    for (int i = 0; i < this.size; i++) {  
        byte b = this.mbb.get(i);  // Stream buffers
        if (b == 0x20 || b == '\n') {  
            if (i > start) {  
                WordMappedByteBuffer word = new WordMappedByteBuffer(this.mbb, start, i);  
                wordsMap.merge(word, 1, Integer::sum);  

            }  
            start = i + 1;  
        }  
    }  

    if (this.size - start > 0) {  
        WordMappedByteBuffer word = new WordMappedByteBuffer(this.mbb, start, (int)size);  
        wordsMap.merge(word, 1, Integer::sum);  
    }  

Enter fullscreen mode Exit fullscreen mode

The result for memory consumption is huge:

Scenario Time(s) Memory(KB)
MappedBufferByte 1.1~1.2 900~920

Conclusion

My tests were on this Machine: Macbook Pro M4, CPU: 10 core, RAM: 16GB
and the results are:

Scenario Time(s) Memory(KB)
readAllLines 7.6 1829121
string + 8 thread 2.682 1829121
stringBuilder + 8 thread 1.4 (1.4 ~ 1.7) 1829121
string less (bytes + offset + length) as hash 1.1~1.2 1829121
MappedBufferByte 1.1~1.2 900~920

What I Really Gained From This Practice 🎓

The most valuable result is what I learned along the way.

Through this single exercise, I deepened my understanding of:

  • Concurrency and multithreading – how to design thread-local workloads, avoid contention, and think in terms of CPU cores instead of just “loops.”
  • Java NIO – using FileChannel, ByteBuffer, and especially MappedByteBuffer to work efficiently with large files.
  • Low-level performance concepts – bytes vs strings, bitwise operations, SIMD/SWAR, and how modern CPUs actually execute and optimize instructions.
  • Cache behavior (L1/L2) – why memory layout and access patterns matter as much as the algorithm itself.
  • Senior-engineer mindset – how experienced engineers approach high-performance problems: measure first, build a baseline, remove abstractions layer by layer, and align the code with how the hardware works.

All of this came from just one practice problem. The performance gains were great—but the learning was even better. 🚀

Next

I don't understand the SWAR concept yet and how can to implement it directly. so, the next steps for me are:

  1. Learning SWAR
  2. Learning about Vector API in Java (Real SIMD)
  3. How these concept can improve the performance.

Please share your idea with me!

Also the Github repo for this project is here, you can read it and learn from it, also contribute in it with your solutions.

Find Me Here

LinkedIn: https://www.linkedin.com/in/eliasmohammadi
Github: https://www.github.com/eliasmohammadi
Devto: https://dev.to/elias_mohammadi