Desenvolvimento

20 mar, 2015

Confusão séria com o “resource timing” ou “duration” bloqueante

Publicidade

O resource timing é uma ótima maneira de medir o quão rápido os recursos são baixados. Infelizmente, quase todo mundo com que falei faz isso usando o atributo “duration” e não está ciente de que “duração” inclui o tempo de bloqueio. Como resultado, os valores de tempo “duration”são (muito) maiores do que o tempo de download real, oferecendo aos desenvolvedores resultados inesperados. Isso é especialmente ruim para os recursos de origem cruzada, nos quais o “duration” é a única métrica disponível. Neste artigo, vou descrever o problema e uma proposta de solução.

Avaliação de resource timing

A especificação do resource timing define APIs para coletar métricas de tempo para cada recurso em uma página web. Ela está atualmente disponível no Chrome, Chrome para Android, IE 10-11 e Opera. Você pode reunir uma lista de objetos PerformanceEntry usando getEntries(), getEntriesByType() e getEntriesByName(). Um PerformanceEntry tem estas propriedades:

  • nome – a URL
  • entryType – “resource” típico
  • startTime – momento em que o recurso começou a ser processado (em milissegundos relativos à navegação de página)
  • duração – tempo total para processar o recurso (em milissegundos)

As propriedades acima estão disponíveis para todos os recursos – tanto de mesma origem quanto origem cruzada. No entanto, os recursos de mesma origem mesmo têm propriedades adicionais disponíveis, como definido pela interface PerformanceResourceTiming. Elas são autoexplicativas e ocorrem praticamente nesta ordem cronológica:

  • redirectStart
  • redirectEnd
  • fetchStart
  • domainLookupStart
  • domainLookupEnd
  • connectStart
  • connectEnd
  • secureConnectionStart
  • requestStart
  • responseStart
  • responseEnd

Aqui está o modelo de processamento gráfico canônico que mostra as diferentes fases. Note-se que “duration” é igual a (responseEnd – startTime).

resource-timing-overview

Dê uma olhada no meu artigo sobre Dicas Práticas dos Resouce Timing para obter mais informações sobre como utilizar o resource timing.

Bloqueio inesperado no “duration”

As propriedades detalhadas do PerformanceResourceTiming são restritas aos recursos de mesma origem por razões de privacidade (note que qualquer recurso pode ser “de mesma origem”, usando o cabeçalho de resposta Timing-Allow-Origin). Cerca de metade dos recursos em sites de hoje são de origem cruzada, então “duration” é a única forma de medir o seu tempo de carregamento. E mesmo para os recursos de mesma origem, “duration” é o único delta fornecido, presumivelmente porque é a fase mais importante para medir. Como resultado, todas as implementações resource timing que eu já vi utilizam “duration” como a métrica de desempenho primário.

Infelizmente, “duration”é mais do que o tempo de download. Ele também inclui “tempo de bloqueio” – o atraso entre quando o navegador percebe que precisa fazer download de um recurso e o tempo que ele realmente leva para começar a baixar o recurso. O bloqueio pode ocorrer em várias situações. A mais típica é quando existem mais recursos do que conexões TCP. A maioria dos navegadores só abrem seis conexões TCP por hostname, sendo as exceções IE10 (oito conexões), e IE11 (12 conexões).

Esta página de teste do Bloqueio de Resource Timing possui 16 imagens, por isso algumas delas incorrem em tempo de bloqueio, independentemente de qual navegador for usado. Cada uma das imagens é programada no servidor para ter um atraso de 1 segundo. O “startTime” e o “duration” são apresentados para cada uma das 16 imagens. Aqui estão os resultados WebPagetest para essa página de teste que está sendo carregada no Chrome, IE10 e IE11. Você pode olhar as imagens para ler os resultados de tempo. Note como “startTime” é aproximadamente a mesma para todas as imagens. Isso porque esse é o tempo que o navegador analisou a tag IMG e percebeu que precisava baixar o recurso. Mas os valores de “duration” aumentam nas etapas de ~ 1 segundo para as imagens que ocorrem no final da página. Isso porque eles estão impedidos de fazer o download com as imagens anteriores.

