Vscode: Uso da CPU mesmo quando ocioso (devido à renderização do cursor)

Criado em 20 mar. 2017  ·  64Comentários  ·  Fonte: microsoft/vscode

  • Versão do VSCode: 1.10.2 (8076a19fdcab7e1fc1707952d652f0bb6c6db331)
  • Versão do sistema operacional: macOS Sierra 10.12.3

O VS Code usa 13% da CPU quando focado e inativo, esgotando a bateria. Isso provavelmente se deve à renderização do cursor piscando. Acho que o uso da CPU quando focado e ocioso poderia idealmente ser próximo a 0%.

Para reproduzir (isso funciona com um arquivo de configurações vazio e todos os plug-ins desativados):

  1. Feche todas as janelas do VS Code.
  2. Abra uma nova janela (Arquivo -> Nova Janela). Ele mostrará a página de boas-vindas.
  3. Abra uma nova guia com um arquivo sem título vazio (Arquivo -> Nova Guia). O cursor está piscando.
  4. Você deve ver o VS Code consumindo uma quantidade não desprezível de CPU - 13% no meu MacBook Pro de 13 ".
  5. Cmd + Tab em algum outro aplicativo. Agora o cursor não está mais visível.
  6. Você deve ver o VS Code consumindo virtualmente nenhuma CPU.

Gravei uma linha do tempo nas Ferramentas do desenvolvedor e uma olhada superficial sugere que a atividade da CPU vem da renderização do cursor piscante a cada 500 ms.

Outros aplicativos macOS, como Chrome ou TextEdit, mostram um cursor piscando sem consumir muito da CPU, então acho que isso certamente poderia ser otimizado.

bug editor-core perf

Comentários muito úteis

Uma solução alternativa para pessoas que são igualmente obcecadas com a vida da bateria: desativar o piscar do cursor fará com que o uso da CPU caia para 0. Esta é a configuração:

  "editor.cursorBlinking": "solid"

Todos 64 comentários

Uma solução alternativa para pessoas que são igualmente obcecadas com a vida da bateria: desativar o piscar do cursor fará com que o uso da CPU caia para 0. Esta é a configuração:

  "editor.cursorBlinking": "solid"

Algumas pessoas no Twitter disseram que não podiam reproduzir isso, então eu fui e gravei uma linha do tempo nas Ferramentas do desenvolvedor para ajudar a depurar.

TimelineRawData-20170321T114212.json.zip

  • Captura de tela da linha do tempo:

    boot mz0y1

  • Ampliando em um único quadro, vemos que enquanto renderizamos apenas 2 fps, o thread principal está realizando algum trabalho a 60 fps (a cada 16 ms) - as linhas finas marcadas com setas:

    boot 684m3

  • Ampliando totalmente em uma dessas linhas finas, vemos que alguma renderização está acontecendo a 60 fps:

    boot f9qau

  • Quando eu pego um perfil de CPU, ele mostra a maior parte da CPU sendo gasta em "(programa)", não em qualquer função JS específica.

    boot g2wbo

  • Quando defino "editor.cursorBlinking": "solid" , o problema praticamente desaparece: uma "árvore de camadas de atualização" / "Pintura" / "Camadas compostas" periódica ainda está acontecendo, mas apenas a cada 500 ms, não a cada 16 ms.

Eu espero que isso ajude!

@joliss Resposta rápida para o que está acontecendo nos bastidores: uma animação nativa é atualizada a 60 Hz no Chromium.

Problema semelhante (quase o mesmo) do Chromium https://bugs.chromium.org/p/chromium/issues/detail?id=500259 e item de rastreamento do Chromium https://bugs.chromium.org/p/chromium/issues/detail ? id = 361587

Nossa animação CSS atual

