Estamos procurando atualizar o sistema operacional em nossos servidores do Ubuntu 10.04 LTS para o Ubuntu 12.04 LTS. Infelizmente, parece que a latência para executar um thread que se tornou executável aumentou significativamente do kernel 2.6 para o kernel 3.2. Na verdade, os números de latência que estamos obtendo são difíceis de acreditar.
Deixe-me ser mais específico sobre o teste. Temos um programa que executa dois threads. O primeiro thread obtém a hora atual (em tiques usando RDTSC) e então sinaliza uma variável de condição uma vez por segundo. O segundo encadeamento espera a variável de condição e desperta quando é sinalizado. Em seguida, obtém a hora atual (em tiques usando RDTSC). A diferença entre a hora no segundo encadeamento e a hora no primeiro encadeamento é calculada e exibida no console. Depois disso, o segundo encadeamento espera a variável de condição mais uma vez. Ele será sinalizado novamente pelo primeiro thread após cerca de um segundo.
Então, em poucas palavras, obtemos um thread para thread comunicação via medição de latência de variável de condição uma vez por segundo como resultado.
No kernel 2.6.32, essa latência está em algum lugar na ordem de 2,8-3,5 us, o que é razoável. No kernel 3.2.0, essa latência aumentou para algo em torno de 40-100 us. Excluí quaisquer diferenças de hardware entre os dois hosts. Eles funcionam em hardware idêntico (processadores dual socket X5687 {Westmere-EP} rodando a 3,6 GHz com hyperthreading, speedstep e todos os estados C desligados). O aplicativo de teste altera a afinidade dos threads para executá-los em núcleos físicos independentes do mesmo soquete (ou seja, o primeiro thread é executado no Core 0 e o segundo thread é executado no Core 1), portanto, não há retorno de threads no núcleos ou salto / comunicação entre sockets.
A única diferença entre os dois hosts é que um está executando o Ubuntu 10.04 LTS com kernel 2.6.32-28 (a caixa de troca rápida de contexto) e o outro está executando o Ubuntu 12.04 LTS mais recente com kernel 3.2.0-23 (o contexto lento caixa de comutação). Todas as configurações e hardware do BIOS são idênticos.
Houve alguma mudança no kernel que poderia ser responsável por essa ridícula lentidão em quanto tempo leva para um thread ser agendado para ser executado?
Update: Se você gostaria de executar o teste em seu host e Linux build, eu postei o código para pastebin para sua leitura. Ajuntar com:
g++ -O3 -o test_latency test_latency.cpp -lpthread
Execute com (assumindo que você tenha pelo menos uma caixa dual-core):
./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1
Atualização 2 : Depois de muito pesquisar os parâmetros do kernel, postar sobre mudanças no kernel e pesquisa pessoal, descobri qual é o problema e postei a solução como resposta a essa pergunta.
fonte
/proc/sys/kernel/*
pode funcionar? Se você encontrar algo que funcione, coloque essa configuração/etc/sysctl.conf
ou um arquivo/etc/sysctl.d/
para fazê-lo persistir nas reinicializações.Respostas:
A solução para o problema de desempenho de ativação de thread ruim em kernels recentes tem a ver com a mudança para o
intel_idle
driver cpuidle deacpi_idle
, o driver usado em kernels mais antigos. Infelizmente, ointel_idle
driver ignora a configuração do BIOS do usuário para os estados C e dança em sua própria melodia . Em outras palavras, mesmo se você desabilitar completamente todos os estados C no BIOS do seu PC (ou servidor), este driver ainda os forçará durante períodos de breve inatividade, que quase sempre acontecem, a menos que um benchmark sintético que consuma todo o núcleo (por exemplo, estresse ) está correndo. Você pode monitorar as transições de estado C, junto com outras informações úteis relacionadas às frequências do processador, usando a maravilhosa ferramenta i7z do Google na maioria dos hardwares compatíveis.Para ver qual driver cpuidle está atualmente ativo em sua configuração, basta catar o
current_driver
arquivo nacpuidle
seção/sys/devices/system/cpu
seguinte:Se você deseja que seu moderno sistema operacional Linux tenha a menor latência de alternância de contexto possível, adicione os seguintes parâmetros de inicialização do kernel para desativar todos esses recursos de economia de energia:
No Ubuntu 12.04, você pode fazer isso adicionando-os à
GRUB_CMDLINE_LINUX_DEFAULT
entrada em/etc/default/grub
e executandoupdate-grub
. Os parâmetros de inicialização a serem adicionados são:Aqui estão os detalhes sangrentos sobre o que as três opções de inicialização fazem:
Definir
intel_idle.max_cstate
como zero irá reverter seu driver cpuidle paraacpi_idle
(pelo menos pela documentação da opção) ou desativá-lo completamente. Na minha caixa, ele está completamente desativado (ou seja, exibir ocurrent_driver
arquivo em/sys/devices/system/cpu/cpuidle
produz uma saída denone
). Neste caso, a segunda opção de inicializaçãoprocessor.max_cstate=0
é desnecessária. No entanto, a documentação afirma que definir max_cstate como zero para ointel_idle
driver deve reverter o sistema operacional para oacpi_idle
driver. Portanto, coloquei a segunda opção de inicialização para o caso.A
processor.max_cstate
opção define o estado C máximo doacpi_idle
driver para zero, desabilitando-o também. Eu não tenho um sistema para testar isso, porqueintel_idle.max_cstate=0
desativa completamente o driver cpuidle em todo o hardware disponível para mim. No entanto, se sua instalação revertê-lo deintel_idle
paraacpi_idle
com apenas a primeira opção de inicialização, informe-me se a segunda opçãoprocessor.max_cstate
fez o que foi documentado nos comentários para que eu possa atualizar esta resposta.Finalmente, o último dos três parâmetros,
idle=poll
é um verdadeiro devorador de poder. Isso desabilitará C1 / C1E, o que removerá o bit final restante de latência à custa de muito mais consumo de energia, então use esse apenas quando for realmente necessário. Para a maioria, isso será um exagero, já que a latência C1 * não é tão grande. Usando meu aplicativo de teste em execução no hardware que descrevi na pergunta original, a latência foi de 9 para 3 nós. Esta é certamente uma redução significativa para aplicativos altamente sensíveis à latência (por exemplo, negociação financeira, telemetria / rastreamento de alta precisão, aquisição de dados de alta frequência, etc ...), mas pode não valer a pena o impacto de energia elétrica incorrido para a grande maioria dos aplicativos de desktop. A única maneira de saber com certeza é traçar um perfil de melhoria de desempenho de seu aplicativo vs.Atualizar:
Após testes adicionais com vários
idle=*
parâmetros, descobri que configuraridle
paramwait
se compatível com o seu hardware é uma ideia muito melhor. Parece que o uso dasMWAIT/MONITOR
instruções permite que a CPU entre em C1E sem que nenhuma latência perceptível seja adicionada ao tempo de ativação do thread. Com oidle=mwait
, você obterá temperaturas de CPU mais frias (em comparação comidle=poll
), menos uso de energia e ainda manterá as excelentes baixas latências de um loop ocioso de polling. Portanto, meu conjunto recomendado atualizado de parâmetros de inicialização para latência de ativação de thread de CPU baixa com base nestas descobertas é:O uso de em
idle=mwait
vez deidle=poll
também pode ajudar com o início do Turbo Boost (ajudando a CPU a ficar abaixo de seu TDP [Energia de Design Térmico]) e hyperthreading (para o qual MWAIT é o mecanismo ideal para não consumir um núcleo físico inteiro ao mesmo tempo tempo evitando os estados C superiores). No entanto, isso ainda precisa ser provado em testes, que continuarei a fazer.Atualização 2:
A
mwait
opção idle foi removida dos kernels 3.x mais novos (graças ao usuário ck_ pela atualização). Isso nos deixa com duas opções:idle=halt
- Deve funcionar bemmwait
, mas teste para ter certeza de que é o caso do seu hardware. AHLT
instrução é quase equivalente a umaMWAIT
dica de estado com 0. O problema reside no fato de que uma interrupção é necessária para sair de um estado HLT, enquanto uma gravação de memória (ou interrupção) pode ser usada para sair do estado MWAIT. Dependendo do que o kernel do Linux usa em seu loop ocioso, isso pode tornar o MWAIT potencialmente mais eficiente. Então, como falei teste / perfil e veja se atende às suas necessidades de latência ...e
idle=poll
- A opção de melhor desempenho, em detrimento de energia e calor.fonte
Talvez o que tenha ficado mais lento seja o futex, o bloco de construção das variáveis de condição. Isso vai lançar alguma luz:
então
que mostrará os microssegundos necessários para as chamadas de sistema interessantes, classificadas por tempo.
No kernel 2.6.32
No kernel 3.1.9
Encontrei este relatório de bug de 5 anos que contém um teste de desempenho de "pingue-pongue" que compara
Eu tive que adicionar
a fim de compilar, o que fiz com este comando
No kernel 2.6.32
No kernel 3.1.9
Concluo que, entre o kernel 2.6.32 e o 3.1.9, a troca de contexto realmente ficou mais lenta, embora não tanto quanto você observa no kernel 3.2. Sei que isso ainda não respondeu à sua pergunta, vou continuar pesquisando.
Edit: descobri que alterar a prioridade em tempo real do processo (ambos os threads) melhora o desempenho em 3.1.9 para corresponder a 2.6.32. No entanto, definir a mesma prioridade em 2.6.32 torna mais lento ... vai entender - vou dar uma olhada nisso.
Aqui estão meus resultados agora:
No kernel 2.6.32
No kernel 3.1.9
fonte
Você também pode ver processadores clicando para baixo em processos mais recentes e kernels Linux devido ao driver pstate que é separado dos c-states. Além disso, para desativar isso, você deve usar o seguinte parâmetro de kernel:
intel_pstate=disable
fonte