Medição de desempenho Javascript

Medir o tempo necessário para executar uma função é uma boa maneira de provar que uma implementação de um mecanismo é mais produtiva que outra. Isso permite garantir que o desempenho da função não sofra após algumas alterações feitas no código. Também ajuda a procurar gargalos no desempenho do aplicativo.

Se um projeto da web tiver alto desempenho, isso contribui para a percepção positiva dos usuários. E se os usuários gostaram de trabalhar com o recurso, eles têm a propriedade de retornar. Por exemplo, nesteO estudo mostrou que 88% dos clientes on-line têm menos probabilidade de retornar aos recursos que encontram com qualquer inconveniente. Esses inconvenientes podem muito bem ser causados ​​por problemas de desempenho.

É por isso que as ferramentas para ajudar a encontrar gargalos de desempenho e medir os resultados das melhorias feitas no código são importantes no desenvolvimento da Web. Essas ferramentas são especialmente relevantes no desenvolvimento do JavaScript. É importante saber que todas as linhas de código JavaScript podem potencialmente bloquear o DOM, pois o JavaScript é uma linguagem de thread único. Neste artigo, falarei sobre como medir o desempenho das funções e o que fazer com os resultados da medição.





Se você acha que alguns cálculos são muito pesados ​​para serem executados no encadeamento principal, pode decidir movê-los para um trabalhador de serviço ou trabalhador da Web.

Método Performance.now ()


O desempenho de interface dá acesso a um valor do tipo DOMHighResTimeStamp através de um método performance.now(). Este método retorna um registro de data e hora indicando o tempo em milissegundos decorrido desde que o documento começou a existir. Além disso, a precisão desse indicador é de cerca de 5 microssegundos (frações de um milissegundo).

Para medir o desempenho de um fragmento de código usando o método performance.now(), é necessário executar duas medições de tempo, salvar os resultados dessas medições em variáveis ​​e subtrair os resultados da primeira dos resultados da segunda medição:

const t0 = performance.now();
for (let i = 0; i < array.length; i++) 
{
  // - 
}
const t1 = performance.now();
console.log(t1 - t0, 'milliseconds');

No Chrome, depois de executar esse código, você pode obter algo assim:

0.6350000001020817 "milliseconds"

No Firefox, assim:

1 milliseconds

Como você pode ver, os resultados das medições obtidas em diferentes navegadores são muito diferentes. O fato é que, no Firefox 60, a precisão dos resultados retornados pela API Performance é reduzida. Falaremos mais sobre isso.

A interface Performance possui muito mais recursos do que apenas retornar um determinado carimbo de data / hora. Estes incluem medir vários aspectos do desempenho representado por essas extensões desta interface como o desempenho Timeline API , sincronismo de navegação , tempo do usuário , sincronismo de recursos . Aqui está o material para descobrir mais sobre essas APIs.

No nosso caso, estamos falando em medir o desempenho de funções, portanto, temos oportunidades suficientes que o método ofereceperformance.now().

Date.now () e performance.now ()


Aqui você pode pensar que pode usar o método para medir o desempenho Date.now(). Isso é realmente possível, mas essa abordagem tem desvantagens.

O método Date.now()retorna o tempo em milissegundos decorrido desde a era Unix (1970-01-01T00: 00: 00Z) e depende do relógio do sistema. Isso significa não apenas que esse método não é tão preciso quanto performance.now(), mas que, ao contrário performance.now(), retorna valores que, sob certas condições, podem ser baseados em indicadores de relógio incorretos. Eis o que Rich Gentlekor, um programador relacionado ao mecanismo WebKit, diz: “Talvez os programadores tenham menos probabilidade de pensar que as leituras retornaram ao acessarDatecom base na hora do sistema, é absolutamente impossível chamar o ideal para monitorar aplicativos reais. A maioria dos sistemas possui um daemon que sincroniza regularmente o tempo. Ajustar o relógio do sistema por alguns milissegundos a cada 15 a 20 minutos é uma coisa comum. Com essa frequência, os ajustes do relógio em cerca de 1% das medições de intervalos de 10 segundos serão imprecisos ".

