Skip to main content

Module benchmarking

Module benchmarking 

Source
Expand description

§Benchmarking Comparison

This document outlines benchmarks implemented for the fp-library against Rust’s standard library.

§Key Results

Results collected on: AMD Ryzen 9 7940HS (16 cores), Linux 6.12.77, rustc 1.93.1, bench profile.

§CoyonedaExplicit Map Fusion

CoyonedaExplicit composes maps at the type level, resulting in a single call to F::map at lower time regardless of chain depth. The fused line is flat while Direct scales linearly.

Coyoneda Fusion

§Coyoneda Variants

Compares all Coyoneda variants across map chain depths (1-100). Direct and Box-Coyoneda are fastest; Rc/Arc variants pay per-map allocation costs.

Coyoneda Variants

§CatList Cons

CatList cons vs Vec insert(0) vs LinkedList push_front across sizes. Vec’s O(n) insert(0) crosses over CatList at ~2000-2500 elements.

CatList Cons

§Parallel vs Sequential Fold Map

Vec par_fold_map vs sequential fold_map across sizes (100-100K) with string monoid. Rayon parallelism crosses over at ~3000-4000 elements. Log scale on both axes.

Vec Par Fold Map

§Trampoline vs Iterative Loop

Cost of stack safety: Trampoline vs plain recursion vs a hand-written while loop, all doing equivalent work per step. Trampoline is ~230x slower than recursion but never overflows (recursion overflows at ~500K depth). Log scale on Y axis.

Trampoline vs Iterative

§Detailed Comparisons

The following tables list all implemented benchmarks.

§Vec

Featurefp-librarystd
MapVecBrand::mapiter().map().collect()
Fold RightVecBrand::fold_rightiter().rev().fold()
Fold LeftVecBrand::fold_leftiter().fold()
Fold MapVecBrand::fold_mapiter().map().fold()
TraverseVecBrand::traverseiter().map().collect::<Result<Vec<_>, _>>() (for Result)
SequenceVecBrand::sequenceiter().collect::<Result<Vec<_>, _>>() (for Result)
BindVecBrand::binditer().flat_map().collect()
AppendSemigroup::append[a, b].concat()
EmptyMonoid::emptyVec::new()
ConstructVecBrand::construct[vec![x], y].concat()
DeconstructVecBrand::deconstructslice.split_first()
FilterVecBrand::filteriter().filter().collect()
Filter MapVecBrand::filter_mapiter().filter_map().collect()
PartitionVecBrand::partitioniter().partition()
Partition MapVecBrand::partition_mapManual loop with two accumulators
CompactVecBrand::compactiter().flatten().collect()
SeparateVecBrand::separateManual loop splitting Results
WitherVecBrand::witherManual loop with conditional push
WiltVecBrand::wiltManual loop with two accumulators
Lift2VecBrand::lift2flat_map + map combination
PureVecBrand::purevec![x]
ApplyVecBrand::applyflat_map + map combination
Par MapVecBrand::par_mapSequential map (100, 1K, 10K, 100K)
Par Fold MapVecBrand::par_fold_mapSequential fold_map (100, 1K, 10K, 100K)
Par Filter MapVecBrand::par_filter_mapSequential filter_map (100, 1K, 10K, 100K)
Par CompactVecBrand::par_compactSequential compact (100, 1K, 10K, 100K)

§Option

Featurefp-librarystd
MapOptionBrand::mapOption::map
Fold RightOptionBrand::fold_rightmap_or
Fold LeftOptionBrand::fold_leftmap_or
TraverseOptionBrand::traverseOption::map().transpose() (for Result)
SequenceOptionBrand::sequenceOption::transpose() (for Result)
BindOptionBrand::bindOption::and_then
FilterOptionBrand::filterOption::filter
Filter MapOptionBrand::filter_mapOption::and_then
PartitionOptionBrand::partitionConditional split
Partition MapOptionBrand::partition_mapmap_or with conditional
CompactOptionBrand::compactOption::flatten
SeparateOptionBrand::separatePattern match on Option<Result>
WitherOptionBrand::withermap + unwrap_or
WiltOptionBrand::wiltmap + unwrap_or
Lift2OptionBrand::lift2Option::zip + map
PureOptionBrand::pureSome(x)
ApplyOptionBrand::applyPattern match

§Result

