Skip to content

Optimize PackageLister version constraints with discrete bitmasks - #4874

Open
sigurdm wants to merge 1 commit into
dart-lang:masterfrom
sigurdm:optimize-bitmask-constraints
Open

Optimize PackageLister version constraints with discrete bitmasks#4874
sigurdm wants to merge 1 commit into
dart-lang:masterfrom
sigurdm:optimize-bitmask-constraints

Conversation

@sigurdm

@sigurdm sigurdm commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Description

This PR optimizes version constraint evaluations in the version solver (PackageLister) by introducing discrete bitmasks (VersionBitmask).

Problem

Previously, PackageLister.countVersions and PackageLister.bestVersion performed linear traversals (.where((id) => constraint.allows(id.version)).length and reverse iteration loops) over the full version list of each candidate package. For mature packages with hundreds of versions (e.g. analyzer, http, intl, build_runner), this caused solving time and backtracking to scale with package age/version count ($O(N)$).

Solution

  1. VersionBitmask (lib/src/solver/version_bitmask.dart):
    • Represents discrete version constraints over a sorted version list of length $N$ using 32-bit words (Uint32List).
    • Supports $O(1)$ bitwise operations: &, |, difference, allowsAll, allowsAny, count() (popcount), highestIndex(), lowestIndex(), and allows(index).
  2. PackageLister (lib/src/solver/package_lister.dart):
    • Indexes package versions once into _PackageVersionIndex.
    • Constructs bitmasks for VersionConstraint instances via fast binary search (Version, VersionRange, VersionUnion, with SemVer pre-release normalization) and caches mask lookups.
    • Replaces linear traversals in countVersions() and bestVersion() with $O(1)$ bitmask queries.
  3. Tests (test/solver/version_bitmask_test.dart):
    • 75 unit test cases covering bitmask operations across lengths [0, 1, 5, 62, 63, 64, 65, 128, 256, 1000], range boundaries, set operations, and equality.

Benchmarks (50,000 queries per config)

Package Versions ($N$) Metric Baseline (Linear) Bitmask ($O(1)$) Speedup
10 versions countVersions 136.1 ms 43.4 ms 3.1x faster
bestVersion 69.4 ms 42.3 ms 1.6x faster
Combined 205.5 ms 85.7 ms 2.4x faster
30 versions countVersions 436.6 ms 42.0 ms 10.4x faster
bestVersion 75.8 ms 41.8 ms 1.8x faster
Combined 512.5 ms 83.9 ms 6.1x faster
60 versions countVersions 870.5 ms 43.0 ms 20.2x faster
bestVersion 341.5 ms 42.7 ms 8.0x faster
Combined 1,212.1 ms 85.7 ms 14.1x faster
150 versions countVersions 2,074.5 ms 41.6 ms 49.9x faster
bestVersion 1,228.3 ms 42.7 ms 28.8x faster
Combined 3,302.8 ms 84.2 ms 39.2x faster
300 versions countVersions 3,996.6 ms 46.7 ms 85.7x faster
bestVersion 2,815.5 ms 47.2 ms 59.7x faster
Combined 6,812.1 ms 93.8 ms 72.6x faster

@sigurdm
sigurdm force-pushed the optimize-bitmask-constraints branch 6 times, most recently from 8d6f021 to 32e3312 Compare August 20, 2026 09:56
Introduce Bitmask to represent discrete bit vectors over a fixed-size index
range. In PackageLister, index versions once and use binary search to construct
bitmasks for VersionConstraint instances, enabling O(1) countVersions() and
bestVersion() operations.
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