RUNLOCALAIv38
->Will it run?Best GPUCompareTroubleshootStartLearnPulseModelsHardwareToolsBench
Run check
RUNLOCALAI

Independently operated catalog for local-AI hardware and software. Hand-written verdicts. Source-cited claims. Reproducible commands when we have them.

OP·Fredoline Eruo
DIR
  • Models
  • Hardware
  • Tools
  • Benchmarks
TOOLS
  • Will it run?
  • Compare hardware
  • Cost vs cloud
  • Choose my GPU
  • Prompting kits
  • Quick answers
REF
  • All buyer guides
  • Learn local AI
  • Methodology
  • Glossary
  • Errors KB
  • Trust
EDITOR
  • About
  • Author
  • How we make money
  • Editorial policy
  • Contact
LEGAL
  • Privacy
  • Terms
  • Sitemap
MAIL · MONTHLY DIGEST
Get monthly local AI changes
Monthly recap. No spam.
DISCLOSURE

Some links on this site are affiliate links (Amazon Associates and other first-class retailers). When you buy through them, we earn a small commission at no extra cost to you. Affiliate links do not influence our verdicts — there are cards we rate highly that we don't have affiliate relationships with, and cards that sell well that we refuse to recommend. Read more →

© 2026 runlocalai.coIndependently operated
RUNLOCALAI · v38
  1. >
  2. Home
  3. /Learn
  4. /Courses
  5. /Document Processing with Local AI
  6. /Ch. 11
Document Processing with Local AI

11. Batch Processing Architecture

Chapter 11 of 18 · 20 min
KEY INSIGHT

Batch processing architecture balances concurrency against memory constraints. Start with worker pools, add progress tracking for visibility, and implement chunked processing when memory limits emerge.

Processing one document is straightforward. Processing one thousand requires architectural decisions about concurrency, memory management, and failure recovery. This chapter covers patterns for scaling document processing reliably.

The Batch Processing Challenge

Each document consumes memory during processing. A naive loop loads all documents simultaneously, causing memory exhaustion on large batches. Processing documents sequentially wastes available CPU capacity on I/O-bound operations like disk reads.

Worker Pool Pattern

Distribute work across multiple processes using concurrent.futures:

from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path

def process_document(filepath):
    from your_processor import process_single
    return process_single(filepath)

def batch_process(document_paths, max_workers=4):
    results = []
    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(process_document, p): p for p in document_paths}
        for future in as_completed(futures):
            filepath = futures[future]
            try:
                result = future.result()
                results.append({"path": filepath, "status": "success", "data": result})
            except Exception as e:
                results.append({"path": filepath, "status": "error", "error": str(e)})
    return results

ProcessPoolExecutor spawns worker processes equal to max_workers. The operating system schedules these across CPU cores automatically.

Memory Management with Generators

For very large batches, use generators to avoid loading all paths into memory:

def document_paths(directory):
    for path in Path(directory).rglob("*.pdf"):
        yield path

for result in batch_process(document_paths("large-batch")):
    print(f"Processed: {result['path']}")

Progress Tracking

Long-running batches need progress visibility:

from tqdm import tqdm

def batch_process_with_progress(document_paths, max_workers=4):
    total = len(document_paths)
    completed = 0
    errors = []
    
    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(process_document, p): p for p in document_paths}
        for future in tqdm(as_completed(futures), total=total, desc="Processing"):
            filepath = futures[future]
            try:
                future.result()
            except Exception as e:
                errors.append({"path": filepath, "error": str(e)})
            completed += 1
    
    return {"completed": completed - len(errors), "errors": errors}

Chunk-Based Processing

For datasets too large to fit in memory, split into chunks:

def chunk_process(documents, chunk_size=100):
    for i in range(0, len(documents), chunk_size):
        chunk = documents[i:i + chunk_size]
        results = batch_process(chunk)
        yield results
        clear_memory_results(results)
EXERCISE

Modify the batch processor to support configurable concurrency levels, display estimated time remaining based on processing rate, and write a summary report to disk upon completion.

← Chapter 10
Table Extraction
Chapter 12 →
Watch-Folder Automation