stringrs: Fast Regex Matching with Rust

High-performance regex matching for R using Rust via extendr. Compares multiple implementations including base R, parallel R (mirai/furrr), and Rust (regex and fancy-regex engines).

Features

Installation

# Install Rust first: https://rustup.rs/
# Then install the package
devtools::install()

Quick Start

library(stringrs)

# Simple detection with smart defaults
strings <- c("apple pie", "banana bread", "cherry tart")
pattern <- "a[aeiou]"
string_detect(strings, pattern)

# Multiple patterns - returns wide tibble
patterns <- c("a[aeiou]", "[bt]read", "^ch")
result <- string_detect(strings, patterns)
# Returns: tibble with columns: string, a.aeiou., X.bt.read., X.ch

# Fixed (literal) matching - bypasses regex for speed
strings <- c("Hello world", "hello there", "HELLO")
string_detect(strings, "hello", fixed = TRUE)  # case-sensitive literal match

API Reference

string_detect()

string_detect(
    strings,           # Character vector of strings to search
    pattern,           # Character vector of patterns (single or multiple)
    fixed = FALSE      # Use literal matching (bypasses regex)
)

Parameters:

Returns:

Auto-optimization (all transparent):

Benchmarking

Run Benchmarks

# Quick test (reduced test cases, single iteration)
Rscript benchmark/run.R --quick

# Full benchmark suite (19 scenarios)
Rscript benchmark/run.R

# Custom workers and iterations
Rscript benchmark/run.R --workers 8 --iterations 5

Compare to Historical Baseline

# Include comparison to previous runs
Rscript benchmark/run.R --compare

Test Configurations

The benchmark suite tests various scales:

Test Strings Patterns Description
1Kx1 1,000 1 Small single pattern
10Kx1 10,000 1 Medium single pattern
100Kx1 100,000 1 Large single pattern
1Kx10 1,000 10 Small multi-pattern
10Kx10 10,000 10 Medium multi-pattern
100Kx10 100,000 10 Large multi-pattern
10Kx50 10,000 50 Many patterns
10Kx100 10,000 100 Very many patterns

Implementations Tested

  1. R Base: grepl(), stringr::str_detect(), stringi::stri_detect_regex()
  2. R Parallel (mirai): Parallel over strings or patterns
  3. R Parallel (furrr): future_map() over strings or patterns
  4. Rust (regex): Standard regex crate, single and parallel
  5. Rust (fancy-regex): Supports backrefs/lookaheads, single and parallel

Performance Tips

When to Use Fixed Matching

Set fixed = TRUE when:

Example: string_detect(text, "ERROR", fixed = TRUE)

When Auto-Optimization Helps

The package automatically optimizes based on your data:

Power User Configuration

Advanced users can tune via R options:

# Adjust chunking threshold (in bytes)
options(stringrs.chunk_threshold = 50 * 1024 * 1024)  # 50MB

# Reset to default
options(stringrs.chunk_threshold = NULL)

Architecture

Auto-Optimization Strategy

All performance decisions happen transparently:

Engine Selection

Parallel Strategy

Memory Chunking

Development

Project Structure

stringrs/
├── R/
│   ├── extendr-wrappers.R    # Auto-generated Rust bindings
│   └── string_detect.R        # Main R API (single function)
├── src/
│   └── rust/
│       ├── src/lib.rs          # Rust implementation
│       └── Cargo.toml          # Rust dependencies
├── benchmark/
│   ├── run.R                   # Benchmark runner
│   ├── engines.R               # Engine implementations
│   ├── scenarios.R             # Test scenarios
│   ├── metrics.R               # Performance metrics
│   ├── visualize.R             # Result visualization
│   └── compare.R               # Baseline comparison
└── tests/
    └── testthat/
        └── test-detect.R       # Unit tests

Adding New Implementations

To add a new regex implementation:

  1. Add function to src/rust/src/lib.rs
  2. Export via extendr_module!
  3. Add R wrapper in R/extendr-wrappers.R
  4. Add benchmark function in inst/benchmark_functions.R
  5. Include in benchmark runner

License

MIT + file LICENSE