What is more performant?
[
for(var item in items)
...
]
or
items.map((e) => ...).toList();
Of course those example are very minimalistic. But I wonder what would be more performant if you have many items and a lot of logic to build the list.
CodePudding user response:
I need to first state that it is not always best to write code with the best theoretical performance, since we in most cases does not need the best performance but rather the most readable code.
Things can also quickly change with newer releases of a programming language since the compiler gets smarter and can do more performance tricks to optimize the code. Especially when it comes to comparing a newer language feature against the older way to achieve the same thing since the new feature might yet have the same amount of optimizations.
It is also very difficult to do any meaningful generic benchmark since performance very often is dependent on the specific application.
E.g. in this example, the runtime type of the result from map() returned directly when calling on List is different than if you do it on the result from a where:
void main() {
final list = [1, 2, 3];
print(list.map((e) => e).runtimeType); // MappedListIterable<int, int>
print(list.where((e) => e.isOdd).map((e) => e).runtimeType); // MappedIterable<int, int>
}
Why? Because this is an optimization that is possible since we know that the result from map() on a List directly have the same amount of elements as the List called map() on, and we can go to a specific element without starting from the beginning:
/**
* Specialized alternative to [MappedIterable] for mapped [List]s.
*
* Expects efficient `length` and `elementAt` on the source iterable.
*/
class MappedListIterable<S, T> extends ListIterable<T> {
final Iterable<S> _source;
final _Transformation<S, T> _f;
MappedListIterable(this._source, this._f);
int get length => _source.length;
T elementAt(int index) => _f(_source.elementAt(index));
}
This kind of optimizations is very much hidden from the end-user of the Dart programming language since they are really not important to know about for most users and most applications. But also because this things can change between releases since they are not part of the Dart SDK API description.
So when we are doing a performance benchmark, it does matter what we are doing with the List before calling map(). Also, the performance difference between the two suggested solutions in the question, might be so small that it really does not matter.
But we can try make the following naive performance benchmark of the two ways of doing mapping into a new List. The benchmark could be better written to ensure proper warmup of the Dart VM (at least for the first test results). But since it is the later results that is more interresting, I did not really care enough of doing proper warmup):
void mapToListTest(List<int> data) {
final stopwatch = Stopwatch()..start();
// The tested operation
final newData = data.map((e) => e 1).toList();
stopwatch.stop();
print('| map().toList() | '
'${data.length.toString().padLeft(9)} | '
'${newData.reduce((a, b) => a b).toString().padLeft(16)} | '
'${stopwatch.elapsed} |');
}
void mapInCollectionTest(List<int> data) {
final stopwatch = Stopwatch()..start();
// The tested operation
final newData = [for (final number in data) number 1];
stopwatch.stop();
print('| in collection | '
'${data.length.toString().padLeft(9)} | '
'${newData.reduce((a, b) => a b).toString().padLeft(16)} | '
'${stopwatch.elapsed} |');
}
void main() {
print(' ---------------- ----------- ------------------ ---------------- ');
print('| TYPE | SIZE | SUM | DURATION |');
print(' ---------------- ----------- ------------------ ---------------- ');
for (var dataSize = 1; dataSize < 1000000000; dataSize *= 10) {
final data = List.generate(dataSize, (index) => index);
for (var i = 0; i < 5; i ) {
mapToListTest(data);
mapInCollectionTest(data);
}
print(' ---------------- ----------- ------------------ ---------------- ');
}
}
The result from running on my old laptop (Arch Linux, i5-2410M, Dart 2.15.1 64 bit) is the following:
Running in Dart VM
---------------- ----------- ------------------ ----------------
| TYPE | SIZE | SUM | DURATION |
---------------- ----------- ------------------ ----------------
| map().toList() | 1 | 1 | 0:00:00.002231 |
| in collection | 1 | 1 | 0:00:00.000115 |
| map().toList() | 1 | 1 | 0:00:00.000011 |
| in collection | 1 | 1 | 0:00:00.000012 |
| map().toList() | 1 | 1 | 0:00:00.000005 |
| in collection | 1 | 1 | 0:00:00.000003 |
| map().toList() | 1 | 1 | 0:00:00.000004 |
| in collection | 1 | 1 | 0:00:00.000003 |
| map().toList() | 1 | 1 | 0:00:00.000003 |
| in collection | 1 | 1 | 0:00:00.000003 |
---------------- ----------- ------------------ ----------------
| map().toList() | 10 | 55 | 0:00:00.000005 |
| in collection | 10 | 55 | 0:00:00.000020 |
| map().toList() | 10 | 55 | 0:00:00.000009 |
| in collection | 10 | 55 | 0:00:00.000007 |
| map().toList() | 10 | 55 | 0:00:00.000008 |
| in collection | 10 | 55 | 0:00:00.000007 |
| map().toList() | 10 | 55 | 0:00:00.000007 |
| in collection | 10 | 55 | 0:00:00.000007 |
| map().toList() | 10 | 55 | 0:00:00.000006 |
| in collection | 10 | 55 | 0:00:00.000007 |
---------------- ----------- ------------------ ----------------
| map().toList() | 100 | 5050 | 0:00:00.000027 |
| in collection | 100 | 5050 | 0:00:00.000046 |
| map().toList() | 100 | 5050 | 0:00:00.000036 |
| in collection | 100 | 5050 | 0:00:00.000051 |
| map().toList() | 100 | 5050 | 0:00:00.000039 |
| in collection | 100 | 5050 | 0:00:00.000047 |
| map().toList() | 100 | 5050 | 0:00:00.000038 |
| in collection | 100 | 5050 | 0:00:00.000040 |
| map().toList() | 100 | 5050 | 0:00:00.000035 |
| in collection | 100 | 5050 | 0:00:00.000039 |
---------------- ----------- ------------------ ----------------
| map().toList() | 1000 | 500500 | 0:00:00.000244 |
| in collection | 1000 | 500500 | 0:00:00.000327 |
| map().toList() | 1000 | 500500 | 0:00:00.000364 |
| in collection | 1000 | 500500 | 0:00:00.000510 |
| map().toList() | 1000 | 500500 | 0:00:00.000418 |
| in collection | 1000 | 500500 | 0:00:00.000430 |
| map().toList() | 1000 | 500500 | 0:00:00.000107 |
| in collection | 1000 | 500500 | 0:00:00.000205 |
| map().toList() | 1000 | 500500 | 0:00:00.000110 |
| in collection | 1000 | 500500 | 0:00:00.000232 |
---------------- ----------- ------------------ ----------------
| map().toList() | 10000 | 50005000 | 0:00:00.002584 |
| in collection | 10000 | 50005000 | 0:00:00.001265 |
| map().toList() | 10000 | 50005000 | 0:00:00.000445 |
| in collection | 10000 | 50005000 | 0:00:00.000863 |
| map().toList() | 10000 | 50005000 | 0:00:00.000690 |
| in collection | 10000 | 50005000 | 0:00:00.000148 |
| map().toList() | 10000 | 50005000 | 0:00:00.000448 |
| in collection | 10000 | 50005000 | 0:00:00.000148 |
| map().toList() | 10000 | 50005000 | 0:00:00.000447 |
| in collection | 10000 | 50005000 | 0:00:00.000124 |
---------------- ----------- ------------------ ----------------
| map().toList() | 100000 | 5000050000 | 0:00:00.002150 |
| in collection | 100000 | 5000050000 | 0:00:00.002659 |
| map().toList() | 100000 | 5000050000 | 0:00:00.002284 |
| in collection | 100000 | 5000050000 | 0:00:00.002345 |
| map().toList() | 100000 | 5000050000 | 0:00:00.002044 |
| in collection | 100000 | 5000050000 | 0:00:00.002486 |
| map().toList() | 100000 | 5000050000 | 0:00:00.005122 |
| in collection | 100000 | 5000050000 | 0:00:00.002441 |
| map().toList() | 100000 | 5000050000 | 0:00:00.002061 |
| in collection | 100000 | 5000050000 | 0:00:00.002195 |
---------------- ----------- ------------------ ----------------
| map().toList() | 1000000 | 500000500000 | 0:00:00.021990 |
| in collection | 1000000 | 500000500000 | 0:00:00.019350 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.020505 |
| in collection | 1000000 | 500000500000 | 0:00:00.019342 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.020572 |
| in collection | 1000000 | 500000500000 | 0:00:00.019332 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.020004 |
| in collection | 1000000 | 500000500000 | 0:00:00.019439 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.019764 |
| in collection | 1000000 | 500000500000 | 0:00:00.019556 |
---------------- ----------- ------------------ ----------------
| map().toList() | 10000000 | 50000005000000 | 0:00:00.208943 |
| in collection | 10000000 | 50000005000000 | 0:00:00.388985 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.268663 |
| in collection | 10000000 | 50000005000000 | 0:00:00.396681 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.281344 |
| in collection | 10000000 | 50000005000000 | 0:00:00.377662 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.289034 |
| in collection | 10000000 | 50000005000000 | 0:00:00.348472 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.312922 |
| in collection | 10000000 | 50000005000000 | 0:00:00.367163 |
---------------- ----------- ------------------ ----------------
| map().toList() | 100000000 | 5000000050000000 | 0:00:02.299466 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.320316 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:02.510911 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.226951 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:02.505292 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.202681 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:02.520128 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.224866 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:02.499601 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.262451 |
---------------- ----------- ------------------ ----------------
Running when compiled: dart compile exe
---------------- ----------- ------------------ ----------------
| TYPE | SIZE | SUM | DURATION |
---------------- ----------- ------------------ ----------------
| map().toList() | 1 | 1 | 0:00:00.000014 |
| in collection | 1 | 1 | 0:00:00.000001 |
| map().toList() | 1 | 1 | 0:00:00.000001 |
| in collection | 1 | 1 | 0:00:00.000000 |
| map().toList() | 1 | 1 | 0:00:00.000000 |
| in collection | 1 | 1 | 0:00:00.000000 |
| map().toList() | 1 | 1 | 0:00:00.000001 |
| in collection | 1 | 1 | 0:00:00.000000 |
| map().toList() | 1 | 1 | 0:00:00.000000 |
| in collection | 1 | 1 | 0:00:00.000000 |
---------------- ----------- ------------------ ----------------
| map().toList() | 10 | 55 | 0:00:00.000001 |
| in collection | 10 | 55 | 0:00:00.000000 |
| map().toList() | 10 | 55 | 0:00:00.000001 |
| in collection | 10 | 55 | 0:00:00.000000 |
| map().toList() | 10 | 55 | 0:00:00.000001 |
| in collection | 10 | 55 | 0:00:00.000000 |
| map().toList() | 10 | 55 | 0:00:00.000001 |
| in collection | 10 | 55 | 0:00:00.000000 |
| map().toList() | 10 | 55 | 0:00:00.000001 |
| in collection | 10 | 55 | 0:00:00.000000 |
---------------- ----------- ------------------ ----------------
| map().toList() | 100 | 5050 | 0:00:00.000004 |
| in collection | 100 | 5050 | 0:00:00.000003 |
| map().toList() | 100 | 5050 | 0:00:00.000004 |
| in collection | 100 | 5050 | 0:00:00.000003 |
| map().toList() | 100 | 5050 | 0:00:00.000005 |
| in collection | 100 | 5050 | 0:00:00.000007 |
| map().toList() | 100 | 5050 | 0:00:00.000004 |
| in collection | 100 | 5050 | 0:00:00.000010 |
| map().toList() | 100 | 5050 | 0:00:00.000004 |
| in collection | 100 | 5050 | 0:00:00.000006 |
---------------- ----------- ------------------ ----------------
| map().toList() | 1000 | 500500 | 0:00:00.000033 |
| in collection | 1000 | 500500 | 0:00:00.000033 |
| map().toList() | 1000 | 500500 | 0:00:00.000037 |
| in collection | 1000 | 500500 | 0:00:00.000035 |
| map().toList() | 1000 | 500500 | 0:00:00.000037 |
| in collection | 1000 | 500500 | 0:00:00.000033 |
| map().toList() | 1000 | 500500 | 0:00:00.000126 |
| in collection | 1000 | 500500 | 0:00:00.000162 |
| map().toList() | 1000 | 500500 | 0:00:00.000038 |
| in collection | 1000 | 500500 | 0:00:00.000033 |
---------------- ----------- ------------------ ----------------
| map().toList() | 10000 | 50005000 | 0:00:00.000323 |
| in collection | 10000 | 50005000 | 0:00:00.000355 |
| map().toList() | 10000 | 50005000 | 0:00:00.000299 |
| in collection | 10000 | 50005000 | 0:00:00.000313 |
| map().toList() | 10000 | 50005000 | 0:00:00.000313 |
| in collection | 10000 | 50005000 | 0:00:00.000312 |
| map().toList() | 10000 | 50005000 | 0:00:00.000316 |
| in collection | 10000 | 50005000 | 0:00:00.000328 |
| map().toList() | 10000 | 50005000 | 0:00:00.000301 |
| in collection | 10000 | 50005000 | 0:00:00.001161 |
---------------- ----------- ------------------ ----------------
| map().toList() | 100000 | 5000050000 | 0:00:00.002970 |
| in collection | 100000 | 5000050000 | 0:00:00.003492 |
| map().toList() | 100000 | 5000050000 | 0:00:00.003719 |
| in collection | 100000 | 5000050000 | 0:00:00.003331 |
| map().toList() | 100000 | 5000050000 | 0:00:00.003188 |
| in collection | 100000 | 5000050000 | 0:00:00.002915 |
| map().toList() | 100000 | 5000050000 | 0:00:00.004179 |
| in collection | 100000 | 5000050000 | 0:00:00.003636 |
| map().toList() | 100000 | 5000050000 | 0:00:00.003345 |
| in collection | 100000 | 5000050000 | 0:00:00.003050 |
---------------- ----------- ------------------ ----------------
| map().toList() | 1000000 | 500000500000 | 0:00:00.029963 |
| in collection | 1000000 | 500000500000 | 0:00:00.024231 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.028978 |
| in collection | 1000000 | 500000500000 | 0:00:00.023922 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.028311 |
| in collection | 1000000 | 500000500000 | 0:00:00.023876 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.028449 |
| in collection | 1000000 | 500000500000 | 0:00:00.023879 |
| map().toList() | 1000000 | 500000500000 | 0:00:00.028365 |
| in collection | 1000000 | 500000500000 | 0:00:00.024178 |
---------------- ----------- ------------------ ----------------
| map().toList() | 10000000 | 50000005000000 | 0:00:00.328268 |
| in collection | 10000000 | 50000005000000 | 0:00:00.386397 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.313756 |
| in collection | 10000000 | 50000005000000 | 0:00:00.375381 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.340152 |
| in collection | 10000000 | 50000005000000 | 0:00:00.363960 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.363968 |
| in collection | 10000000 | 50000005000000 | 0:00:00.364880 |
| map().toList() | 10000000 | 50000005000000 | 0:00:00.323537 |
| in collection | 10000000 | 50000005000000 | 0:00:00.418280 |
---------------- ----------- ------------------ ----------------
| map().toList() | 100000000 | 5000000050000000 | 0:00:03.089848 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.745593 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:03.271331 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.632665 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:03.216420 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.561714 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:03.272032 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.571876 |
| map().toList() | 100000000 | 5000000050000000 | 0:00:03.286417 |
| in collection | 100000000 | 5000000050000000 | 0:00:03.622480 |
---------------- ----------- ------------------ ----------------
So based on this we can conclude that for this specific test, the map().toList() is faster than doing the mapping inside a collection declaration with a for-loop. But we can also conclude that the size of the source-list needs to be kinda huge before seeing any meaningful difference.
Again, this benchmark should NOT be used to determine what is best in real-life applications. Especially since the performance difference is so small and can change hugely depending on what we are doing. E.g. if we instead compare the following:
final newData = data.where((e) => e.isEven).map((e) => e 1).toList();
final newData = [for (final number in data) if (number.isEven) number 1];
The result is reversed (I skipped the first parts of the result and only posted the result for the largest lists):
Running in Dart VM
---------------- ----------- ------------------ ----------------
| TYPE | SIZE | SUM | DURATION |
---------------- ----------- ------------------ ----------------
| map().toList() | 100000000 | 2500000000000000 | 0:00:03.498376 |
| in collection | 100000000 | 2500000000000000 | 0:00:01.979149 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:03.369444 |
| in collection | 100000000 | 2500000000000000 | 0:00:01.931808 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:03.335894 |
| in collection | 100000000 | 2500000000000000 | 0:00:01.950542 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:03.386153 |
| in collection | 100000000 | 2500000000000000 | 0:00:01.959559 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:03.307532 |
| in collection | 100000000 | 2500000000000000 | 0:00:01.972022 |
---------------- ----------- ------------------ ----------------
Running when compiled: dart compile exe
---------------- ----------- ------------------ ----------------
| map().toList() | 100000000 | 2500000000000000 | 0:00:04.820222 |
| in collection | 100000000 | 2500000000000000 | 0:00:02.577894 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:04.636095 |
| in collection | 100000000 | 2500000000000000 | 0:00:02.631294 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:04.661244 |
| in collection | 100000000 | 2500000000000000 | 0:00:02.585229 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:04.704841 |
| in collection | 100000000 | 2500000000000000 | 0:00:02.631487 |
| map().toList() | 100000000 | 2500000000000000 | 0:00:04.646802 |
| in collection | 100000000 | 2500000000000000 | 0:00:02.551113 |
---------------- ----------- ------------------ ----------------
So do whatever that makes your code more readable and if you notice a performance issue, track down the specific place where it happens and analyze what can be done.
CodePudding user response:
Using this is more perfomant:
[
for(var item in items)
...
]
As also recommended by effective dart Here
To test you could try running:
main() {
var before = DateTime.now();
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0].map((e) => e).toList();
var difference = DateTime.now().difference(before).inMicroseconds;
print(difference);
}
And also try running:
main() {
var before = DateTime.now();
[
for(var item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 0])
item
];
var difference = DateTime.now().difference(before).inMicroseconds;
print(difference);
}
To see the difference in time.
