Skip to content

Simplify significantColors in image.dart#3165

Merged
veloce merged 1 commit into
lichess-org:mainfrom
freebeartogoodhome:simplerImage
May 12, 2026
Merged

Simplify significantColors in image.dart#3165
veloce merged 1 commit into
lichess-org:mainfrom
freebeartogoodhome:simplerImage

Conversation

@freebeartogoodhome

Copy link
Copy Markdown
Contributor

Old:
Map.from iterates through every element to clone the map. Then removeWhere iterates through every element again.
Allocates memory for a full copy of the map, including all the "trash" entries about to be deleted. This triggers the Garbage Collector more frequently.

New:
The collection for iterates through the map exactly once, only adding what is needed.
Only allocate memory for the final filtered result.

A sample test shows it produces the same result:

import 'dart:typed_data';

void main() {
  // Test with a map with 50,000 colors
  final Map<int, int> colorToCount = {
    for (var i = 0; i < 50000; i++) i: (i % 20),
  };
  print('Starting benchmark with ${colorToCount.length} items...');
  final swOld = Stopwatch()..start();
  for (var i = 0; i < 1000; i++) {
    // ignore: unused_local_variable
    final significantColors = Map<int, int>.from(colorToCount)
      ..removeWhere((key, value) => value < 10);
  }
  swOld.stop();
  print('Old Method (Map.from + removeWhere): ${swOld.elapsedMilliseconds}ms');
  final swNew = Stopwatch()..start();
  for (var i = 0; i < 1000; i++) {
    // ignore: unused_local_variable
    final significantColors = {
      for (final entry in colorToCount.entries)
        if (entry.value >= 10) entry.key: entry.value,
    };
  }
  swNew.stop();
  print('New Method (Collection for): ${swNew.elapsedMilliseconds}ms');
  
  if (swOld.elapsedMilliseconds > 0) {
    final double improvement = ((swOld.elapsedMilliseconds - swNew.elapsedMilliseconds) / swOld.elapsedMilliseconds) * 100;
    print('Efficiency Gain: ${improvement.toStringAsFixed(2)}%');
  }
  final oldResult = Map<int, int>.from(colorToCount)..removeWhere((k, v) => v < 10);
  final newResult = {
    for (final entry in colorToCount.entries)
      if (entry.value >= 10) entry.key: entry.value,
  };
  if (oldResult.length == newResult.length && 
      newResult.entries.every((e) => oldResult[e.key] == e.value)) {
    print('✅ Verification Passed: Logic is identical.');
  } else {
    print('❌ Verification Failed: Logic differs!');
  }
}

Starting benchmark with 50000 items...
Old Method (Map.from + removeWhere): 6199ms
New Method (Collection for): 3359ms
Efficiency Gain: 45.81%
✅ Verification Passed: Logic is identical.

@veloce veloce merged commit d4e656c into lichess-org:main May 12, 2026
1 check passed
@freebeartogoodhome freebeartogoodhome deleted the simplerImage branch May 12, 2026 11:19
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.

2 participants