Featurefp-librarystd
MapResultErrAppliedBrand::mapResult::map
Fold RightResultErrAppliedBrand::fold_rightmap_or
Fold LeftResultErrAppliedBrand::fold_leftmap_or
TraverseResultErrAppliedBrand::traverseResult::map().transpose() (for Option)
SequenceResultErrAppliedBrand::sequenceResult::transpose() (for Option)
BindResultErrAppliedBrand::bindResult::and_then
Lift2ResultErrAppliedBrand::lift2and_then + map
PureResultErrAppliedBrand::pureOk(x)
ApplyResultErrAppliedBrand::applyPattern match

§String

Featurefp-librarystd
AppendSemigroup::append+ / push_str
EmptyMonoid::emptyString::new()

§Pair

Featurefp-librarystd
MapPairFirstAppliedBrand::mapManual tuple construction
Fold RightPairFirstAppliedBrand::fold_rightDirect field access
Fold LeftPairFirstAppliedBrand::fold_leftDirect field access
TraversePairFirstAppliedBrand::traversemap + tuple reconstruction
SequencePairFirstAppliedBrand::sequencemap + tuple reconstruction
BindPairFirstAppliedBrand::bindManual semigroup append + extraction
Lift2PairFirstAppliedBrand::lift2Manual semigroup append + field combine
PurePairFirstAppliedBrand::purePair(Monoid::empty(), x)
ApplyPairFirstAppliedBrand::applyManual semigroup append + function apply

§Functions

Featurefp-librarystd
Identityidentitystd::convert::identity

§Lazy Evaluation

Featurefp-libraryDescription
Thunk BaselineThunk::new + evaluateBaseline overhead
Thunk Map ChainThunk::map chains (1, 5, 10, 25, 50, 100)Cost of chained maps
Thunk Bind ChainThunk::bind chains (1, 5, 10, 25, 50, 100)Cost of chained binds
Trampoline BaselineTrampoline::new + evaluateBaseline overhead
Trampoline Bind ChainTrampoline::bind chains (100-10K, 6 sizes)Stack-safe bind performance
Trampoline Map ChainTrampoline::map chains (100-10K, 6 sizes)Stack-safe map performance
Trampoline tail_rec_mCountdown from 10K via ControlFlowMonadic tail recursion
Trampoline vs Iterativetail_rec_m vs hand-written loopOverhead vs imperative code
RcLazy First AccessLazy::<_, RcLazyConfig>::new + first evaluateMemoization first-access cost
RcLazy Cached AccessRepeated evaluate on cached valueMemoization cache-hit cost
RcLazy ref_map Chainref_map chains (1, 5, 10, 25, 50, 100)Cost of chained ref-mapped lazy values
ArcLazy First AccessArcLazy::new + first evaluateThread-safe memoization first-access cost
ArcLazy Cached AccessRepeated evaluate on cached valueThread-safe memoization cache-hit cost
ArcLazy ref_map Chainref_map chains (1, 5, 10, 25, 50, 100)Cost of chained ref-mapped thread-safe lazy values
Free Left-Assoc BindLeft-associated Free::bind chains (100-10K, 6 sizes)CatList-backed O(1) bind reassociation
Free Right-Assoc BindRight-associated Free::bind chains (100-10K, 6 sizes)Nested right-bind performance
Free EvaluateFree::wrap + bind chains (100-10K, 6 sizes)Evaluation of suspended computations

§CatList

FeatureCompared AgainstDescription
ConsCatList vs LinkedList vs VecPrepend element (O(1))
SnocCatList vs VecAppend element (O(1))
AppendCatList vs VecConcatenation (O(1) vs O(n))
UnconsCatList vs Vec vs LinkedListHead/Tail decomposition (amortized O(1))
Left-Assoc AppendCatList vs Vec vs LinkedListRepeated left-associated appends (O(n) vs O(n^2))
IterationCatList vs Vec vs LinkedListFull iteration overhead
Nested UnconsCatList (nested vs flat)Uncons on deeply nested structures
Fold MapCatList vs Vec (fp + std)fold_map performance
Fold LeftCatList vs Vec (fp + std)fold_left performance
TraverseCatList vs Vec (fp)traverse with Option
FilterCatList vs Vec (fp + std)filter performance
CompactCatList vs Vec (fp + std)compact performance

§Coyoneda

FeatureCompared AgainstDescription
Direct vs VariantsDirect map vs all 4 Coyoneda variantsMap chain cost at depths 1, 5, 10, 25, 50, 100
Repeated LowerRcCoyoneda vs ArcCoyonedaRe-evaluation cost (3x lower_ref)
Clone MapRcCoyoneda vs ArcCoyonedaClone + map + lower_ref pattern