Método Console.time ()


A medição do tempo usando esta API é extremamente simples. Suficiente, antes do código cujo desempenho é preciso avaliar, chamar o método console.time(), e após este código - o método console.timeEnd(). Nesse caso, um e os outros métodos precisam passar o mesmo argumento de string. Em uma página, até 10.000 desses temporizadores podem ser usados ​​simultaneamente.

A precisão das medições de tempo feitas com esta API é a mesma que a API de desempenho, mas a precisão que será alcançada em cada situação específica depende do navegador.

console.time('test');
for (let i = 0; i < array.length; i++) {
  // - 
}
console.timeEnd('test');

Depois de executar esse código, o sistema emitirá automaticamente informações sobre o tempo decorrido para o console.

No Chrome, será algo parecido com isto:

test: 0.766845703125ms

No Firefox, assim:

test: 2ms - timer ended

De fato, tudo aqui é muito parecido com o que vimos enquanto trabalhamos performance.now().

A força do método console.time()é sua facilidade de uso. Ou seja, estamos falando sobre o fato de sua aplicação não exigir a declaração de variáveis ​​auxiliares e encontrar a diferença entre os indicadores registrados nelas.

Precisão de tempo reduzida


Se você, usando as ferramentas descritas acima, mediu o desempenho do seu código em diferentes navegadores, pode prestar atenção ao fato de que os resultados da medição podem variar.

A razão para isso é que os navegadores estão tentando proteger os usuários contra ataques com base no tempo e contra mecanismos de identificação do navegador ( impressão digital do navegador ). Se os resultados da medição do tempo forem muito precisos, isso pode dar aos atacantes a oportunidade, por exemplo, de identificar usuários.

No Firefox 60, como já mencionado, a precisão dos resultados da medição de tempo é reduzida . Isso é feito configurando o privacy.reduceTimerPrecisionvalor da propriedade para 2 ms.

Algo a ter em mente ao testar o desempenho


Agora você tem ferramentas à sua disposição para medir o desempenho das funções JavaScript. Mas antes de começar a trabalhar, você precisa considerar alguns dos recursos sobre os quais falaremos agora.

▍ Divida e conquiste


Suponha que, ao filtrar alguns dados, você preste atenção na operação lenta do aplicativo. Mas você não sabe exatamente onde está o gargalo de desempenho.

Em vez de especular sobre qual parte do código está sendo executada lentamente, seria melhor descobrir usando os métodos acima.

Para ver a imagem geral do que está acontecendo, primeiro você precisa usar console.time()e console.timeEnd()avaliar o desempenho do bloco de código, que, presumivelmente, tem um efeito ruim no desempenho. Então você precisa observar a velocidade das partes individuais desse bloco. Se um deles parecer visivelmente mais lento que os outros, você precisará prestar atenção especial a ele e como analisá-lo.

Quanto menos código houver entre as chamadas para métodos que medem o tempo, menor a probabilidade de que algo que não seja relevante para a situação do problema seja medido.

▍Tenha em consideração as características do comportamento das funções com diferentes valores de entrada


Em aplicações reais, os dados recebidos na entrada de uma função específica podem ser muito diferentes. Se você medir o desempenho de uma função que recebeu um conjunto de dados selecionado aleatoriamente, isso não fornecerá informações valiosas que possam esclarecer o que está acontecendo.

As funções ao pesquisar desempenho precisam ser chamadas com dados de entrada que se assemelhem aos reais, tanto quanto possível.

▍ Executar funções várias vezes


Suponha que você tenha uma função que itere sobre uma matriz. Ela realiza alguns cálculos usando cada elemento da matriz e, em seguida, retorna uma nova matriz com os resultados dos cálculos. Pensando em otimizar essa função, você quer saber o que funciona mais rapidamente na sua situação - um loop forEachou um loop regular for.

Aqui estão duas opções para esse recurso:

