Skip to content

FILE BUFFERING - Understanding and controlling file buffering

Python
#!/usr/bin/env python3
"""
FILE BUFFERING - Understanding and controlling file buffering
Demonstrates different buffering modes and their effects
"""

import os
import tempfile
import time

print("=" * 60)
print("FILE BUFFERING - Controlling I/O Performance")
print("=" * 60)

temp_dir = tempfile.gettempdir()

# Example 1: Default buffering
print("\n1. Default Buffering Behavior")
print("-" * 40)
default_file = os.path.join(temp_dir, "default.txt")

with open(default_file, 'w') as f:
    print(f"  Buffer size: {f.buffer.raw._CHUNK_SIZE if hasattr(f, 'buffer') else 'default'}")
    f.write("Line 1\n")
    print("  Wrote line 1 (buffered)")
    f.write("Line 2\n")
    print("  Wrote line 2 (buffered)")
    # Data written to disk when file closes

print("  File closed, buffer flushed to disk")

# Example 2: Unbuffered writing (binary mode)
print("\n2. Unbuffered Writing (Binary Mode)")
print("-" * 40)
unbuffered_file = os.path.join(temp_dir, "unbuffered.txt")

# buffering=0 only works in binary mode
with open(unbuffered_file, 'wb', buffering=0) as f:
    f.write(b"Line 1\n")
    print("  Wrote line 1 (unbuffered, immediate disk write)")
    f.write(b"Line 2\n")
    print("  Wrote line 2 (unbuffered, immediate disk write)")

# Example 3: Line buffering (text mode)
print("\n3. Line Buffering (Text Mode)")
print("-" * 40)
line_buffered_file = os.path.join(temp_dir, "line_buffered.txt")

# buffering=1 means line buffering for text files
with open(line_buffered_file, 'w', buffering=1) as f:
    f.write("First line\n")
    print("  Wrote first line (flushed due to newline)")
    f.write("Second")
    print("  Wrote partial line (still in buffer)")
    f.write(" line\n")
    print("  Completed line (flushed)")

# Example 4: Custom buffer size
print("\n4. Custom Buffer Size")
print("-" * 40)
custom_buffer_file = os.path.join(temp_dir, "custom_buffer.txt")

buffer_size = 100  # 100 bytes
with open(custom_buffer_file, 'w', buffering=buffer_size) as f:
    print(f"  Using {buffer_size} byte buffer")

    for i in range(20):
        f.write(f"Line {i}\n")

    print("  Wrote 20 lines (may still be in buffer)")
    # Buffer flushed when full or file closes

# Example 5: Manual flushing
print("\n5. Manual Flush Control")
print("-" * 40)
flush_file = os.path.join(temp_dir, "flush.txt")

with open(flush_file, 'w') as f:
    f.write("Before flush\n")
    print("  Wrote data (in buffer)")

    f.flush()
    print("  Called flush() - data written to disk")

    f.write("After flush\n")
    print("  Wrote more data (in buffer again)")

# Example 6: Buffering performance comparison
print("\n6. Buffering Performance Comparison")
print("-" * 40)

# Buffered writing
buffered_file = os.path.join(temp_dir, "perf_buffered.txt")
start = time.time()
with open(buffered_file, 'w') as f:
    for i in range(1000):
        f.write(f"Line {i}\n")
buffered_time = time.time() - start
print(f"  Buffered: {buffered_time:.4f} seconds")

# Unbuffered writing (binary)
unbuffered_file2 = os.path.join(temp_dir, "perf_unbuffered.txt")
start = time.time()
with open(unbuffered_file2, 'wb', buffering=0) as f:
    for i in range(1000):
        f.write(f"Line {i}\n".encode())
unbuffered_time = time.time() - start
print(f"  Unbuffered: {unbuffered_time:.4f} seconds")
print(f"  Speedup: {unbuffered_time / buffered_time:.2f}x faster with buffering")

# Example 7: Reading with different buffer sizes
print("\n7. Reading with Different Buffer Sizes")
print("-" * 40)
read_file = os.path.join(temp_dir, "read_test.txt")

# Create large file
with open(read_file, 'w') as f:
    for i in range(100):
        f.write(f"Line {i}: " + "x" * 50 + "\n")

# Read with small buffer
start = time.time()
with open(read_file, 'r', buffering=512) as f:
    data = f.read()
small_buffer_time = time.time() - start
print(f"  Small buffer (512): {small_buffer_time:.4f}s")

# Read with large buffer
start = time.time()
with open(read_file, 'r', buffering=8192) as f:
    data = f.read()
large_buffer_time = time.time() - start
print(f"  Large buffer (8192): {large_buffer_time:.4f}s")

# Example 8: Buffer and seek operations
print("\n8. Buffer Behavior with Seek")
print("-" * 40)
seek_file = os.path.join(temp_dir, "seek_test.txt")

with open(seek_file, 'w+') as f:
    f.write("ABCDEFGHIJ")
    print("  Wrote: ABCDEFGHIJ")

    # Seek back
    f.seek(0)
    f.write("123")
    print("  Seeked to start, wrote: 123")

    # Flush to see result
    f.flush()

    # Read back
    f.seek(0)
    content = f.read()
    print(f"  Content: {content}")

# Example 9: Detecting buffer status
print("\n9. Checking Buffer Status")
print("-" * 40)
status_file = os.path.join(temp_dir, "status.txt")

with open(status_file, 'w') as f:
    print(f"  File writable: {f.writable()}")
    print(f"  File closed: {f.closed}")

    f.write("Some data")
    print("  Wrote data")

    # Check if seekable (affects buffering)
    print(f"  File seekable: {f.seekable()}")

print(f"  After context - closed: {f.closed}")

# Example 10: Binary vs text buffering
print("\n10. Binary vs Text Mode Buffering")
print("-" * 40)

# Text mode
text_file = os.path.join(temp_dir, "text_mode.txt")
with open(text_file, 'w', buffering=1) as f:
    print(f"  Text mode, line buffering")
    f.write("Line 1\n")
    f.write("Line 2\n")

# Binary mode
binary_file = os.path.join(temp_dir, "binary_mode.txt")
with open(binary_file, 'wb', buffering=1024) as f:
    print(f"  Binary mode, 1024 byte buffer")
    f.write(b"Line 1\n")
    f.write(b"Line 2\n")

# Example 11: Flushing on critical operations
print("\n11. Flushing for Critical Operations")
print("-" * 40)
log_file = os.path.join(temp_dir, "critical.log")

with open(log_file, 'w') as f:
    f.write("Starting critical operation\n")
    f.flush()  # Ensure logged before proceeding
    print("  Log entry flushed")

    # Simulate critical operation
    time.sleep(0.01)

    f.write("Critical operation completed\n")
    f.flush()  # Ensure logged
    print("  Completion logged and flushed")

# Cleanup
cleanup_files = [
    default_file, unbuffered_file, line_buffered_file,
    custom_buffer_file, flush_file, buffered_file,
    unbuffered_file2, read_file, seek_file, status_file,
    text_file, binary_file, log_file
]

for f in cleanup_files:
    if os.path.exists(f):
        os.remove(f)

print("\n" + "=" * 60)
print("Key Points:")
print("  - Default: buffering enabled for performance")
print("  - buffering=0: unbuffered (binary only)")
print("  - buffering=1: line buffering (text mode)")
print("  - buffering=N: custom buffer size")
print("  - flush() forces write to disk")
print("  - Buffering significantly improves performance")
print("=" * 60)