Os JITers JavaScript modernos precisam de um cache de tamanho de array em loops?

Eu acho a prática de armazenar em cache uma propriedade de length de array dentro de um loop for bastante desagradável. Como em,

 for (var i = 0, l = myArray.length; i < l; ++i) { // ... } 

Pelo menos aos meus olhos, isso prejudica muito a legibilidade em comparação com o simples

 for (var i = 0; i < myArray.length; ++i) { // ... } 

(para não mencionar que ele vaza outra variável para a function circundante devido à natureza do escopo léxico e içamento).

Eu gostaria de dizer a qualquer um que fizer isso “não se incomode: os JS JITs modernos otimizam esse truque”. Obviamente, não é uma otimização trivial, já que você poderia, por exemplo, modificar o array enquanto ele está sendo iterado, mas eu acho que, considerando todas as coisas loucas que ouvi sobre os JITers e seus truques de análise de tempo de execução, eles teriam chegado a isso agora.

Alguém tem provas de uma forma ou de outra?

E sim, eu também gostaria que fosse suficiente dizer “isso é uma micro-otimização; não faça isso até o seu perfil”. Mas nem todo mundo ouve esse tipo de razão, especialmente quando se torna um hábito armazenar em cache o tamanho e eles acabam fazendo isso automaticamente, quase como uma escolha de estilo.

Depende de algumas coisas:

  • Se você já provou que seu código está gastando tempo significativo em looping
  • Se o navegador mais lento que você está suportando se beneficia do cache de tamanho de array
  • Se você ou as pessoas que trabalham em seu código encontram o comprimento do array em cache difícil de ler

Parece que dos benchmarks que eu vi (por exemplo, aqui e aqui ) que o desempenho no IE <9 (que geralmente são os navegadores mais lentos com os quais você tem de lidar) se beneficia do cache do tamanho do array, então pode valer a pena fazer . Por que vale a pena, eu tenho um hábito de longa data de armazenamento em cache do comprimento da matriz e como resultado, é fácil de ler. Há também outras otimizações de loop que podem ter um efeito, como contagem regressiva em vez de up.

Aqui está uma discussão relevante sobre isso na lista de discussão do JSMentors: http://groups.google.com/group/jsmentors/browse_thread/thread/526c1ddeccfe90f0

Meus testes mostram que todos os principais navegadores mais recentes armazenam em cache a propriedade length das matrizes. Você não precisa fazer o cache sozinho, a menos que esteja preocupado com o IE6 ou com o 7, não me lembro exatamente. No entanto, tenho usado outro estilo de iteração desde aqueles dias, uma vez que me fornece outro benefício, que descreverei no exemplo a seguir:

 var arr = ["Hello", "there", "sup"]; for (var i=0, str; str = arr[i]; i++) { // I already have the item being iterated in the loop as 'str' alert(str); } 

Você deve perceber que esse estilo de iteração pára se a matriz tiver permissão para conter valores ‘falsos’, portanto, esse estilo não pode ser usado nesse caso.

Primeiro de tudo, como isso é mais difícil de ser feito ou menos legível?

 var i = someArray.length; while(i--){ //doStuff to someArray[i] } 

Isso não é uma micro-otimização estranha e críptica. É apenas um princípio básico de evitação de trabalho. Não usando o ‘.’ ou ‘[‘] operadores mais do que o necessário devem ser tão óbvios quanto não recalcular pi mais de uma vez (supondo que você não saiba que já temos isso no object Math).

[elementos rancolors yoinked]

Se someArray é inteiramente interno a uma function, é um jogo justo para a otimização JIT de sua propriedade length, que é realmente como um getter que realmente conta os elementos do array toda vez que você o acessa. Um JIT pode ver que o escopo é inteiramente local e ignorar o comportamento real de contagem.

Mas isso envolve uma quantidade razoável de complexidade. Toda vez que você faz algo que muda o Array, você precisa tratar length como uma propriedade estática e informar seus methods de alteração de array (o lado nativo do código deles, quero dizer) para definir a propriedade manualmente, enquanto o comprimento apenas conta os itens toda vez que referenciado. Isso significa que toda vez que um novo método de alteração de matriz é adicionado, é necessário atualizar o JIT para o comportamento de ramificação para referências de comprimento de uma matriz com escopo local.

Eu pude ver o Chrome fazendo isso eventualmente, mas não acho que ainda seja baseado em alguns testes realmente informais. Não tenho certeza se o IE terá esse nível de ajuste de desempenho como prioridade. Quanto aos outros navegadores, você poderia fazer um argumento forte para o problema de manutenção de ter que ramificar o comportamento para cada novo método de matriz, sendo mais problemático do que o seu valor. No mínimo, não teria prioridade máxima.

Em última análise, acessar a propriedade length a cada ciclo de loop não vai custar uma tonelada, mesmo nos navegadores antigos, para um típico loop JS. Mas aconselho adquirir o hábito de armazenar em cache qualquer pesquisa de propriedade que seja feita mais de uma vez, pois com as propriedades getter você nunca pode ter certeza de quanto trabalho está sendo feito, quais navegadores otimizam de que forma ou que tipo de desempenho você pode reduzir a estrada quando alguém decide mover someArray para fora da function, o que poderia levar à verificação do object de chamada em uma dúzia de locais antes de encontrar o que está procurando toda vez que você acessa essa propriedade.

O armazenamento em cache de pesquisas de propriedades e retornos de methods é fácil, limpa seu código e, por fim, torna-o mais flexível e robusto em termos de desempenho em face da modificação. Mesmo que um ou dois JITs tornassem desnecessário em circunstâncias envolvendo um número de ‘ifs’, você não poderia ter certeza de que eles sempre fariam ou de que seu código continuaria a possibilitar isso.

Então, sim, desculpas pelo anti-let-the-compiler-handle-it rant, mas não vejo por que você nunca iria querer não armazenar suas propriedades em cache. É fácil. Está limpo. Garante um melhor desempenho, independentemente do navegador ou do movimento do object, tendo suas propriedades examinadas para um escopo externo.

Mas isso realmente me irrita quando os documentos do Word são carregados tão lentamente quanto em 1995 e que as pessoas continuam a escrever sites java horrendamente de baixo desempenho, embora a VM do Java supostamente supere todos os concorrentes não-compilados por desempenho. Eu acho que essa noção de que você pode deixar o compilador resolver os detalhes de desempenho e que “os computadores modernos são tão rápidos” tem muito a ver com isso. Devemos estar sempre atentos ao evitar o trabalho, quando o trabalho é fácil de evitar e não ameaça a legibilidade / manutenção, IMO. Fazê-lo de forma diferente nunca me ajudou (ou suspeito que alguém) escreve o código mais rapidamente a longo prazo.