<strong i="11">@keyframes</strong> monaco-cursor-blink {
    50% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

.cursor-blink {
    animation: monaco-cursor-blink 1s step-start 0s infinite;
}

Atualizar

Citando Paul Irish (cara do Chrome) https://news.ycombinator.com/item?id=13941293

Editores de texto poderosos * construídos na pilha da web não podem contar com o circunflexo de texto do sistema operacional e têm que fornecer o seu próprio.
Nesse caso, o VSCode está provavelmente usando a abordagem mais razoável para piscar um cursor: uma função de tempo step com uma animação de quadro-chave CSS. Isso informa ao navegador para alterar a opacidade apenas a cada 500 ms. Enquanto isso, o Chrome ainda não otimizou isso completamente, portanto, http://crbug.com/361587.
Então, atualmente, o Chrome está fazendo todo o ciclo de vida de renderização (estilo, pintura, camadas) a cada 16 ms, quando deveria estar fazendo esse trabalho apenas em um intervalo de 500 ms. Estou confiante de que os engenheiros que trabalham nos componentes de estilo do Chrome podem resolver isso, mas vai dar um pouco de trabalho. Acho que a visibilidade adicionada neste tópico provavelmente aumentará a prioridade da correção. :)

  • Editores de texto simples e básicos construídos em [contenteditable] podem, mas raramente escalam para o conjunto de recursos mais desejado.

Esta mudança sobre o uso de CPU em segundo plano das guias de cromo afetará o editor?

Esperamos que não (consulte https://github.com/electron/electron/issues/7553). Nós definir backgroundThrottling a false uma vez um tempo já.

Obrigado @joliss @rebornix pela boa análise.

Parece que devemos voltar a usar setInterval no OSX.

ps Aqui está a lógica inicial de piscar do cursor :)
image

@rebornix Aqui está um problema semelhante que encontramos há algum tempo - https://bugs.chromium.org/p/chromium/issues/detail?id=658894. A solução alternativa em nosso caso foi remover o elemento de animação do DOM quando ele não era usado, em vez de apenas obstruí-lo.

Há também um estilo css para o mesmo, mas não tenho certeza se ele é usado aqui -

<strong i="6">@keyframes</strong> monaco-cursor-blink {
    50% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

https://github.com/Microsoft/vscode/blob/master/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css

Use a API pageVisibility , que permite saber quando a página está oculta para desativar a animação.

function handleVisibilityChange() {
  if (document.hidden) {
    // disable cursor animation with class name
  } 
  else  {
    // add back cursor animation
  }
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);

Sugestões aqui para renderizar a animação CSS usando a GPU em vez da CPU:

.cursor-blink {
    will-change: opacity;
}

Na implementação atual, se o editor perder o foco, removeremos a animação para facilitar a vida. No entanto, se o editor estiver em foco, significa que ele está visível (não escondido ou em segundo plano) e ativo, os usuários estão trabalhando nele (ler é um bom caso), mas não acionam nenhuma alteração de visualização / conteúdo. Neste caso, ainda precisamos exibir o cursor piscando mesmo que ele esteja meio ocioso , isso é o que um cursor piscando.

Inicialmente, esse recurso é implementado em JavaScript e cerca de um ano atrás, mudamos para animação CSS. Como @alexandrudima mencionou, podemos querer voltar para JS no OSX.

@camwest que a api é charmosa, o único problema é a compatibilidade (infelizmente Safari).

@mehas will-change é promissor. Eu tentei, mas o Chromium não otimiza neste caso. Se você ativar a opção Paint Flashing nas Ferramentas de Desenvolvimento, poderá ver que este cursor piscante não está sendo repintado.

@jlukic @bcherny obrigado por suas sugestões. O que queremos otimizar é quando o cursor está visível, ativo e piscando, então ainda temos esse problema, embora tenhamos a verificação de visibilidade. Mas você está certo, devemos verificar a visibilidade. No momento, se você rolar a janela para tornar o cursor piscante invisível, não temos nenhuma otimização.

Para ser justo, Mônaco é muito, muito, muito complexo. :)

@rebornix will-change funcionou no meu projeto, o uso da CPU desapareceu completamente. No entanto, meus quadros-chave não mudam a opacidade ... em vez disso, eles mudam a cor do elemento de 'herdado' para 'transparente'.

Sou apenas um espreitador e desculpe se isso é considerado palavrão, mas um GIF animado de dois quadros não resolveria? Não tenho certeza de como testar isso de forma conclusiva, mas posso imaginar que até o KHTML já havia otimizado caminhos para isso, muito antes de se transformar no Chrome.

