Desempenho na Web: Quando a resolução de milissegundos não é suficiente

Às vezes, medir o tempo em uma resolução de milissegundos não é suficientemente preciso. Juntamente com os líderes da comunidade e do setor, o grupo de trabalho de desempenho na Web do W3C trabalhou para solucionar esse problema, padronizando a especificação Tempo de alta resolução. Essa especificação foi publicada nesta semana como uma PR (Recomendação proposta) e é amplamente adotada em navegadores modernos. Veja a demonstração de test drive What Time is it? para ver como funciona essa API.

De uma simples ideia, essa especificação se tornou uma PR em apenas oito meses. O estágio PR da padronização é a última etapa antes de um padrão da Web se tornar uma Recomendação oficial do W3C. Além disso, essa interface foi amplamente adotada nos navegadores, incluindo o suporte completo no Internet Explorer 10 e no Firefox 15, e o suporte com um prefixo no Chrome 22. Trata-se de um ótimo exemplo do que é possível fazer quando o setor e a comunidade se unem por meio do W3C.

Então, por que os milissegundos não são bons o suficiente? Há muito que o tempo é medido na plataforma Web com o uso de alguns formulários do objeto JavaScript Date, seja por meio do método Date.now() ou do tipo DOMTimeStamp. O objeto Date representa um valor temporal em milissegundos desde 1º de janeiro de 1970 UTC. Para a maioria das finalidades práticas, essa definição de tempo é suficiente para representar qualquer instante dentro de 285.616 anos a partir de 1º de janeiro de 1970 UTC.

Por exemplo, no momento da escrita desta postagem, o meu valor temporal Date.now() no console de ferramentas de desenvolvedor do IE10 era 1350509874902. Esse número de treze dígitos representa o número de milissegundos a partir do início dessa base de tempo, 1º de janeiro de 1970. Esse horário corresponde a 17 de outubro de 2012 21:37:54 UTC.

Embora essa definição continue sendo realmente útil para determinar o horário do calendário atual, há alguns casos em que ela não é suficiente. Por exemplo, é útil para o desenvolvedor determinar se uma animação está sendo executada de forma suave a 60 quadros por segundo (um quadro a cada 16,667 milissegundos). Com o método simples de calcular os quadros/s instantâneos medindo quando ocorreu o último retorno de chamada de desenho de quadros, somente é possível determinar quadros/s como 58,8 (1/17) ou 62,5 quadros/s (1/16).

Da mesma forma, a resolução de submilissegundos também é desejável na medição precisa do tempo decorrido (por exemplo, usar as APIs de tempo de navegação, tempo de recurso e tempo do usuário para instrumentar o seu tempo de script e de rede) ou na tentativa de sincronizar cenas da animação ou o áudio com a animação.

Para resolver esse problema, a especificação Tempo de alta resolução define uma nova base de tempo com pelo menos uma resolução de microssegundo (um milésimo de um milissegundo). Para reduzir o número de bits usados para representar esse número e aumentar a legibilidade, em vez de medir o tempo a partir de 1º de janeiro de 1970 UTC, essa nova base de tempo mede o tempo a partir do início da navegação do documento, performance.timing.navigationStart.

A especificação define performance.now() como o método análogo de Date.now() para determinar o horário atual em alta resolução. O DOMHighResTimeStamp é o tipo análogo para DOMTimeStamp que define o valor de tempo de alta resolução.

Por exemplo, ao olhar o horário atual usando performance.now() e Date.now() no console de ferramentas de desenvolvedor do IE10 no momento da escrita desta postagem, vejo estes dois valores:

 performance.now():        196.304879519774
Date.now():        1350509874902

Embora esses valores de tempo representem a mesma instância no tempo, eles estão sendo medidos de uma origem diferente. O valor temporal performance.now() definitivamente parece mais legível.

Como o Tempo de alta resolução é medido do início da navegação de um documento, performance.now() em um subdocumento será medido a partir do início da navegação do subdocumento, não do documento raiz. Por exemplo, suponhamos que um documento tenha o iframe A de mesma origem e o iframe B de origens diferentes, e uma navegação tenha ocorrido no iframe A cerca de 5 milissegundos após o início da navegação do documento raiz e no iframe B cerca de 10 milissegundos após o início da navegação do documento raiz. Se medirmos o momento exato do horário 15 milissegundo após o início da navegação do documento raiz, obteríamos os seguintes valores para chamadas do performance.now() nos contextos diferentes:

performance.now() no iframe B:                                5,123 ms

performance.now() no iframe A:                               10,123 ms

performance.now() no documento raiz:                    15,123 ms

Date.now() em qualquer contexto:                         134639846051 ms

Figura ilustrando a diferença da medição com Date.now() em relação a performance.now()
Figura: Date.now() mede o tempo a partir de 1º de janeiro de 1970, enquanto performance.now() mede o tempo a partir do início da navegação do documento

Esse modelo não apenas garante que não haja vazamento de dados no momento da criação do pai em iframes de origens diferentes, ele também permite que você meça o tempo com relação ao seu início. Por exemplo, se você estivesse usando a interface do Tempo de recurso (que usa o Tempo de alta resolução) para determinar quanto tempo leva para um servidor responder a uma solicitação de recurso em um subdocumento, não seria necessário fazer ajustes para considerar o tempo da adição do subdocumento ao documento raiz.

Para fazer comparações entre tempos de quadro, seria necessário solicitar top.performance.now() para obter um valor temporal relacionado ao início da navegação do documento raiz, que retornaria o mesmo valor em todos os iframes com a mesma origem.

Outra vantagem importante dessa API em relação ao Date.now() é que performance.now() aumenta de maneira uniforme e não de acordo com o ajuste ou defasagem do relógio. A diferença entre chamadas subsequentes para performance.now() nunca será negativa. Date.now() não oferece essa garantia e, na prática, ouvimos falar de casos em que horários nulos foram vistos em dados analíticos.

O Tempo de alta resolução é outro ótimo exemplo de quão rapidamente novas ideias podem se tornar padrões interoperáveis nos quais os desenvolvedores podem confiar em navegadores modernos habilitados para HTML5. Agradecemos a todos do grupo de trabalho de desempenho da Web do W3C por ajudarem a desenvolver essas APIs e a outros fornecedores de navegador por implementarem essas APIs rapidamente buscando a interoperabilidade.

Obrigado,
Jatinder Mann
gerente de programas do Internet Explorer