CSV performance at scale

csv · Expert · 11 min

Turning a 10-minute import into a 1-minute one usually takes a few simple changes.

The csv module's parser is written in C (_csv), so parsing itself is fast; most time goes into your per-row Python code. Typical speedups:

  • `reader` over `DictReader`: DictReader builds a dict per row and is noticeably slower. On hot paths, read the header once, look up column indexes, and use plain lists.
  • Avoid repeated work inside the loop: precompile regexes, hoist attribute lookups, convert only the columns you need.
  • Batch I/O: writer.writerows(generator) beats calling writerow in a Python loop, and large file buffers (open(..., buffering=1 << 20)) cut syscalls.
  • Batch downstream work: insert into databases in batches (executemany), not row by row.
  • Parallelism: CSV parsing is sequential, but CPU-heavy per-row work can be split across processes in chunks.

Measure first with time.perf_counter or cProfile.

For numerical analysis or files of many gigabytes, columnar tools are in another league: pandas.read_csv (C or pyarrow engine), polars.read_csv/scan_csv (multithreaded, lazy), and DuckDB querying CSV directly. Use the csv module for streaming transformations, small-to-medium files and zero-dependency scripts; reach for those when you need aggregation over hundreds of millions of rows.

Example

import csv, io, time

text = "id,name,qty\n" + "".join(f"{i},user{i},{i % 7}\n" for i in range(300_000))

def timed(label, fn):
    t = time.perf_counter(); result = fn()
    print(f"{label:12} {time.perf_counter() - t:.3f}s  total={result}")

def with_dictreader():
    return sum(int(r["qty"]) for r in csv.DictReader(io.StringIO(text)))

def with_reader():
    rows = csv.reader(io.StringIO(text))
    qty = next(rows).index("qty")
    return sum(int(r[qty]) for r in rows)

timed("DictReader", with_dictreader)   # e.g. DictReader   0.250s  total=899997
timed("reader", with_reader)           # e.g. reader       0.110s  total=899997

Remember

  • Parsing is C-fast; per-row Python work dominates
  • Plain reader with column indexes beats DictReader on hot paths
  • Switch to pandas, polars or DuckDB for huge analytical workloads

Watch out for

  • Optimising the parser when the real cost is one database insert per row.

Check yourself

  1. A CSV import is slow. What usually dominates the time?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/csv.html