Neste vídeo recente de Nick Chapsas, no final ele comentou que o Find
método on List<T>
era "não otimizado" porque o código não itera no Span do array, mas no próprio array:
public T? Find(Predicate<T> match) {
if (match == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
for (int i = 0; i < _size; i++) {
if (match(_items[i])) {
return _items[i];
}
}
return default;
}
Achei que a iteração em spans poderia ser feita tão rápido quanto em arrays (mas não mais rápido) a partir deste parágrafo da postagem do blog (atualizada?) de Stephen Toub sobre Spans:
O código assembly é tão similar em parte por causa da eliminação de verificações de limites. Mas também é relevante o reconhecimento do JIT do indexador span como um intrínseco, o que significa que o JIT gera código especial para o indexador, em vez de traduzir seu código IL real para assembly.
Tudo isso serve para ilustrar que o tempo de execução pode aplicar aos spans os mesmos tipos de otimizações que aplica aos arrays , tornando os spans um mecanismo eficiente para acessar dados.
Quando se tornou evidente que iterar no Span de um array seria tão rápido e talvez mais rápido do que apenas iterar no próprio array? Qual versão do .NET introduziu essas otimizações de JIT Span que não podiam ser usadas para arrays?
A iteração em um intervalo tem duas vantagens aqui:
Se não estivesse
_size
também em jogo, seria possível consertar o código; por exemplo:simplesmente elevar
_arr
para um local permite que o JIT elimine as verificações de limites, porque a matriz não pode ser reatribuída :Note também que ambos
foreach (var x in _arr)
eforeach (var x in arr)
seriam bons e permitiriam que as verificações de limites fossem elididas. No entanto, o chato_size
significa que as verificações de limites não podem ser elididas, e se o_size
não corresponder_arr.Length
(buffers superdimensionados), não podemos usar simplesforeach
. Span corrige isso indiretamente, porque o intervalo é limitado; criar um intervalo de comprimento_size
significa que, uma vez que o intervalo tenha sido construído (com validação de limites no construtor), o JIT pode confiar nele e elidir as verificações de limites.