function testForEach(x) {
  console.time('test-forEach');
  const res = [];
  x.forEach((value, index) => {
    res.push(value / 1.2 * 0.1);
  });

  console.timeEnd('test-forEach')
  return res;
}

function testFor(x) {
  console.time('test-for');
  const res = [];
  for (let i = 0; i < x.length; i ++) {
    res.push(x[i] / 1.2 * 0.1);
  }

  console.timeEnd('test-for')
  return res;
}

Teste as funções:

const x = new Array(100000).fill(Math.random());
testForEach(x);
testFor(x);

Depois de executar o código, obtemos os seguintes resultados:

test-forEach: 27ms - timer ended
test-for: 3ms - timer ended

O ciclo parecia forEachser muito mais lento que o ciclo for. Afinal, os resultados dos testes indicam exatamente isso?

De fato, após um único teste, é muito cedo para tirar essas conclusões. Vamos tentar chamar as funções duas vezes:

testForEach(x);
testForEach(x);
testFor(x);
testFor(x);

Temos o seguinte:

test-forEach: 13ms - timer ended
test-forEach: 2ms - timer ended
test-for: 1ms - timer ended
test-for: 3ms - timer ended

Acontece que a função em que é usada forEach, chamada segunda vez, é tão rápida quanto a função em que é usada for. Mas, como a primeira forEachfunção é chamada, a função funciona muito mais devagar, mas pode não valer a pena usá-la.

Desempenho de teste em diferentes navegadores


Os testes acima foram realizados no Firefox. Mas e se você executá-los no Chrome? Os resultados serão completamente diferentes:

test-forEach: 6.156005859375ms
test-forEach: 8.01416015625ms
test-for: 4.371337890625ms
test-for: 4.31298828125ms

O fato é que os navegadores Chrome e Firefox são baseados em diferentes mecanismos JavaScript que implementam diferentes otimizações de desempenho. É muito útil conhecer essas diferenças.

Nesse caso, o Firefox tem uma otimização melhor forEachcom entradas semelhantes. E o ciclo foré mais rápido do que forEachno Chrome e no Firefox. Como resultado, provavelmente é melhor insistir na variante da função c for.

Este é um bom exemplo, demonstrando a importância de medir o desempenho em diferentes navegadores. Se você avaliar o desempenho de algum código apenas no Chrome, poderá concluir que o ciclo forEach, em comparação com o ciclo for, não é tão ruim.

▍ Aplicar limites artificiais aos recursos do sistema


Os valores obtidos em nossas experiências não parecem particularmente grandes. Mas lembre-se de que os computadores usados ​​para desenvolvimento geralmente são muito mais rápidos do que, digamos, o telefone celular comum no qual navegam na web.

Para se colocar no lugar de um usuário que não possui o dispositivo mais rápido, use os recursos do navegador para limitar artificialmente os recursos do sistema. Por exemplo - para reduzir o desempenho do processador.

Com essa abordagem, 10 ou 50 milissegundos podem facilmente se transformar em 500.

▍ Medir o desempenho relativo


As medições de desempenho geralmente dependem não apenas do hardware, mas também da carga atual do processador e da carga de trabalho do encadeamento principal do aplicativo JavaScript. Portanto, tente confiar em indicadores relativos que caracterizam a mudança no desempenho, pois os indicadores absolutos obtidos ao analisar o mesmo fragmento de código em momentos diferentes podem variar bastante.

Sumário


Neste artigo, vimos algumas APIs JavaScript projetadas para medir o desempenho. Nós conversamos sobre como usá-los para analisar o código real. Acredito que, para realizar algumas medições simples, é mais fácil de usar console.time().

Tenho a sensação de que muitos desenvolvedores de front-end não prestam atenção suficiente para medir o desempenho de seus projetos. E eles devem monitorar constantemente os indicadores relevantes, pois a produtividade afeta o sucesso e a lucratividade dos projetos.

Queridos leitores! Se você monitorar constantemente o desempenho de seus projetos, diga-nos como fazê-lo.


All Articles