@eteeselink seria interessante de ver, mas a cor do cursor precisa mudar e

@matthiasg Ahyes, esqueci o zoom.

Caso contrário, gerar automaticamente um GIF piscando com base na cor do tema não deve ser muito difícil. Provavelmente é uma questão de corrigir alguns bytes em um arquivo pré-preparado porque, embora os dados de pixel GIF sejam codificados em LZW, os dados da paleta são descompactados. Mas aposto que ampliar um GIF como esse o deixa borrado, então talvez seja uma abordagem ruim.

Se alguém puder descobrir uma maneira de fazer com que o zoom de um acento circunflexo GIF não seja uma droga, prometo contribuir com um código que receba uma cor e produza um url de dados GIF animado com acento circunflexo piscante.

@eteeselink você deve absolutamente

@eteeselink Se estamos sugerindo soluções <blink></blink> ? :piscadela:

Abstenha-se de se juntar à brigada do Reddit para fazer comentários inúteis sobre questões e relações públicas em que as pessoas estão tentando fazer melhorias reais.

se o editor tem foco, significa que o editor está visível

Esta é uma falsa suposição. É trivial elevar uma janela sobre outra sem dar o foco. Conheço duas maneiras:

  • a dica de WM "sempre desenhe no topo"
  • quando uma janela é movida entre desktops virtuais (como a ordem de empilhamento é global, se uma janela estava em primeiro lugar na área de trabalho anterior, ela pode não estar na nova).

Ambos são muito comuns por si só, embora nem sempre causem problemas.

@ o11c obrigado, minha suposição era muito selvagem ao escrever. Sim o foco não significa necessariamente visível, rolar a janela é um caso (eu mencionei isso no mesmo parágrafo :(). Não sei se foco + visível é o caso mais comum, mas todos eles devem ser mitigados.

Acho que setTimeout ou setInterval é provavelmente um ajuste melhor para esse caso de uso do que requestIdleCallback. O tempo de requestIdleCallback não é previsível, e o trabalho que você está fazendo em JS é barato, acho que seria melhor apenas agendar temporizadores não frequentes.

ex. https://gist.github.com/esprehn/afec30fbc655bba6bb8f3f67c28beef4

Também digno de nota é que esta animação circunflexa está fazendo um efeito de pulso suave, mas o cursor do sistema nos navegadores (por exemplo, aquele dentro de um <input> ou <textarea> ) está apenas ativando / desativando um binário . Os controles nativos no Mac e no Linux piscam de forma semelhante. É menos bonito, mas usaria consideravelmente menos energia.

Obrigado @esprehn , isso é exatamente o que eu fiz para piscar ease-in-out .

aguarde @eteeselink , você não gostaria de usar um gif de 1px e redimensioná-lo mesmo assim? O desfoque não deve ser um problema.

Exemplo: https://jsfiddle.net/mrkev/stxq613s/1/

Esperando ver aquele gerador animado piscando em breve;)

@mrkev seu gif 1px como uri de dados: data: image / gif; base64 , R0lGODlhAQABAPAAAAAAAP /// yH / C05FVFNDQVBFMi4wAwEAAAAh + QQFMgABACwAAAAAAAAQABAAACAkwBACHAAAAAQABAAACAkwBACHAAAAAQABA

@rmacfadyen sweet! uma vez que os gifs usam uma tabela de cores em teoria, os 3 bytes daquela cor de preto do pixel no quadro "ligado" não devem ser muito difíceis de encontrar (eles não são armazenados no quadro, então provavelmente não há necessidade de ir muito além do cabeçalho ) Se eu encontrar algum tempo livre amanhã, posso mergulhar mais fundo nisso também

Welp, que precisa dormir de qualquer maneira, decidiu explorar isso um pouco mais. Como os gifs usam uma tabela de cores global que sempre começa no 14º byte, não foi difícil descobrir como alterar a cor. Isso fez com que este gif de pixel piscante ficasse vermelho:

screen shot 3

