CodeGym /Courses /C# SELF /Data buffering principle

Data buffering principle

C# SELF
Level 41 , Lesson 1
Available

1. Introduction

Imagine you're writing a letter with a pencil, but you only have a tiny piece of an eraser that’s enough for one word at a time. Until you erase you can't continue. You'd prefer to erase more at once, right? Buffering is kinda like that "multi-eraser": it lets you work with bigger chunks of data at once instead of bit by bit.

In programming, buffering is temporary storage of data in memory (in a "buffer") before a read or write to disk happens. It's like a laundry basket: you throw socks in during the week and wash them all together, not one sock at a time. That saves time (and resources!).

I/O operations

Accessing a hard drive, SSD, or flash is one of the slowest operations for the CPU. RAM is roughly a thousand times faster! So if every Write or Read call went straight to disk, your app would crawl like Windows XP on an old laptop with 512 MB of RAM.

Buffering exists to reduce the number of actual physical disk accesses and boost performance.

2. How buffering works for input and output

Buffer is just a chunk of RAM where data is temporarily placed. Here's how it works:

When writing a file:

  • Your code calls Write() several times.
  • All the data is first accumulated in the buffer.
  • When the buffer fills or the operation needs to finish, the buffer's contents are written to disk in one big chunk.

When reading a file:

  • You ask to read a small amount of data.
  • The system reads a large chunk from the file and places it into the buffer.
  • When you call again, the data is already in the buffer and no disk access is needed.

Result:

  • Fewer disk accesses.
  • Reads and writes are faster.

3. Buffering in .NET: where it's applied

In .NET most I/O streams use buffering by default:

  • StreamWriter / StreamReader
  • FileStream
  • BufferedStream
  • Even Console.Out!

But the buffer size and its usage can (and often should) be configured.

Why this matters?

When you write or read large amounts of data (log files, databases, media processing) — properly configured buffering can speed your program up by orders of magnitude. Without buffering, even a good CPU starts "yawning" waiting for data, like a cat in the rain.

4. Simple example without buffering

First let's look how writing a file would look if we wrote each byte separately (don't do this!):

string path = "slowfile.txt";
using (FileStream fs = new FileStream(path, FileMode.Create))
{
    for (int i = 0; i < 100000; i++)
    {
        fs.WriteByte((byte)'A'); // Writing 1 byte at a time!
    }
}
Console.WriteLine("Done! (but very slow)");

This example causes 100,000 real disk accesses! Even an SSD will be like "why are you doing this to me?.."

What buffer size to choose?

It depends on your task:

  • By default .NET often uses 4 KB or 8 KB for internal buffering.
  • For large files (100 MB and up) you can safely use buffers of 16 KB, 64 KB or even 1 MB.
  • Too large a buffer is also bad: it wastes memory and sometimes gives no benefit.

Golden rule: measure (profile), don't guess! Sometimes increasing the buffer speeds things up 10x, sometimes it hardly affects anything.

5. Buffering: speeding up I/O

The word "buffering" in file context is basically "buying in bulk". We don't carry bananas one by one; we take a whole crate.

In .NET almost all I/O streams use buffering by default, but there are exceptions: when you explicitly manage FileStream and its parameters, or when you work in "unrealistic" conditions (e.g., very small buffer or none).

How does buffering speed up I/O?

When you read or write a big block of data, the OS can optimize: coalesce multiple operations into one, reduce the number of disk accesses, and prefetch the next chunk into memory.

Illustration: Reading a file — without buffer and with buffer

Variant Number of accesses Time, roughly
Read 1 byte at a time 10,000,000 10 minutes
Read 4096 bytes at a time 2,500 5 seconds

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

6. FileStream and buffering in .NET

The FileStream class is the lowest-level tool for file work that gives maximum control but requires care. It has a constructor that lets you configure the buffer size:

// FileMode.Open: open existing file
// FileAccess.Read: read
// FileShare.Read: allow others to read
// bufferSize: buffer size in bytes
var fs = new FileStream("bigfile.txt", FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 8192)

    // Work with the file faster

By default FileStream uses a 4096-byte buffer, but you can set a bigger value if the file is large (for example, 16 KB, 64 KB or even 1 MB).

Tip: don't set the buffer too large

If the buffer is huge, you'll waste a lot of RAM and won't get a speed win — modern OSes already cache blocks. An optimal buffer is from 4 KB to 128 KB for most everyday tasks.

When performance issues show up especially bad?

  • Copying a large number of small files (e.g., photos).
  • Reading big files in tiny chunks (1 byte, or line-by-line without buffering).
  • Opening many files simultaneously (e.g., a script searching text across all logs on disk).
  • Working with network shares (latency + network overload).
  • Mass operations: archiving, backups, import/export tasks.

