-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
A common pattern when allocating a large list where the length is known ahead of time is to use the default List constructor. This avoids needing to repeatedly grow the internal buffer, though there might have been other optimizations that applied too.
var result = List<int>(1000);
for (var i = 0; i < 1000; i++) {
result[i] = i;
}
In a null-safe world, this API has been removed since it required filling the list with nulls . There are several alternative APIs that could be used.
- List.add
var result = <int>[];
for (var i = 0; i < 1000; i++) {
result.add(i);
}
- Collection for
var result = <int>[
for (var i = 0; i < 1000; i++) i
];
- List.generate
int fill(int x) => x;
var result = List<int>.generate(1000, fill);
Preallocating the list is between 2 - 4x faster than any of these alternative methods unfortunately. This feels bad because it will only impact users that already needed to look for performance. Whether or not that performance is needed is hard to say, but it would be nice to have a good replacement for this optimization.
Looking at the kernel output for the Collection for, it essentially de-sugars into repeated calls to List.add:
static method generateWithElement() → core::List<core::int*>* {
return block {
final core::List<core::int*>* #t1446 = <core::int*>[];
for (core::int* i = 0; i.{core::num::<}(1000); i = i.{core::num::+}(1))
[@vm.call-site-attributes.metadata=receiverType:InterfaceType(List<int*>*)] #t1446.{core::List::add}(i);
} =>#t1446;
}
And while List.generate) does preallocate the right size, it also requires a closure and seems to be running marginally slower than List.add in AOT
It seems like the easiest way (though I really have no idea) to get some performance back here would be to set an initial capacity in the collection for, if a some minimal length is known. This could apply if a collection for is used with a loop or List with no nested collection ifs.
The performance measurement was done with this benchmark running a flutter release build on android-arm64. Note: its not a very good benchmark, the best way to work around that is to comment out all but one of the methods for testing.