Skip to content

Conversation

@depial
Copy link

@depial depial commented Dec 30, 2025

This update brings combinations.jl in line with the updates to permuations.jl in PR #205.

Broadly, this includes:

  • Return a Combinations iterator, similarly to the other iterators, instead of one wrapped in a Generator.
  • Mutating the state during iteration, thereby saving memory allocation.

More specifically, the Combinations iterator has been made to parallel other implementations in the package, which also eliminates the state being returned during iteration (i.e. it now keeps it private to iterate). Performance remains largely the same, with just three extra allocations and a very slight drop in running time. This does result in a redefinition of Combinations to accept a Vector and Int, whereas it used to accept an Int and Int.

The performance boosts have come with changes to MultiSetCombinations and WithReplacementCombinations mostly with a reduction in allocations by mutating the iteration state (while keeping it private to iterate).

An effort was made to standardize the implementations in both combinations.jl and permutations.jl, so all have similar return values and underlying design for easier maintenance.

Benchmarks

multiset_combinations(1:12, 6) |> collect

Before

BenchmarkTools.Trial: 6124 samples with 1 evaluation per sample.
 Range (min  max):  748.202 μs    5.619 ms  ┊ GC (min  max): 0.00%  83.01%
 Time  (median):     772.638 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   811.868 μs ± 205.246 μs  ┊ GC (mean ± σ):  3.21% ±  8.48%

  ██▅▄▄▃▁▁                                                      ▁
  ████████▇▇▇▅▇▅▆▆▄▅▅▅▃▃▁▄▄▄▁▃▄▁▄▁▄▃▃▁▁▁▃▁▃▃▁▁▁▁▁▃▁▁▁▁▃▁▁▅▅▇▇▇▇ █
  748 μs        Histogram: log(frequency) by time          2 ms <

 Memory estimate: 427.17 KiB, allocs estimate: 8935.

After

BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min  max):  29.404 μs    6.432 ms  ┊ GC (min  max):  0.00%  98.83%
 Time  (median):     32.010 μs               ┊ GC (median):     0.00%
 Time  (mean ± σ):   40.272 μs ± 122.507 μs  ┊ GC (mean ± σ):  12.94% ±  5.06%

  ▂▆█▇▇▇▅▅▄▄▄▄▃▂▂▁▁             ▁▂▃▃▂▁▁        ▁▁▁▁            ▂
  ████████████████████▇██▇▇▇▆▆▆▇██████████▇▇▇██████▇█▆▆▇▅▅▅▆▅▆ █
  29.4 μs       Histogram: log(frequency) by time      61.9 μs <

 Memory estimate: 117.20 KiB, allocs estimate: 2029.

multiset_combinations([9, 8, 8, 1, 2, 6, 1, 6, 7, 7, 4, 8], 6) |> collect

Before

BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min  max):  130.244 μs    5.860 ms  ┊ GC (min  max): 0.00%  96.83%
 Time  (median):     135.780 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   144.084 μs ± 104.581 μs  ┊ GC (mean ± σ):  2.93% ±  4.16%

   █▃  ▅                                                         
  ▅██▅▇██▄▃▃▃▃▃▃▄▆▄▃▄▅▄▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂▂▁▂ ▃
  130 μs           Histogram: frequency by time          189 μs <

 Memory estimate: 79.27 KiB, allocs estimate: 1617.

After

BenchmarkTools.Trial: 10000 samples with 5 evaluations per sample.
 Range (min  max):  6.245 μs   1.447 ms  ┊ GC (min  max):  0.00%  98.77%
 Time  (median):     6.774 μs              ┊ GC (median):     0.00%
 Time  (mean ± σ):   8.525 μs ± 27.436 μs  ┊ GC (mean ± σ):  13.38% ±  4.99%

   ▄▆██▇▆▅▄▄▄▄▃▂▂▁                   ▁▂▂▂▂▁         ▂▂▂▂▁    ▂
  ▇████████████████▇▇█▇▇▆▇▇▆▄▅▅▅▅▃▅▇███████▇▇▅▆▅▅▇████████▇▇ █
  6.24 μs      Histogram: log(frequency) by time     12.1 μs <

 Memory estimate: 24.36 KiB, allocs estimate: 440.

with_replacement_combinations(1:12, 6) |> collect

Before

BenchmarkTools.Trial: 5882 samples with 1 evaluation per sample.
 Range (min  max):  621.257 μs    8.156 ms  ┊ GC (min  max):  0.00%  83.88%
 Time  (median):     660.460 μs               ┊ GC (median):     0.00%
 Time  (mean ± σ):   846.161 μs ± 627.301 μs  ┊ GC (mean ± σ):  16.47% ± 17.31%

  █▆▅▃▂▁▁                                                       ▁
  ████████████▇▇█▆▄▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▁▁▄▅▅▅▅▄▇▆▆▆▇▇▆▇▇▇▆▇▆▇▆▇▆▇▆▅ █
  621 μs        Histogram: log(frequency) by time       3.61 ms <

 Memory estimate: 3.12 MiB, allocs estimate: 61885.

After

BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min  max):  321.837 μs    6.602 ms  ┊ GC (min  max):  0.00%  91.76%
 Time  (median):     343.325 μs               ┊ GC (median):     0.00%
 Time  (mean ± σ):   405.012 μs ± 296.448 μs  ┊ GC (mean ± σ):  12.33% ± 13.97%

  █▆▄▂                                                          ▁
  █████▇▇██▇▇▇▄▁▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▄▄▄▅▆▅▆▆▆▆▆▇▆▆▆▆▅▆▆▆▆▆▆ █
  322 μs        Histogram: log(frequency) by time       2.06 ms <

 Memory estimate: 1.42 MiB, allocs estimate: 24760.

combinations(1:12, 6) |> collect

Before

BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min  max):  25.064 μs    6.588 ms  ┊ GC (min  max):  0.00%  98.92%
 Time  (median):     31.577 μs               ┊ GC (median):     0.00%
 Time  (mean ± σ):   43.241 μs ± 135.331 μs  ┊ GC (mean ± σ):  13.15% ±  4.70%

   ▃  ▆█                                                        
  ▃██▅███▄▃▃▃▃▃▃▃▂▃▄▄▄▃▃▃▃▄▃▂▂▂▂▂▂▁▂▁▁▁▁▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▂
  25.1 μs         Histogram: frequency by time           83 μs <

 Memory estimate: 108.48 KiB, allocs estimate: 1853.

After

BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min  max):  24.348 μs    6.947 ms  ┊ GC (min  max):  0.00%  98.88%
 Time  (median):     28.325 μs               ┊ GC (median):     0.00%
 Time  (mean ± σ):   36.609 μs ± 129.873 μs  ┊ GC (mean ± σ):  13.48% ±  4.71%

    █▇▂▃▆▅                                                      
  ▂▇██████▇▄▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▃▃▃▃▃▃▃▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂▂▂▂▂▂▂▂ ▃
  24.3 μs         Histogram: frequency by time         63.8 μs <

 Memory estimate: 108.67 KiB, allocs estimate: 1856.

As in PR #205, multiset_combinations(m, f::Vector{<:Integer}, t::Integer) has been renamed to MultisetCombinations(m, f::Vector{<:Integer}, t::Integer) since this method is not intended to be exported.

@codecov
Copy link

codecov bot commented Dec 30, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.35%. Comparing base (b808ce2) to head (fa1700a).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #207      +/-   ##
==========================================
+ Coverage   97.17%   97.35%   +0.18%     
==========================================
  Files           8        8              
  Lines         813      831      +18     
==========================================
+ Hits          790      809      +19     
+ Misses         23       22       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant