pjullrich/f_enum
Changelog
v0.1.1
True drop-in replacement for Enum
FEnum now delegates every Enum function, so FEnum.X works for any X that Enum supports. Functions without NIF acceleration transparently forward to Enum. Non-integer lists (strings, tuples, atoms) automatically fall back to Enum when the NIF can't handle them.
Binary input support
All functions now accept packed binaries (<<i::signed-native-64>> format) in addition to lists and FEnum.Ref. When a binary is passed, it goes to the NIF by reference with near-zero copy -- no Rustler list protocol overhead. This is the fastest path:
- sort: 9.6x faster than Enum (binary) vs 5.7x (list)
- uniq: 38.9x faster than Enum (binary) vs 19.1x (list)
- sum: 18.4x faster than Enum (binary), delegates to Enum for lists
- min/max: 10.4x faster than Enum (binary), delegates to Enum for lists
Rust NIF optimizations
- Lock-free resource storage: Replaced
RwLock<Vec<i64>>withBox<[i64]>. The data is immutable after creation, so the atomic CAS on every read was pure overhead. - FxHash for uniq and frequencies: Replaced std
HashMap/HashSet(SipHash) withrustc-hashFxHash. 4x faster for uniq on lists, 6x on binaries. - Zero-copy binary reads: Aggregations (sum, min, max, member?, etc.) no longer copy the binary into a Vec. They read the bytes in-place via a macro that reinterprets the binary pointer.
- Custom Encoder for FrequencyMap: Encodes directly to an Erlang map term via
Term::map_from_term_arrays, skipping the O(n) FxHashMap-to-std-HashMap rehash conversion. - itoa for join: Replaced
write!("{}", v)formatting with theitoacrate for ~3x faster integer-to-string conversion. - Single-pass minmax: One loop instead of separate
.min()+.max()calls. - Cache-friendly reverse:
memcpy+ in-place swap instead of backwards reads that cause prefetcher misses. - Pre-sized allocations:
Vec::with_capacity,HashMap::with_capacity,String::with_capacitythroughout. - Raw pointer writes for with_index/zip: Enables auto-vectorization by removing per-element capacity checks.
get_uncheckedfor at/zip: Bounds already validated, so the redundant check is removed.- Cargo.toml:
lto = "fat",codegen-units = 1,panic = "abort"for maximum link-time optimization.
Elixir-side optimizations
- Skip NIF calls for length-preserving chain ops:
sort,reverse,concat,slice,take,dropcompute the output length from the input length in pure Elixir arithmetic instead of callingnif_length. - Pure Elixir binary operations:
at,slice,take,drop,count,concatfor binaries usebinary_part/pattern matching instead of NIF calls. O(1) sub-binary references with zero allocation. - Removed O(n)
length(list)fromnew/1: Usesnif_length(O(1) field read) instead. - Smart delegation: Simple traversals (sum, min, max, reverse, member?, dedup) delegate to
Enumfor lists since the BEAM's JIT is already optimal. NIF path only activates for operations where Rust genuinely wins (sort, uniq, frequencies).
Testing
- Full compatibility with Elixir v1.19's official Enum test suite (227 tests, 0 failures).
- 422 total tests across FEnum-specific and Enum compatibility suites.
v0.1.0
Initial release with core FEnum functionality:
- One-shot mode (list in, list out) for sort, reverse, dedup, uniq, sum, product, min, max, min_max, count, at, fetch!, slice, take, drop, member?, empty?, concat, frequencies, join, with_index, zip, chunk_every, into.
- Chain mode via
FEnum.new/1andFEnum.run/1keeping data in Rust between operations. - Tier 2 hybrid functions (filter, reject, map, flat_map, reduce, map_reduce, scan, find, find_index, find_value, any?, all?, count/2, sort_by, each, group_by).
- Enumerable and Inspect protocol implementations for
FEnum.Ref. - Fallback to
Enumfor non-list enumerables.