#csv #csv-parser #high-performance #memmap #rust

csv_lib

Library for parsing CSV files using memory-mapped I/O, with low alloc, and AVX2/NEON support

5 releases (stable)

Uses new Rust 2024

new 1.0.3 May 6, 2025
1.0.2 May 5, 2025
1.0.1 May 4, 2025
0.1.0 Apr 28, 2025

#269 in Encoding

Download history 53/week @ 2025-04-22 181/week @ 2025-04-29

234 downloads per month

MIT license

125KB
3K SLoC

πŸ“š Csv Lib v1.0.2

Crates.io Documentation License


A high-performance, zero-copy CSV reader for Rust, optimized for extremely fast parsing using:

  • πŸ”₯[ New ] Supports multithread complete Row process with a clousure,and a editable shared object
  • Memory-mapped files (memmap2)
  • SIMD acceleration (AVX2 on x86_64, NEON on aarch64)
  • memchr3 fallback for broad CPU compatibility
  • Configurable line breaks, delimiters, and string escaping
  • Low memory overhead, even on massive datasets

✨ Features

  • πŸš€ Memory-mapped CSV reading (no BufReader overhead)
  • πŸš€ SIMD acceleration (AVX2, NEON) if available
  • πŸš€ Fallback to memchr3 for full CPU compatibility
  • πŸš€ Per-row zero-copy parsing
  • πŸš€ Per-field parsing using efficient iterators
  • πŸš€ Support for custom delimiters and string escaping
  • πŸš€ Support for column type mapping or auto-detection
  • πŸš€ Optional FFI export for C, C++, Python, C#, and Java between other options
  • πŸš€ Safe cursor management
  • πŸš€ UTF-8, Windows1252 and custom encoding support

πŸ“Š Performance Benchmark – csv_lib Rust Library vs other libraries

We benchmarked the performance of processing 1,000,000,000 CSV rows using several popular Rust libraries. Each result is averaged over 3 independent runs. The test is get &str of each line. In this library we NOT decode the fields, untile you ask for it un the struct field get value, we just get the bytes of the line. For this example we decoded the full row, which is not so efficient.

πŸ§ͺ Implementation 🧡 CPU Usage ⏱️ Average Time
csv crate Single-core 103.272 s
csv-core crate Single-core 66.767 s
csv_lib (sync mode) Single-core 58.963 s βœ…
csv_lib (parallel mode) Multi-core (full) 37.936 s πŸ†

πŸ”₯ Why Choose csv_lib?

  • βœ… Faster than csv and csv-core even in single-threaded mode.
  • 🧡 True parallelism: multi-core support with chunked processing and low synchronization overhead.
  • πŸš€ Built on memmap2 and SIMD (AVX2 / NEON) for fast parsing directly from memory.
  • 🧠 Configurable closures for per-line processing and custom splitting logic.
  • πŸ“ Capable of handling files larger than 13 GB without memory bottlenecks.

πŸ“¦ Best Suited For

  • Massive CSV file ingestion (ETL, logs, data lakes)
  • High-performance backend pipelines
  • Lightweight embedded CLI applications with zero-copy data access

πŸ’‘ csv_lib is optimized for sequential, chunked, and parallel processing using memory-mapped I/O and customizable parsing logic.


βš™οΈ Installation

Put in your terminal

cargo add csv_lib

If you also want FFI support:

cargo add csv_lib --features ffi

In your project folder, at the same level src create a .cargo/config.toml file with the following content:

[build]
rustflags = ["-C", "target-cpu=native"]

Usage:

We use Row and Field struct, to handle the navigation in the document.

  • For a full example project check examples folder.
  • Check the macros here

One Core Example:

pub fn main(){
    //Create Config
    let cfg = CsvConfig::new(
        b',',
        0u8,
        b'\n',
        Encoding::Windows1252,
        false
    );
    //Open the file
    let f = match CsvReaderWithMap::open("data.csv", &cfg) {
        Ok(f) => f,
        Err(e) => panic!("{}", e)
    };
    // We extract different' s country's of the dataset :
    // For example:
    //Create a Hash Acumulator
    let mut cities :HashSet<String>= HashSet::with_capacity(195);
    //Iter over rows
    while let Some(mut row) = f.next_raw() {
        //Extract Field index 6 starting on 0
        let city = row.get_index(6 );
        // Decode bytes as &str
        let name = city.get_utf8_as_str();
        //Check and accumulate
        if !cities.contains(name){
            cities.insert(name.to_string());
        }
    }
}

Multicore Example:

pub fn main(){
    //Create Config
    let cfg = CsvConfig::new(
        b',',
        0u8,
        b'\n',
        Encoding::Windows1252,
        false
    );
    //Open the file
    let f = match CsvReaderWithMap::open("data.csv", &cfg) {
        Ok(f) => f,
        Err(e) => panic!("{}", e)
    };

    //Get Slice Reference
    let data = f.get_slice();
    //Create a shared counter
    let shared = Shared::<i32>::default();
    //Create de clousere executed on each thread (the ARC Mutex type must be the same as Shared)
    let closure = |_: &mut Row<'_>, target: Arc<Mutex<i32>>| {
        //Do some stuff
        // ...
        //Access editable variable.(Use after process due it blocks). Omit this lock uf you can.
        let mut lock = target.lock().unwrap();
        *lock += 1;
    };
    //Execute parallel process
    parallel_processing_csv(
        data,
        b'\n',
        b',',
        0u8,
        false,
        closure,
        shared.arc(),
    );
    println!("Iterated Lines: {}", shared.lock())
}

πŸ“Š CSV Parsing Benchmark Results between different functions of the library

  • Full line decoding: If you decode only needed fields performance will be better.
  • File Size: data_1000000000.txt (approx. 14GB)
  • Test Metric: Time in milliseconds (ms) β€” lower is better
OS Arch CPU/Chipset Type Sync Avg Multi-Core (Lock) Avg Multi-Core Avg
Windows x86_64 i9-12900KF [Desktop] Execution 58,819 ms (58.82 s) 191,619 ms (191.62 s) 39,581 ms (39.58 s)
Windows x86_64 i7-12650H [Notebook] Execution 77,463 ms (77.46 s) 216,394 ms (216.39 s) 52,459 ms (52.46 s)
macOS aarch64 Apple M2 2022 [Notebook] Execution 76,337 ms (76.34 s) 120,968 ms (120.97 s) 73,739 ms (73.74 s)

Changelog

Check it here


πŸ“ˆ Performance Tips

If you are going to test performance of the library, do it in release mode. It have a huge difference due the trash lines of code cargo generates in debug profile, and the time of process is awful

  • Use force_memcach3 = false to take advantage of SIMD (AVX2 or NEON).
  • Match your delimiter, line_break, and string_separator properly to the file format.
  • Prefer UTF-8 / Windows-1252 encodings for maximum parsing speed.
  • Process fields immediately without copying them if possible (&[u8] slices).

The reached performance was possible due this 3 crates


πŸ† Author

Made by Ignacio PΓ©rez Panizza πŸ‡ΊπŸ‡Ύ πŸ§‰

LinkedIn


Dependencies

~250–440KB