Is there any performance benefit of using an unmodifiable list instead of a fixed-length list in Dart?

Viewed 242

I was thinking if I should use a fixed-length list or an unmodifiable list if I know that I won't need to change the data or add/remove any elements from the list.

I would think that an unmodifiable list is more efficient, because we know that the items in the list won't change, so maybe the compiler can optimize something for us. However, in this github issue they mention that unmodifiable lists aren't used that often because of the performance overhead. What is this overhead? Is it only related to the construction of the new unmodifiable list (when we need to copy the items) or there is some more underlying overhead? If it's only in the construction then can we avoid it by using UnmodifiableListView() instead of List.unmodifiable()?

1 Answers

Different platforms might differ on the implementation of unmodifiable lists, but I don't think the issue here is affected by that.

I don't think there is any significant performance difference between fixed-length lists and unmodifiable lists, as long as you make sure that it's the only list you read at a particular point in your code.

A growable list is implemented by an outer wrapper object containing a fixed-length list as backing store, and when the backing store is filled, it's replaced by a bigger one, and the existing elements are copied over. That's true for both the VM and the JavaScript Array used when compiled to JavaScript. That means that it has an extra layer of indirection, when you call list[1], it passes that through to _innerList[1].

The VM's fixed length list and unmodifiable lists do not have a wrapper object, they have room for the precise number of elements they need. When calling a read method, they should do exactly the same thing, they only differ on writes. They should be marginally faster than a growable list for reading.

One thing which can make the VM slower is polymorphism, where objects of different types reach the same method call, because then the JIT optimizer cannot choose to call the method directly (if it's always the same type, then it only needs to look up the function once, if not, it needs to do it every time). If you ensure that your method invocations only ever get one of a growable list, fixed length list or unmodifiable list, then that won't be an issue.

The same thing applies to JavaScript compiled code, because the JavaScript engine is also doing JIT optimizations, and is likely to make similar choices on the JS objects.

Where you can get a problem is if you generally use fixed length lists, and then you use just one unmodifiable list somewhere, but it then makes every call site it reaches polymorphic. It's not a big problem, but if it happens inside your performance critical inner loop, then you might be able to see it.

Related