7. Copying a file "old way" and "fast way"

Let's compare approaches that in real life affect program speed.

Very slow:

// ❌ Bad — read and write one byte at a time
using FileStream source = new FileStream("source.bin", FileMode.Open);
using FileStream dest = new FileStream("dest.bin", FileMode.Create);

int b;
while ((b = source.ReadByte()) != -1)
{
    dest.WriteByte((byte)b);
}

Much faster:

// ✅ Good — read and write in big blocks
byte[] buffer = new byte[16 * 1024]; // 16 KB
int bytesRead;

using FileStream source = new FileStream("source.bin", FileMode.Open);
using FileStream dest = new FileStream("dest.bin", FileMode.Create);

while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
{
    dest.Write(buffer, 0, bytesRead);
}

Super-fast (and simple):

// 🚀 File.Copy — uses optimized buffering under the hood
File.Copy("source.bin", "dest.bin");

Why bother with manual blocks? Because sometimes you need to process file contents on the fly (e.g., filter lines, encrypt data, compute sums).

Runtime comparison

To make the experiment obvious, here's a table (approximate numbers that illustrate the order of differences):

Method File size 1GB Time (Approx)
1 byte at a time 1GB ~30 minutes
4 KB blocks 1GB ~20 seconds
Built-in File.Copy 1GB ~5 seconds

Don't run this test on important files or your system SSD — you might get a "truce agreement" between your disk and your nerves.

8. Useful nuances

Where else do "slowdowns" come from?

Besides disk physics and a poorly chosen block size, there are other reasons your program can be slow:

  • Opening and closing files "on the fly" (better open once, work, then close).
  • Doing I/O on the main app thread (it freezes the UI in Windows Forms/WPF/MAUI).
  • Insufficient memory: the OS starts swapping file pages between RAM and disk — double slowdown.
  • Antivirus, Windows search indexers, background processes — they sometimes "grab" your file and slow things down invisibly.

Practical use

In a real project: if you build software for processing files (logs, media, documents), cloud storage services, report aggregators, backups — you'll 100% face the question "how to make fast I/O?". Using buffering, large blocks and built-in tools like File.Copy is the basics of efficient file handling.

In an interview: you might be asked "Explain why reading a file one byte at a time is an antipattern?" Or "how to speed up bulk file copying." Having experience and knowledge about buffering helps you answer confidently, give examples and propose solutions.

At work: sometimes everything worked fast and then suddenly "slows down" after switching from SSD to a network drive, or after an OS update. Knowing how I/O works helps you find the cause and suggest optimizations quickly.

How to speed up I/O: useful tips

  • Always use buffered I/O (BufferedStream, buffer configuration in FileStream).
  • Read and write in large blocks (from 4 KB and up).
  • Minimize open/close cycles — open once, use it, then close.
  • Use async methods when possible (ReadAsync, WriteAsync) — they don't make I/O itself faster, but they let your app not block waiting for completion.
  • If working with very large files — learn about Memory<T>, Span<T>.
  • Trust built-in functions: File.Copy, File.Move, etc. — under the hood they use the fastest system calls available.

Buffering in .NET classes

Let's look at a small table — who buffers data and how:

Class Buffered by default Configurable buffer
FileStream
Yes Yes (constructor)
StreamWriter
Yes Yes (via constructor)
StreamReader
Yes Yes
BufferedStream
No (just a wrapper) Yes
BinaryWriter/Reader
Yes No

Almost nothing in .NET works without a buffer — because it's inefficient.

When you need to manually "flush" the buffer

Sometimes data stays in the buffer and you want it written to disk right now. For example, you're writing a log and the program crashes. What to do?

In such cases call .Flush():

using var fs = new FileStream("log.txt", FileMode.Append);
using var writer = new StreamWriter(fs);
writer.WriteLine("Something important");
writer.Flush(); // Flush the buffer to disk right now

Flush is like shouting "Okay, put everything away, enough mess!". All unsaved data will be actually written.

9. Practical questions: common mistakes and nuances

One of the most common disappointments for beginners: "I wrote to the file, but it's empty!" The reason is that the data hasn't been "flushed" from the buffer yet. The program buffers heavily and doesn't always write to disk immediately. Avoid this by calling Flush() or closing the stream (Dispose()).

Another issue: you opened a big file for writing and allocated a gigantic buffer, but the system has little memory — the program starts to "lag". Too big a buffer isn't always good, so don't overdo it.

2
Task
C# SELF, level 41, lesson 1
Locked
Copying a file in large blocks
Copying a file in large blocks
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION