CodeGym /Courses /JAVA 25 SELF /IO performance bottlenecks

IO performance bottlenecks

JAVA 25 SELF
Level 41 , Lesson 0
Available

1. What is a bottleneck in IO

Imagine a supermarket with a single checkout and a long queue of customers. Each customer is your program, and the checkout is the disk or the network you access to read or write data. No matter how fast a customer “runs,” if the checkout is slow, the queue will grow and performance will drop.

In programming, a “bottleneck” is the part of a system that limits the overall speed of an application. For input/output operations (IO, Input/Output), the bottleneck almost always becomes the speed of reading/writing to disk or to the network. Why? Because a modern CPU can perform billions of operations per second, while a disk (especially an HDD) can read and write data thousands—sometimes tens of thousands—of times slower.

Examples of IO bottlenecks

  • Slow opening or reading of large files. If you try to read a huge file “in chunks” in a loop but use a buffer that’s too small or read one byte at a time, the speed will be miserable and the user unhappy.
  • Delays when writing logs. When logging is done synchronously and each message is immediately written to disk, the application can visibly “hang.”
  • Threads blocking on IO. If multiple program threads simultaneously wait for read or write operations to complete, the whole system slows down.

Why is IO slow?

When we work with RAM, everything happens almost instantly, and it’s easy to forget that input/output works very differently. A disk, however modern, is still much slower than RAM: a hard drive lags by roughly thousands of times, and even a snappy modern SSD is slower by hundreds of times. The situation is even worse with the network. If data is not local but on a server or in the cloud, bandwidth and latency start to matter, so access becomes noticeably slower.

There’s also another layer: the operating system itself. Each read or write request goes through drivers, caching, security checks, and permission checks. All these mechanisms are important, but they also add latency. As a result, any IO operation is significantly slower than working with memory, which is why developers value caches, buffering, and asynchronous approaches so much.

2. Typical causes of poor performance

Now let’s figure out which mistakes and unfortunate decisions most often turn IO into a true “bottleneck.”

Frequent access in tiny chunks

The most common beginner mistake is reading or writing a file one byte or character at a time. It’s like going to the store for three kilograms of apples but buying a single apple each time, carrying it home, then returning for the next one, and so on until you have three kilograms. You are technically doing the task, but extremely inefficiently. The same story with files: instead of processing data in larger chunks, the program spends a ton of time on overhead calls.

An “anti-pattern” example:

// Very slow: reading one byte at a time
try (InputStream in = new FileInputStream("bigfile.txt")) {
    int b;
    while ((b = in.read()) != -1) {
        // Processing a single byte
    }
}

Each call to in.read() is a separate disk access. If the file is large, there will be millions of such calls!

Lack of buffering

Buffering means that data is not read/written one byte at a time but grouped into blocks (for example, 4 KB or 8 KB). Without buffering, the load on the disk increases manyfold and performance drops. Java provides ready-made classes for this: BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter.

Synchronous processing of large amounts of data

If you read or write large files on a single thread, the program will wait for the IO operation to finish before continuing. This is especially noticeable in user interfaces (GUI) or server applications where “freezing” is unacceptable.

Single-threaded processing when parallelism is possible

Sometimes you can speed things up by reading or writing several files simultaneously (for example, processing a batch of logs). But if everything is done in one thread, you are not using all the capabilities of the CPU and the disk.

3. How to identify problems

IO performance issues often aren’t obvious while writing code. Everything works... until you try to process a larger file or run the program on a server under real load. That’s why it’s important to be able to find and analyze bottlenecks.

Using profilers

Profilers are special tools that help you “peek” at where your application spends the most time. For Java there are both free and paid tools:

  • VisualVM — ships with the JDK, can build charts and show hot spots.
  • JProfiler — a powerful commercial tool for in-depth analysis.

With a profiler you can see, for example, that the program spends 80% of its time in read() or write(), and draw conclusions.

Logging operation execution time

Sometimes it’s enough to simply measure the execution time of individual operations:

long start = System.currentTimeMillis();
processFile("bigfile.txt");
long end = System.currentTimeMillis();
System.out.println("Processing time: " + (end - start) + " ms");

If processing takes suspiciously long, look for where IO happens. It’s convenient to extract timing into a utility, for example by wrapping calls in a timing method.

Code analysis for inefficient patterns

Watch out for the following red flags:

  • Nested loops that perform file reads or writes inside.
  • Using read() or write() without buffering.
  • Opening and closing a file in every loop iteration.
  • Writing logs synchronously in a hot code path.

An interesting fact

In large projects, teams sometimes create separate “log files for logs” to figure out which part of the code writes to logs most often and slows the system down.

4. Hardware factors

Even if you’ve written perfect code, the hardware can let you down. Let’s see how different types of devices affect IO speed.

SSD vs HDD

  • HDD (hard drive): slow, especially with random data access. It handles sequential reads of large files well but “hesitates” with frequent small operations.
  • SSD (solid-state drive): tens of times faster than HDDs, especially with random access and parallel operations. But even an SSD still lags behind RAM.

Network speed

If files are stored on a network drive or in the cloud, transfer speed depends on network bandwidth and latency, and sometimes on internet congestion. Even if your server is in the next room, a network drive can become the bottleneck.

File system

Different file systems (NTFS, ext4, FAT32, exFAT) handle large files, many small files, and parallel access differently. Sometimes changing the file system gives a performance boost without changing code.

Cache and buffer size

The operating system and disks often use their own caches to speed things up. If the cache is small and the data is large, some operations will bypass the cache and throughput will drop.

5. Practice: comparing file read speed with and without buffering

Let’s run a small experiment to be concrete. We’ll compare two ways of reading a file: one byte at a time and using a buffer.

Reading one byte at a time (slow)

import java.io.FileInputStream;
import java.io.IOException;

public class SlowReadExample {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();

        try (FileInputStream in = new FileInputStream("bigfile.txt")) {
            int b;
            while ((b = in.read()) != -1) {
                // Just read, do nothing
            }
        }

        long end = System.currentTimeMillis();
        System.out.println("Reading one byte at a time: " + (end - start) + " ms");
    }
}

Reading with a buffer (fast)

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class FastReadExample {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();

        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream("bigfile.txt"))) {
            int b;
            while ((b = in.read()) != -1) {
                // Just read, do nothing
            }
        }

        long end = System.currentTimeMillis();
        System.out.println("Reading with a buffer: " + (end - start) + " ms");
    }
}

Result: Even on small files, the difference can be several times; on large ones—tens or hundreds of times! Try it yourself (but prepare some tea—the first option can take a long time).

6. Table: speed comparison

Reading method File size Time (approx.)
One byte at a time 100 MB 30–60 seconds
Buffered (8 KB) 100 MB 1–2 seconds
Buffered (64 KB) 100 MB 0.7–1.5 seconds

The values are approximate, but the order-of-magnitude difference is impressive!

7. Visual diagram: why buffering speeds up IO

flowchart LR
    A[Your code] --> B[Buffer in memory]
    B --> C[Operating system]
    C --> D[File system]
    D --> E[Disk/Network]
  • Without a buffer: each disk access is a separate operation.
  • With a buffer: many operations happen in memory, one operation goes to disk.

8. Common mistakes with IO and performance

Error #1: Reading/writing one byte or character at a time.
This is a classic. Even if the task seems simple, always use buffering (BufferedInputStream, BufferedReader, etc.).

Error #2: Ignoring the execution time of operations.
If you don’t measure how long your code runs, you don’t know where the slowdown is. Targeted measurements via System.currentTimeMillis() or more precise profilers will help.

Error #3: Opening and closing files inside a loop.
Each open/close is expensive. Open a file once, work with it, then close it.

Error #4: Ignoring hardware limitations.
Don’t try to squeeze SSD speeds out of an HDD. Don’t spawn hundreds of threads to work with a single file: the disk can’t keep up.

Error #5: Writing logs synchronously in a hot path.
Logging is IO. If it happens in critical sections, the program will slow down. Consider asynchronous logging and buffering.

1
Task
JAVA 25 SELF, level 41, lesson 0
Locked
Digital Recount of Old Records 💾
Digital Recount of Old Records 💾
1
Task
JAVA 25 SELF, level 41, lesson 0
Locked
Chronicle of Reading Ancient Manuscripts ⏱️
Chronicle of Reading Ancient Manuscripts ⏱️
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION