Open ranquild opened 7 years ago
@rakudrama @jmesserly Any thoughts on this? While this issue seems insignificant, List iteration/manipulation performance is major issue in our code. In most of times code that work with List ends up with calling interceptor methods for all operations.
I think access to the built-in List types would be useful. I see the what you describe in many places.
@floitschG This is a question for the Libraries team. Whatever we do, we need to support it widely to permit sharing of business logic between Dart web apps and Flutter mobile apps.
One problem is that on the VM there are several types (growable, fixed-length and immutable (const-like) Lists are three different types), but in JavaScript and dart2js there is one (JavaScript Array is used for all variants).
Another problem is that the benefits might be transient. Some library might implement a fake version of that type, bringing us back to there being many types and a need for getInceptor, e.g.
class MockArrayList<T> implements ArrayList<T> { ... }
There are coding styles that can help dart2js's type analysis. One is to use final fields and initialize them to the new list
class ... {
final _list = []; // Trivial inference.
It feels counterintuitive, but you might get better inference results from wrapping arrays to exploit the above trivial inference.
One thing we are considering is specializing the library code for (JS)Array. Today if you write a.map(transform).toList()
, map
is implemented by MappedListIterable
, which uses ListIterator
. In a nontrivial program, ListIterator
is used for many kinds of list, so ListIterator.moveNext
ends up using getInterceptor
for each call. If JSArray had its own map()
iterable, say ArrayMapIterable
, then ArrayMapIterable.toList
could directly index the Array. You would get this benefit even if one getInterceptor call was needed at a.map
to get 'into' the specialized code.
I'm strongly in favor of this, for what it's worth.
EDIT: though we would want this type to be non-implementable/non-extensible, similar to some of the existing dart:core types.
+1.
The other option in my mind is creating a library of JS-level primitives for optimization:
/// A `List`-like object guaranteed to be 1:1 mapped to a JavaScript `Array`.
///
/// May be used internally in frameworks that are browser-only in order to avoid
/// interceptors and other de-optimized code in Dart to JavaScript compilers that
/// needs to support polymorphic Dart `List` types.
abstract class JSArray<T> {
// Assume compilers can know new JSArray([1, 2, 3]) really means [1, 2, 3]
external JSArray(List<T> from);
T operator[](int index);
void operator[]=(int index, T value);
List<T> toDartList();
}
Here are other silly ideas: https://github.com/dart-lang/sdk/issues/30870
Another option that would work for us is new primitive Array
type, that will not implement List
and will work like in C# or Java.
I'm interested in someone doing an experiment to see how much we can get from wrappers which help dart2js with inference. I don't have the capacity to follow up on this until next year.
This library contains Array
(fixed length) and ArrayList
(growable) classes that should always be nicely inferred by dart2js provided you change various types in your program from List
to one of these types. This should be pretty close to what you would get from the proposed special type known to the compiler.
tl;dr: I don't see this happening in the core platform libraries.
Most of the optimizations here are really JavaScript only, and would detract from the non-JavaScript APIs. A JSArray
class would fit in well in JS-interop, but wouldn't make sense on the VM.
We keep the implementation classes for List
(and other general interfaces) private because we want to reserve the ability to change them in the future. The VM can introduce a new DenseHashMap
implementation at any point.
We could introduce an interface like PlatformList
which is implemented only by the platform list implementations (well, some of them, probably not the one returned by List.codeUnits
).
It wouldn't differ from List
at all, so it's really just a marker interface. We try to avoid those, since they separate classes into two groups without any functional reason behind it (same interface). You could call it a "class hierarchy fragmentation" issue.
The second someone makes a function which only accepts platform lists, we have now introduced a new mental overhead for everybody using lists. Should I accept only a platform list or a general list? If I accept any list, I can't call that fancy function. If I only accept platform lists, everybody else now needs to consider the same when calling me.
The feature just doesn't carry its own weight, as measured in complexity for users.
Long ago, in 2012, we briefly had a Sequence
interface which was basically class with length and integer indexing
(and both String
and List
were sequences). Even that was not considered worth its own weight, and was removed again after ~1 month.
Currently there is no way to specify type that is "default" List implementation. Dart2js uses type inference for optimizations, however in real world it is unable infer type in most cases. What I suggest is to add unextandable ArrayList type in dart:collection that dart2js will know about.