No Chrome, por exemplo, as imagens são transferidas em três conjuntos – porque o Chrome só faz downloads de seis recursos de cada vez. As seis primeiras imagens têm uma duração de ~ 1,3 segundo (o atraso de um segundo no backend somado à mais um segundo estabelecendo a conexão TCP e fazendo download do corpo da resposta). As próximas seis imagens têm uma a “duração” de ~ 2,5 segundos. As últimas quatro imagens têm uma a “duração” de ~ 3,7 segundos. O segundo conjunto está bloqueado para ~ 1 segundo de espera para o primeiro conjunto terminar. O terceiro conjunto está bloqueado para ~ 2 segundos de espera para conjuntos 1 e 2 até o fim.

Mesmo embora os valores da duração aumentem de 1 para 2 a 3 segundos, o tempo de transferência real para todas as imagens é de aproximadamente 1 segundo, como mostrado no gráfico waterfall WebPagetest.

rt-timing-waterfall

Os resultados são semelhantes para IE10 e IE11. IE10 começa com seis conexões TCP paralelas, mas, em seguida, pula para até oito conexões. IE11 também começa com seis conexões TCP paralelas, mas, em seguida, pula até a doze. IE10 e IE11 apresentam o mesmo problema – embora o tempo de carregamento de cada imagem seja de aproximadamente um segundo, a duração apresenta valores que variam de 1-3 segundos.

Proposta: “networkDuration”

É evidente que “duration” não é uma forma precisa de medir o tempo de carregamento de recursos, pois pode incluir o tempo de bloqueio (inclui também redirecionar tempo, mas isso ocorre com muito menos frequência). Infelizmente, “duration” é a única métrica disponível para os recursos de origem cruzada. Portanto, eu apresentei uma proposta para a lista de discussão de Desempenho Web do W3C para adicionar “networkDuration” ao resource timing. Isso estará disponível tanto para recursos de mesma origem quanto de origem cruzada (eu sou flexível quanto ao nome; outros candidatos incluem “networkTime”, “loadTime” etc.)

O cálculo para “networkDuration” está como se segue (suponha que “r” seja um objeto PerformanceResourceTiming):

dns = r.domainLookupEnd - r.domainLookupStart;
tcp = r.connectEnd - r.connectStart; // includes ssl negotiation
waiting = r.responseStart - r.requestStart;
content = r.responseEnd - r.responseStart;
networkDuration = dns + tcp + waiting + content;

Os desenvolvedores que trabalham com recursos de mesma origem podem fazer os mesmos cálculos para obter “networkDuration”, como mostrado acima – desde que o resultado como um novo atributo simplifique o processo. Isso também evita possíveis erros, já que é provável que as empresas e as equipes irão comparar esses valores. Por isso é importante assegurar uma comparação de igual para igual. Mas a necessidade primária do “networkDuration” é para os recursos de origem cruzada. Agora, “duration”é a única métrica disponível para recursos de origem cruzada. Eu encontrei várias equipes que estavam rastreando “duration”, assumindo que significava tempo de download. Elas ficaram surpresas quando expliquei que ele também incluiu o tempo de bloqueio, e concordaram que não era a métrica que queriam; em vez disso, queriam o equivalente a “networkDuration”.

Eu mencionei anteriormente que os valores de tempo de execução (domainLookupStart, connectStart etc.) estão restritos aos recursos de mesma origem por razões de privacidade. A proposta de acrescentar “networkDuration” é susceptível de levantar preocupações com a privacidade; principalmente porque, ao remover o tempo de bloqueio, “networkDuration” iria ativar um JavaScript de terceiros malicioso para determinar se um recurso foi lido a partir do cache. No entanto, é possível remover o tempo de bloqueio hoje usando “duration”, carregando um recurso quando não há contenção de bloqueio (por exemplo, depois de window.onload). Mesmo quando o tempo de bloqueio é removido, é ambíguo se um recurso foi lido a partir do cache ou carregado pela rede. Mesmo uma leitura de cache terá tempos de carregamento diferentes de zero.

O problema que o “networkDuration” resolve é encontrar o tempo de carregamento para mais recursos típicos que são carregados durante a criação da página e que podem, portanto, implicar em tempo de bloqueio.

Conclusão

Não é possível hoje utilizar resource timing para medir o tempo de carregamento de recursos de origem cruzada. As empresas que querem medir o tempo de carregamento e o tempo de bloqueio podem usar “duration”, mas todas as empresas com quem conversei querem medir o tempo de carga real (sem tempo de bloqueio). Para proporcionar melhores métricas de desempenho, encorajo a adição de “networkDuration” à especificação do resource timing.

***

Steve Souders parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://www.stevesouders.com/blog/2014/11/25/serious-confusion-with-resource-timing/