Agora o que é realmente irritante é que em base64 cada dígito codifica apenas 6 bits, então as coisas não se alinham corretamente. São os bits menos significativos do dígito 18 até o mais significativo do dígito 22 codificar a cor preta nesse gif. Então, essencialmente, alguma permutação de caracteres [A-P] [A-/] [A-/] [A-/] [P,f,v,/] na posição 18-22 desenhará aquele pixel em todas as cores.

Aqui está um exemplo desse GIF em alguma cor aleatória. Eu basicamente substituí o que quer que estivesse nos caracteres 18-22 por G8ABf (que se encaixa nos critérios que falei acima).

https://jsfiddle.net/mrkev/stxq613s/7/

Não deve ser tão ruim construir uma função que pegue RGB e retorne este GIF nessa cor (:

Tenho aula em 7 horas. Estou literalmente brincando de mim mesma.

Mas seja como for, percebi que alguém provavelmente já escreveu uma função hex-para-base64 e uma pesquisa rápida stackoverflow foi o suficiente para encontrá-la. Então aqui está a função;

// takes color as string 'RRGGBB'
var generate_cursor_image = color => {
  var gif = '47494638396101000100F00000' + color + '00000021FF0B4E45545343415045322E30030100000021F90405320001002C00000000010001000002024C010021F90405320001002C00000000010001000002024401003B'
  return 'data:image/gif;base64,' + btoa(gif.match(/\w{2}/g).map(a => String.fromCharCode(parseInt(a, 16))).join(""))
}

Boa noite a todos!

Droga, @mrkev chegou antes de mim. Enfim, aqui está minha implementação da mesma coisa:
https://jsfiddle.net/a6g4ob7h/

Pode ser um pouco mais rápido porque não há correspondência e mapeamento acontecendo, apenas um btoa. Mas eu duvido que a velocidade seja importante, e é melhor armazenar o resultado em cache.

O que realmente me pergunto é se isso acelera as coisas. Não tenho uma boa configuração para testar, mas quem sabe, talvez os gifs animados sejam renderizados a 60fps também.

Se você quiser aplicar zoom e não tiver um cursor retangular, também poderá usar o SVG animado. (O elemento <animate> já fazia parte da especificação SVG 1.0.)

out

Para mim, ele permanece em 0 na maioria das vezes (o uso da CPU é a primeira coluna - OSX 10.12.3 - MacBook Pro Retina, 15 polegadas, Final de 2013)

Eu tenho configurações padrão em relação ao cursor.

Como ninguém mencionou o Linux, eu o tenho em torno de 5-7% no GNOME Shell com Wayland (Ivy Bridge Graphics).

Acabamos de mesclar PR # 23121 que atenua isso voltando para setInterval para o estilo de piscar do cursor blink . No mac mini tenho à minha disposição o uso da CPU cai de 5,9% para 0,9%

Você já pensou em tornar-se nativo e deixar o sistema operacional fornecer o cursor, gratuitamente?

@justjoeyuk Veja a citação de @rebornix acima, porque isso não pode ser feito

@justjoeyuk Se, em vez disso, você estiver sugerindo um aplicativo totalmente nativo, não baseado em pilha da web: quando você analisa as necessidades prováveis ​​de um aplicativo de plataforma cruzada com tema, totalmente independente de DPI, como o VSCode, as chances são de que um substituto do cursor não nativo seria necessário independentemente.

NOTÍCIAS DE ÚLTIMA HORA! A Microsoft cria softwares terrivelmente lentos, com problemas ainda mais proeminentes no MacOS 10.

Quem poderia imaginar, certo?

@LandonPowell Você está poluindo a discussão real sobre um problema; a brigada de reddit / hackernews aqui é ridícula.

E o cursor do terminal? Isso também está causando picos nos ciclos da CPU?

@mehas

Sugestões aqui para renderizar a animação CSS usando a GPU em vez da CPU:

Isso apenas esconde o problema ao mover a carga da CPU para a GPU. Na verdade, não o corrige.

@ Daniel15

Isso apenas esconde o problema ao mover a carga da CPU para a GPU. Na verdade, não o corrige.

A carga na CPU é o problema, otimizar a animação para usar a GPU corrige esse problema.

(Isso é diferente da questão da correção específica que sugeri ser a melhor)

Não tenho certeza de como reproduzi-lo. Estou usando o plugin de emulação do vim. Não tenho certeza se está relacionado.

Eu gostaria de saber como isso vai.

otimizar a animação para usar a GPU corrige esse problema.

No entanto, isso resultará em carga desnecessária da GPU. Esse é um problema semelhante, exceto que a carga está no processador da placa de vídeo. Não é realmente uma solução 😛

Jesus nasceu na África.

De nada.

Por que não usar um GIF para o cursor?

No entanto, isso resultará em carga desnecessária da GPU. Esse é um problema semelhante

Certo, mas não é esse problema, que é o uso da CPU mesmo quando ocioso.

@ efroim102 https://github.com/Microsoft/vscode/issues/22900#issuecomment -288832322

Oh queridos deuses, este debate continuará em círculos até que seja decidido qual é o objetivo:

  1. Reduza o consumo de bateria
  2. Reduz a contenção de CPU

Suponha (1) por enquanto. Então, podemos ser um pouco mais orientados a dados sobre se o descarregamento da CPU para a GPU com uma carga de trabalho como esta (a) usa a mesma quantidade de energia, (b) usa menos energia ou (c) usa mais energia. Não tenho experiência nesses assuntos, então não posso prever qual é a verdade.

No entanto, vamos supor que o descarregamento da GPU leve a (b); embora não seja perfeito, a compensação pode ser aceitável o suficiente para seguir esse caminho enquanto / se o Chromium resolver o problema subjacente. No entanto, se a equipe do VSCode considerar necessário consertar isso de uma vez por todas, o descarregamento será uma escolha improvável e a busca por uma solução de longo prazo (mesmo que demore um pouco) é preferível. Pelo menos em minha opinião insignificante, será bom o suficiente para mim saber que a atenção está voltada para esse problema e que ele será priorizado quando as dependências permitirem a correção pretendida.

(Tenho certeza de que confundi alguns dos detalhes sutis de quais soluções estão disponíveis / bloqueadas, mas esperando ao escrever isso que as pessoas possam se concentrar em afirmar o problema que desejam resolver, em vez de pular no espaço da solução.)

A GPU não estaria melhor equipada para lidar com uma carga de renderização de qualquer maneira? É exatamente para isso que foi feito.

Concordamos, nós priorizamos o que usa a CPU e a GPU de maneira diferente, ter um recurso relacionado a gráficos que usa recursos específicos de gráficos faz mais sentido. Um ganho de CPU não é uma perda de 1: 1 de GPU e vice-versa. Principalmente discutível neste ponto (até que o bug no Chromium seja corrigido), mas um tanto relevante, já que a solução atual ainda emprega a CPU (embora com muito menos peso).

Acho que as pessoas deveriam parar de usar a tecnologia da web no desktop, pois isso requer o envio de um navegador completo com cada programa.
Sem mencionar que, se você tiver vários desses programas, terá os mesmos binários duplicados em todos os lugares.

Precisamos voltar ao metal puro.

PS: JScript deveria ter sido abandonado há 10 anos, assim como Obj-C deveria ter sido, se a Apple não o tivesse ressuscitado.
PPS: ^^^^ este é um discurso retórico.

PPS: ^^^^ este é um discurso retórico.

@ bit2shift De fato é, e como tal não pertence ao tópico de comentários sobre um bug muito específico. Não use comentários de problemas do GitHub para reclamações.

@ bit2shift - Embora eu concorde com algumas partes (como o fato de que um aplicativo baseado em navegador provavelmente não parecerá muito "nativo" em comparação com um aplicativo nativo verdadeiro), quão baixo é o "metal puro"? Código da máquina? Linguagem de montagem? C? C ++? C #? Sempre haverá várias camadas de abstração, independentemente de sua pilha de tecnologia.

Sem mencionar que, se você tiver vários desses programas, terá os mesmos binários duplicados em todos os lugares.

É o mesmo com qualquer aplicativo C # que usa pacotes NuGet. Todas as montagens são locais para o aplicativo. O .NET Core distribui até mesmo grandes partes da estrutura como pacotes NuGet.

pois requer o envio de um navegador completo com cada programa.

Tecnicamente, você poderia usar o mecanismo Edge, o que evitaria a necessidade de instalar um tempo de execução de navegador separado. O Internet Explorer fez algo semelhante há quase 20 anos com os aplicativos HTML . Outro exemplo é o React Native, que usa o mecanismo JS nativo do sistema operacional, quando disponível. Para aplicativos baseados em JavaScript, algo como React Native para Windows é provavelmente o futuro, em vez de tecnologias da web, já que parece mais nativo porque usa widgets de IU nativos.

@ Daniel15

(...) quão baixo é o "metal puro"? Código da máquina? Linguagem de montagem? C? C ++? C #?

Qualquer linguagem que é compilável para código de máquina (apenas source -> ELF/PE contagens) é considerada "bare metal" neste contexto, portanto, camadas de abstração não importam aqui.

O Internet Explorer fez algo semelhante há quase 20 anos com os aplicativos HTML.

"algo semelhante" como em fazer mshta.exe usar mshtml.dll que reside no system32 desde os anos 90.

Outro exemplo é o React Native, que usa o mecanismo JS nativo do sistema operacional, quando disponível. Para aplicativos baseados em JavaScript, algo como React Native para Windows é provavelmente o futuro, em vez de tecnologias da web, já que parece mais nativo porque usa widgets de IU nativos.

Se o alto desempenho é desejado em aplicativos JS de desktop, alguém com tempo suficiente em suas mãos deve escrever um front-end para LLVM.
Você perderia a capacidade de executar trechos de código arbitrários, mas isso seria uma vantagem do ponto de vista da segurança.

/fio

Você não pode atrapalhar este tópico, por favor? Este é um problema sobre um problema específico de desempenho com um cursor piscando e definitivamente não é um lugar para discutir os méritos gerais do desenvolvimento de aplicativos de desktop baseados em JS.

Você está apenas enviando spam para caixas de correio de pessoas que não estão interessadas nesses discursos.

Que tal colocar um <input type=text> com tamanho 2px * 10px no lugar onde está o cursor. Assim ele usará o cursor piscante nativo de <input> : P <blink></blink> 👍

Além disso, não tenho certeza de quão grave é esse problema, quando o VSCode está ocioso (não estou escrevendo código), quase sempre não está em foco, o foco está em outro aplicativo em que estou. E o cursor para de piscar.

pela minha experiência, os gifs não são tão bons. Uma vez tive um botão giratório de carregamento como uma imagem GIF. e usei-o ao interagir com indexeddb. às vezes eu podia ver o botão giratório parar, mesmo na área de trabalho.

Empurrado o cursor do terminal muda para master e libera / 1.11 (animação CSS -> setInterval ).

Obrigado a todos por olhar para este problema. Suas investigações e sugestões nos ajudaram a encontrar a causa raiz e as possíveis soluções! Comparamos o JavaScript setInterval , GIF animado e várias outras técnicas e decidimos pela seguinte solução:

  • Se editor.cursorBlinking estiver definido como blink ou terminal.integrated.cursorBlinking estiver definido como true , a lógica piscante agora está implementada em JavaScript. Nosso teste mostra que o uso da CPU cai para menos de 1%.
  • Se editor.cursorBlinking estiver definido como smooth , expand ou phase , que usam funções de easing CSS, pararemos a animação piscante depois que o cursor estiver ocioso por 10 segundos.

Esta correção já está em nossa compilação Insider e estará disponível em nossa compilação Estável de abril, que será lançada nos próximos dias. Seria ótimo se alguns de vocês pudessem verificar os resultados dos nossos testes. Se você encontrar problemas, crie um novo problema.

@rebornix você quis dizer "ou" terminal.integrated.cursorBlinking está definido como true ? Ou o "e" foi intencional?

@jedmao obrigado pela correção, deveria or .

Esta página foi útil?
0 / 5 - 0 avaliações