Skip to content

Performance Benchmarks ​

Benchmark Environment ​

  • Node.js: v20.10.0
  • CPU: Apple M1 Pro (10 cores)
  • RAM: 16 GB
  • Dataset Size: 10,000 objects (unless specified)
  • Iterations: 1,000 runs per test
  • Tool: Vitest benchmark

Filter Strategy Comparison ​

Small Dataset (100 items) ​

StrategyTime (ms)Ops/secRelative
Simple string0.0520,0001.00x (baseline)
Object match0.0616,6671.20x
Operator ($eq)0.0714,2861.40x
Predicate function0.1010,0002.00x
Wildcard (%)0.156,6673.00x

Medium Dataset (1,000 items) ​

StrategyTime (ms)Ops/secRelative
Simple string0.452,2221.00x
Object match0.521,9231.16x
Operator ($eq)0.581,7241.29x
Predicate function0.951,0532.11x
Wildcard (%)1.427043.16x

Large Dataset (10,000 items) ​

StrategyTime (ms)Ops/secRelative
Simple string4.22381.00x
Object match4.82081.14x
Operator ($eq)5.31891.26x
Predicate function8.91122.12x
Wildcard (%)13.5743.21x

Operator Performance ​

Comparison Operators (10,000 items) ​

OperatorTime (ms)Ops/sec
$eq5.3189
$ne5.4185
$gt5.5182
$gte5.5182
$lt5.6179
$lte5.6179
Range ($gte + $lte)6.2161

Array Operators (10,000 items) ​

OperatorTime (ms)Ops/sec
$in (3 items)6.8147
$in (10 items)8.2122
$nin (3 items)6.9145
$contains7.1141
$size5.8172

String Operators (10,000 items) ​

OperatorTime (ms)Ops/sec
$startsWith7.2139
$endsWith7.3137
$contains7.5133
$regex (simple)12.183
$regex (complex)18.554

Logical Operators (10,000 items) ​

OperatorTime (ms)Ops/sec
$and (2 conditions)8.5118
$and (5 conditions)12.381
$or (2 conditions)7.8128
$or (5 conditions)10.298
$not6.1164

Lazy Evaluation Performance ​

Early Exit Optimization ​

OperationStandardLazyImprovement
Find first match (position 10)4.2ms0.04ms105x faster
Find first 10 matches4.2ms0.42ms10x faster
Check existence (match at position 5)4.2ms0.02ms210x faster
Check existence (no match)4.2ms4.2msSame (full scan)

Memory Usage ​

Dataset SizeStandardLazySavings
1,000 items1.2 MB12 KB99% less
10,000 items12 MB12 KB99.9% less
100,000 items120 MB12 KB99.99% less
1,000,000 items1.2 GB12 KB99.999% less

Memoization Strategy ​

Multi-Layer Caching Architecture ​

The filter library implements a sophisticated multi-layer memoization strategy:

  1. Result Cache - Caches complete filter results using WeakMap
  2. Predicate Cache - Memoizes compiled predicate functions (LRU with TTL)
  3. Regex Cache - Caches compiled regex patterns
  4. Expression Hash - Stable hashing for cache key generation

Cache Performance Impact ​

OperationWithout CacheWith CacheSpeedup
First run (10K items)5.3ms5.3ms1.0x (baseline)
Second run (same query)5.3ms0.01ms530x
Regex pattern (first)12.1ms12.1ms1.0x
Regex pattern (cached)12.1ms0.02ms605x
Complex nested (first)15.2ms15.2ms1.0x
Complex nested (cached)15.2ms0.01ms1520x

Cache Configuration ​

typescript
// Enable all caching layers
filter(data, expression, { enableCache: true });

// Clear all caches manually
import { clearFilterCache } from '@mcabreradev/filter';
clearFilterCache();

// Get cache statistics
import { getFilterCacheStats } from '@mcabreradev/filter';
const stats = getFilterCacheStats();
console.log(stats); // { predicateCacheSize: 45, regexCacheSize: 12 }

LRU Cache Settings ​

  • Max Size: 500 predicates, 1000 regex patterns
  • TTL: 5 minutes (300,000ms)
  • Eviction: Least Recently Used (LRU)

Memory Usage ​

Cache TypeMemory per EntryMax Memory
Result Cache~8 bytes (WeakMap ref)Unlimited*
Predicate Cache~200 bytes~100 KB
Regex Cache~150 bytes~150 KB

*WeakMap automatically garbage collects when arrays are no longer referenced

Best Practices ​

✅ Enable caching for:

  • Large datasets (>1000 items)
  • Repeated identical queries
  • Complex expressions with regex
  • Read-heavy workloads

❌ Disable caching for:

  • Frequently changing data
  • One-time queries
  • Memory-constrained environments
  • Unique expressions every time

Cache Invalidation ​

typescript
// Automatic: WeakMap clears when array is garbage collected
let data = [/* large dataset */];
filter(data, query, { enableCache: true });
data = null; // Cache entry automatically cleared

// Manual: Clear all caches
clearFilterCache();

// TTL: Predicate cache entries expire after 5 minutes

Configuration Impact ​

Case Sensitivity ​

caseSensitiveTime (ms)Relative
false (default)7.21.00x
true5.80.81x (faster)

Insight: Case-insensitive matching adds ~20% overhead due to .toLowerCase() calls.

Max Depth ​

maxDepthTime (ms)Relative
14.51.00x
3 (default)5.21.16x
56.11.36x
108.31.84x

Insight: Each additional depth level adds ~10-15% overhead for nested objects.

Real-World Scenarios ​

typescript
// Scenario: Filter 10,000 products
const query = {
  category: { $in: ['Electronics', 'Accessories'] },
  price: { $gte: 100, $lte: 500 },
  inStock: true,
  rating: { $gte: 4.0 }
};

// Results:
// - Time: 12.5ms
// - Throughput: 800 products/sec
// - Matches: ~350 products

User Management ​

typescript
// Scenario: Filter 50,000 users
const query = {
  $and: [
    { active: true },
    { $or: [{ role: 'admin' }, { permissions: { $contains: 'manage' } }] },
    { lastLogin: { $gte: thirtyDaysAgo } }
  ]
};

// Results:
// - Time: 45ms
// - Throughput: 1,111 users/sec
// - Matches: ~2,500 users

Log Analysis ​

typescript
// Scenario: Filter 1,000,000 log entries
const query = {
  level: { $in: ['error', 'critical'] },
  timestamp: { $gte: yesterday, $lte: today },
  message: { $regex: 'database.*timeout' }
};

// Standard filter:
// - Time: 2,500ms
// - Memory: 1.2 GB

// Lazy filter (first 100):
// - Time: 15ms (167x faster)
// - Memory: 120 KB (10,000x less)

Optimization Recommendations ​

1. Choose the Right Strategy ​

typescript
// ✅ Best for exact matches
filter(data, { id: 123 });

// ✅ Best for ranges
filter(data, { age: { $gte: 18, $lte: 65 } });

// ✅ Best for complex logic
filter(data, (item) => customComplexLogic(item));

2. Use Lazy Evaluation for Large Datasets ​

typescript
// ❌ Slow: Process all 1M records
const results = filter(millionRecords, query);
displayFirst10(results);

// ✅ Fast: Only process what's needed
const results = filterFirst(millionRecords, query, 10);
displayFirst10(results);

3. Enable Caching for Repeated Queries ​

typescript
// ✅ Good for repeated identical queries
filter(data, query, { enableCache: true });

4. Optimize Operator Order ​

typescript
// ✅ Fast checks first
filter(products, {
  $and: [
    { inStock: true },
    { price: { $lte: 100 } },
    { name: { $regex: 'pattern' } }
  ]
});

5. Avoid Deep Nesting ​

typescript
// ⚠️ Slower
filter(deeplyNested, query, { maxDepth: 10 });

// ✅ Faster
filter(flatStructure, query, { maxDepth: 2 });

Benchmark Methodology ​

Setup ​

typescript
import { bench, describe } from 'vitest';
import { filter } from '@mcabreradev/filter';

const data = Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  name: `Item ${i}`,
  price: Math.random() * 1000,
  category: ['A', 'B', 'C'][i % 3],
  inStock: i % 2 === 0
}));

describe('Filter Performance', () => {
  bench('simple string match', () => {
    filter(data, 'Item 5000');
  });

  bench('operator query', () => {
    filter(data, { price: { $gte: 500 } });
  });

  bench('complex query', () => {
    filter(data, {
      $and: [
        { category: { $in: ['A', 'B'] } },
        { price: { $gte: 100, $lte: 500 } },
        { inStock: true }
      ]
    });
  });
});

Running Benchmarks ​

bash
pnpm run bench

Continuous Monitoring ​

Benchmarks are run automatically in CI/CD on every PR to detect performance regressions.

Regression Threshold: 10% slowdown triggers a warning.

See Also ​

Released under the MIT License.