Skip to content

Conversation

@Danilospano00
Copy link

@Danilospano00 Danilospano00 commented Jun 18, 2025

Status

READY

Breaking Changes

NO

Description

This PR optimizes the iterableEquals function to improve performance when comparing List types by using direct indexing. It also ensures efficient comparison for other Iterable types using iterators, while maintaining the function's compatibility with various iterable structures.

Todos

  • Tests
  • Documentation
  • Examples

Steps to Test or Reproduce

Steps to Test or Reproduce
The updated iterableEquals function improves performance by handling List types separately.

Old Version: Uses elementAt(i) for all Iterable types, which is efficient only for List. For other iterables like LinkedList or Queue, elementAt(i) can be inefficient (O(n)), potentially leading to O(n²) performance for large iterables.

New Version: Specifically optimizes List comparison by using direct indexing (O(1)), making it faster for lists. For other Iterable types, it uses iterators, ensuring linear time complexity (O(n)) for all cases. This avoids the performance bottleneck of elementAt(i).

In summary, the new version is more efficient, especially for List types, and ensures better performance for all Iterable structures by using the appropriate methods for each.

Benchmark on MAC m3:

Before

EmptyEquatable
          total runs:  2 109 775   
          total time:     2.0000  s
         average run:          0 μs
         runs/second:   Infinity
               units:        100   
        units/second:   Infinity
       time per unit:     0.0000 μs

PrimitiveEquatable
          total runs:    827 764   
          total time:     2.0000  s
         average run:          2 μs
         runs/second:    500 000   
               units:        100   
        units/second: 50 000 000   
       time per unit:     0.0200 μs

CollectionEquatable (static, small)
          total runs:    157 982   
          total time:     2.0000  s
         average run:         12 μs
         runs/second:     83 333   
               units:        100   
        units/second:  8 333 333   
       time per unit:     0.1200 μs

CollectionEquatable (static, medium)
          total runs:    100 363   
          total time:     2.0000  s
         average run:         19 μs
         runs/second:     52 632   
               units:        100   
        units/second:  5 263 158   
       time per unit:     0.1900 μs

CollectionEquatable (static, large)
          total runs:     19 913   
          total time:     2.0000  s
         average run:        100 μs
         runs/second:     10 000   
               units:        100   
        units/second:  1 000 000   
       time per unit:     1.0000 μs

CollectionEquatable (dynamic, small)
          total runs:    413 921   
          total time:     2.0000  s
         average run:          4 μs
         runs/second:    250 000   
               units:        100   
        units/second: 25 000 000   
       time per unit:     0.0400 μs

CollectionEquatable (dynamic, medium)
          total runs:    412 132   
          total time:     2.0000  s
         average run:          4 μs
         runs/second:    250 000   
               units:        100   
        units/second: 25 000 000   
       time per unit:     0.0400 μs

CollectionEquatable (dynamic, large)
          total runs:    421 141   
          total time:     2.0000  s
         average run:          4 μs
         runs/second:    250 000   
               units:        100   
        units/second: 25 000 000   
       time per unit:     0.0400 μs

After

EmptyEquatable
          total runs:  2 202 358   
          total time:     2.0000  s
         average run:          0 μs
         runs/second:   Infinity
               units:        100   
        units/second:   Infinity
       time per unit:     0.0000 μs

PrimitiveEquatable
          total runs:    975 117   
          total time:     2.0000  s
         average run:          2 μs
         runs/second:    500 000   
               units:        100   
        units/second: 50 000 000   
       time per unit:     0.0200 μs

CollectionEquatable (static, small)
          total runs:    180 526   
          total time:     2.0000  s
         average run:         11 μs
         runs/second:     90 909   
               units:        100   
        units/second:  9 090 909   
       time per unit:     0.1100 μs

CollectionEquatable (static, medium)
          total runs:    145 341   
          total time:     2.0000  s
         average run:         13 μs
         runs/second:     76 923   
               units:        100   
        units/second:  7 692 308   
       time per unit:     0.1300 μs

CollectionEquatable (static, large)
          total runs:     49 162   
          total time:     2.0000  s
         average run:         40 μs
         runs/second:     25 000   
               units:        100   
        units/second:  2 500 000   
       time per unit:     0.4000 μs

CollectionEquatable (dynamic, small)
          total runs:    482 441   
          total time:     2.0000  s
         average run:          4 μs
         runs/second:    250 000   
               units:        100   
        units/second: 25 000 000   
       time per unit:     0.0400 μs

CollectionEquatable (dynamic, medium)
          total runs:    484 044   
          total time:     2.0000  s
         average run:          4 μs
         runs/second:    250 000   
               units:        100   
        units/second: 25 000 000   
       time per unit:     0.0400 μs

CollectionEquatable (dynamic, large)
          total runs:    497 302   
          total time:     2.0000  s
         average run:          4 μs
         runs/second:    250 000   
               units:        100   
        units/second: 25 000 000   
       time per unit:     0.0400 μs

@Danilospano00 Danilospano00 requested a review from felangel as a code owner June 18, 2025 08:07
@felangel
Copy link
Owner

Would you mind running the benchmarks before and after and including the results in the description?

@Danilospano00
Copy link
Author

Done, and I've refactored the comments so the build respect lint rules

@Danilospano00
Copy link
Author

I've added tests for the Queues to cover all cases.

@felangel
Copy link
Owner

I've added tests for the Queues to cover all cases.

Thanks! I’ll review later today 🙏

expect(iterableEquals(queue1, queue2), isTrue);
});

test('returns false for two different Queues', () {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also add a third queue with a different length just to make sure that case is correctly handled.

@felangel felangel changed the title feat: Optimize iterableEquals function for better handling of Lists and other Iterables feat: iterableEquals performance optimizations Jul 14, 2025
@felangel felangel added the enhancement New feature or request label Jul 14, 2025
@felangel
Copy link
Owner

@Danilospano00 I just took a closer look and it looks like when you run in AOT, there is actually a regression.

Before:

CollectionEquatable (static, large)
          total runs:     33 653
          total time:     2.0000  s
         average run:         59 μs
         runs/second:     16 949
               units:        100
        units/second:  1 694 915
       time per unit:     0.5900 μs

After:

CollectionEquatable (static, large)
          total runs:     20 008   
          total time:     2.0001  s
         average run:         99 μs
         runs/second:     10 101   
               units:        100   
        units/second:  1 010 101   
       time per unit:     0.9900 μs

dart compile exe benchmarks/main.dart

@felangel
Copy link
Owner

Need to profile it more to see what's going on but I don't think we should ship a regression in AOT so I think this needs a bit more tweaking before it's ready to merge.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants