[lib] Improve IndexedIterable performance on VM
* Check against raw `EfficientLengthIterable` instead of specifying
its type parameter. Both checks are equivalent in this context as
`EfficientLengthIterable` is an internal marker interface and an
instance of `Iterable<T>` can never be an instance of
`EfficientLengthIterable` but not an instance of
`EfficientLengthIterable<T>`.
VM compiler is currently not good enough to eliminate the
`is` check if involves an uninstantiated type
(see https://dartbug.com/53445).
* Force inlining of `IndexedIterable` factory,
`IndexedIterable.get iterator` and `IterableExtensions.indexed`.
These changes significantly reduce overhead of for-in-indexed
when compared to baseline classical loop: before these changes
for-in-indexed is 13x slower than classical loop, after these
changes it is only 2.8x slower.
Performance comparison was using the following benchmark kernels:
```dart
final list = List<int>.generate(10000, (i) => i);
// For for-in-indexed
var result = 0;
for (var (i, e) in list.indexed) {
result ^= (i & e);
}
// For classical loop
var result = 0;
for (var i = 0; i < list.length; i++) {
result ^= (i & list[i]);
}
```
CoreLibraryReviewExempt: No API changes, VM specific optimisations.
Change-Id: Ic935a2aab2eda0837981184d872ee1eeef89ee7a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/324461
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
diff --git a/sdk/lib/collection/iterable.dart b/sdk/lib/collection/iterable.dart
index 7d35b6a..9a0a674 100644
--- a/sdk/lib/collection/iterable.dart
+++ b/sdk/lib/collection/iterable.dart
@@ -34,6 +34,7 @@
///
/// The elements are `(0, this.first)` through
/// `(this.length - 1, this.last)`, in index/iteration order.
+ @pragma('vm:prefer-inline')
Iterable<(int, T)> get indexed => IndexedIterable<T>(this, 0);
/// The first element of this iterator, or `null` if the iterable is empty.
diff --git a/sdk/lib/internal/iterable.dart b/sdk/lib/internal/iterable.dart
index 5f4e090..563bda2 100644
--- a/sdk/lib/internal/iterable.dart
+++ b/sdk/lib/internal/iterable.dart
@@ -934,8 +934,9 @@
/// efficiently.
final int _start;
+ @pragma('vm:prefer-inline')
factory IndexedIterable(Iterable<T> source, int start) {
- if (source is EfficientLengthIterable<T>) {
+ if (source is EfficientLengthIterable) {
return EfficientLengthIndexedIterable(source, start);
}
return IndexedIterable._(source, start);
@@ -971,6 +972,7 @@
Iterable<(int, T)> skip(int count) => IndexedIterable<T>.nonEfficientLength(
_source.skip(_checkCount(count)), count + _start);
+ @pragma('vm:prefer-inline')
Iterator<(int, T)> get iterator =>
IndexedIterator<T>(_source.iterator, _start);
}