Esta página oferece dicas para melhorar o tempo de inicialização.
Retirar símbolos de depuração de módulos
Semelhante a como os símbolos de depuração são removidos do kernel em uma produção remova os símbolos de depuração dos módulos. Remoção de depuração de módulos ajuda o tempo de inicialização reduzindo:
- O tempo necessário para ler os binários do flash.
- O tempo necessário para descompactar o ramdisk.
- O tempo que leva para carregar os módulos.
A remoção do símbolo de depuração dos módulos pode economizar vários segundos durante a inicialização.
A remoção de símbolos é ativada por padrão no build da plataforma Android, mas
para ativá-las, defina
BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES
na configuração específica do dispositivo
em dispositivo/vendor/device.
Usar a compactação LZ4 para kernel e ramdisk
O Gzip gera uma saída compactada menor em comparação com o LZ4, mas o LZ4 descompacta mais rapidamente do que o Gzip. Para o kernel e os módulos, o armazenamento absoluto a redução de tamanho com o Gzip não é tão significativa em comparação com a benefício do tempo de descompactação do LZ4.
Adicionamos suporte à compactação LZ4 do ramdisk na plataforma Android.
criar com BOARD_RAMDISK_USE_LZ4
. Você pode definir essa opção
configuração específica do dispositivo. A compactação do kernel pode ser definida pelo defconfig do kernel.
Mudar para o LZ4 aumenta a velocidade de inicialização de 500 ms a 1.000 ms.
Evite o login excessivo nos seus drivers
Em ARM64 e ARM32, as chamadas de função que estão a mais de uma distância específica do site de chamada precisam de uma tabela de salto (chamada de tabela de vinculação de procedimentos ou PLT) para codificar o endereço de salto completo. Como os módulos são carregados dinamicamente, Essas tabelas de salto precisam ser consertadas durante o carregamento do módulo. As chamadas que precisam realocação são chamadas de entradas de realocação com adendos explícitos entradas (ou RELA, para abreviar) no formato ELF.
O kernel do Linux faz alguma otimização do tamanho da memória (como ocorrência em cache
otimização) ao alocar o PLT. Com este commit
upstream,
o esquema de otimização tem uma complexidade O(N^2)
, em que N
é o número de
RELAs do tipo R_AARCH64_JUMP26
ou R_AARCH64_CALL26
. Então, ter menos RELAs
desses tipos é útil para reduzir o tempo de carregamento do módulo.
Um padrão de codificação comum que aumenta o número de
R_AARCH64_CALL26
ou R_AARCH64_JUMP26
RELAs é um registro excessivo em um
motorista. Cada chamada para printk()
ou qualquer outro esquema de registro geralmente adiciona uma
entrada RELA CALL26
/JUMP26
. No texto de confirmação no commit
upstream,
observe que, mesmo com a otimização, os seis módulos levam cerca de 250 ms
para carregar. Isso ocorre porque esses seis módulos foram os seis principais com
a maior quantidade de registros.
Reduzir o registro de dados pode economizar de 100 a 300 ms no tempo de inicialização, dependendo da quantidade de registros.
Ative a sondagem assíncrona, seletivamente
Quando um módulo é carregado, se o dispositivo compatível já foi
preenchido na árvore de dispositivos (DT, na sigla em inglês) e adicionado ao núcleo do driver, a detecção
do dispositivo é feita no contexto da chamada module_init()
. Quando uma sondagem do dispositivo é
for feito no contexto de module_init()
, o módulo não poderá terminar de carregar até que
que a sondagem seja concluída. Como o carregamento do módulo é em grande parte serializado, um dispositivo que
leva um tempo relativamente longo para detectar retarda o tempo de inicialização.
Para evitar tempos de inicialização mais lentos, ative a sondagem assíncrona para módulos que demoram um pouco para sondar os dispositivos. Ativar a sondagem assíncrona para todos os módulos pode não ser benéfico, já que o tempo necessário para bifurcar uma linha de execução e iniciar a sondagem pode ser tão alto quanto o tempo necessário para sondar o dispositivo.
Dispositivos conectados por um barramento lento, como o I2C, dispositivos que fazem o carregamento de firmware na função de sonda e dispositivos que fazem muita inicialização de hardware podem causar problemas de tempo. A melhor maneira de identificar quando isso acontece é coletar o tempo de sondagem de cada driver e classificá-lo.
Para ativar a sondagem assíncrona de um módulo, não é suficiente
definir apenas a flag PROBE_PREFER_ASYNCHRONOUS
no código do driver. Para módulos, também é preciso adicionar
module_name.async_probe=1
na linha de comando do kernel
ou transmitir async_probe=1
como um parâmetro ao carregar o módulo usando
modprobe
ou insmod
.
Ativar a sondagem assíncrona pode economizar cerca de 100 a 500 ms no tempo de inicialização, dependendo do hardware/driver.
Analise o driver CPUfreq o mais cedo possível
Quanto mais cedo o driver CPUfreq for analisado, mais cedo você poderá dimensionar a frequência
da CPU para o máximo (ou algum máximo limitado termicamente) durante a inicialização. Quanto
mais rápida a CPU, mais rápida a inicialização. Essa diretriz também se aplica a drivers devfreq
que controlam a DRAM, a memória e a frequência de interconexão.
Com os módulos, a ordem de carregamento pode depender do nível da initcall
e
ordem de compilação ou vinculação dos drivers. Use um alias MODULE_SOFTDEP()
para fazer
garanta que o driver cpufreq
esteja entre os primeiros módulos a serem carregados.
Além de carregar o módulo antecipadamente, você também precisa garantir que todos dependências para sondar o driver CPUfreq também foram sondadas. Por exemplo, se você precisar de uma alça de relógio ou regulador para controlar a frequência da CPU, elas são sondadas primeiro. Ou talvez seja necessário carregar drivers térmicos antes do driver CPUfreq se as CPUs ficarem muito quentes durante a inicialização. Faça o possível para que o parâmetro CPUfreq os drivers devfreq sondem o mais cedo possível.
As economias na sondagem antecipada do driver CPUfreq podem ser de muito pequena a muito baixa grandes, dependendo da antecedência com que você pode fazer a sondagem e com que frequência o carregador de inicialização deixa as CPUs lá.
Mover módulos para a segunda etapa de inicialização, fornecedor ou partição vendor_dlkm
Como o processo init do primeiro estágio é serializado, não há muitos
para carregar o processo de inicialização em paralelo. Se um módulo não for necessário para
do init do primeiro estágio para terminar, mova o módulo para o init do segundo estágio colocando-o
no fornecedor ou na partição vendor_dlkm
.
A inicialização no primeiro estágio não exige a sondagem de vários dispositivos para chegar ao segundo estágio init. Apenas os recursos de console e armazenamento flash são necessários para um fluxo de inicialização normal.
Carregue os seguintes drivers essenciais:
watchdog
reset
cpufreq
Para o modo fastbootd
de recuperação e espaço do usuário, a inicialização do primeiro estágio exige mais
dispositivos a serem sondados (como USB) e de exibição. Mantenha uma cópia desses módulos no
ramdisk do primeiro estágio e na partição do fornecedor ou vendor_dlkm
. Assim, eles podem
ser carregados no init do primeiro estágio para recuperação ou no fluxo de inicialização fastbootd
. No entanto,
não carregue os módulos do modo de recuperação na inicialização do primeiro estágio durante a inicialização normal
fluxo Os módulos do modo de recuperação (Recovery mode) podem ser adiados para a inicialização no segundo estágio para diminuir a
o tempo de inicialização. Todos os outros módulos que não são necessários na inicialização do primeiro estágio precisam ser
movidos para a partição do fornecedor ou vendor_dlkm
.
Com uma lista de dispositivos folha (por exemplo, UFS ou serial),
dev needs.sh
encontra todos os drivers, dispositivos e módulos necessários para dependências ou
fornecedores (por exemplo, relógios, reguladores ou gpio
) para a sondagem.
Mover módulos para a inicialização de segundo estágio diminui os tempos de inicialização nos seguintes maneiras:
- Redução do tamanho do ramdisk.
- Isso produz leituras em flash mais rápidas quando o carregador de inicialização carrega o ramdisk. (etapa de inicialização serializada).
- Isso proporciona velocidades de descompactação mais rápidas quando o kernel ramdisk (etapa de inicialização serializada).
- O init do segundo estágio funciona em paralelo, o que oculta o tempo de carregamento do módulo. com o trabalho sendo feito na inicialização do segundo estágio.
Mover módulos para a segunda fase pode economizar 500 a 1.000 ms nos tempos de inicialização, dependendo de quantos módulos você pode mover para a inicialização da segunda fase.
Logística de carregamento do módulo
Os recursos de build mais recentes do Android com configurações de placa que controlam quais os módulos são copiados para cada estágio e quais módulos são carregados. Esta seção foca no seguinte subconjunto:
BOARD_VENDOR_RAMDISK_KERNEL_MODULES
: Esta lista de módulos a serem copiados no ramdisk.BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD
: lista de módulos a serem carregados na inicialização do primeiro estágio.BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD
: lista de módulos a serem carregados quando a recuperação oufastbootd
for selecionada no ramdisk.BOARD_VENDOR_KERNEL_MODULES
: Esta lista de módulos a serem copiados para o fornecedor ou partiçãovendor_dlkm
no diretório/vendor/lib/modules/
.BOARD_VENDOR_KERNEL_MODULES_LOAD
: Esta lista de módulos a serem carregados init de segundo estágio.
Os módulos de inicialização e recuperação no ramdisk também precisam ser copiados para o fornecedor ou
Partição vendor_dlkm
em /vendor/lib/modules
. Copiar esses módulos para a
partição do fornecedor garante que eles não fiquem invisíveis durante a inicialização do segundo estágio,
o que é útil para depuração e coleta de modinfo
para relatórios de bugs.
A duplicação deve ocupar um espaço mínimo no fornecedor ou na partição vendor_dlkm
,
desde que o conjunto de módulos de inicialização seja minimizado. Verifique se o arquivo modules.list
do fornecedor tem uma lista filtrada de módulos em /vendor/lib/modules
.
A lista filtrada garante que os tempos de inicialização não sejam afetados pelo carregamento de módulos
novamente, o que é um processo caro.
Verifique se os módulos do modo de recuperação são carregados como um grupo. Carregando módulos do modo de recuperação pode ser feita no modo de recuperação ou no início do segundo estágio init em cada fluxo de inicialização.
É possível usar os arquivos Board.Config.mk
do dispositivo para realizar essas ações, conforme mostrado
neste exemplo:
# All kernel modules
KERNEL_MODULES := $(wildcard $(KERNEL_MODULE_DIR)/*.ko)
KERNEL_MODULES_LOAD := $(strip $(shell cat $(KERNEL_MODULE_DIR)/modules.load)
# First stage ramdisk modules
BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))
# Recovery ramdisk modules
RECOVERY_KERNEL_MODULES_FILTER := $(foreach m,$(RECOVERY_KERNEL_MODULES),%/$(m))
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
$(filter $(BOOT_KERNEL_MODULES_FILTER) \
$(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))
# ALL modules land in /vendor/lib/modules so they could be rmmod/insmod'd,
# and modules.list actually limits us to the ones we intend to load.
BOARD_VENDOR_KERNEL_MODULES := $(KERNEL_MODULES)
# To limit /vendor/lib/modules to just the ones loaded, use:
# BOARD_VENDOR_KERNEL_MODULES := $(filter-out \
# $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))
# Group set of /vendor/lib/modules loading order to recovery modules first,
# then remainder, subtracting both recovery and boot modules which are loaded
# already.
BOARD_VENDOR_KERNEL_MODULES_LOAD := \
$(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
$(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
BOARD_VENDOR_KERNEL_MODULES_LOAD += \
$(filter-out $(BOOT_KERNEL_MODULES_FILTER) \
$(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
# NB: Load order governed by modules.load and not by $(BOOT_KERNEL_MODULES)
BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD := \
$(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
# Group set of /vendor/lib/modules loading order to boot modules first,
# then the remainder of recovery modules.
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD := \
$(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD += \
$(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
$(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
Este exemplo mostra um subconjunto mais fácil de gerenciar de BOOT_KERNEL_MODULES
e
RECOVERY_KERNEL_MODULES
a ser especificado localmente na configuração da placa.
. O script anterior encontra e preenche cada um dos módulos de subconjunto dos
módulos de kernel disponíveis selecionados, deixando os módulos restantes para a inicialização
da segunda fase.
Para a inicialização de segundo estágio, recomendamos executar o carregamento do módulo como um serviço para que ele não bloqueie o fluxo de inicialização. Use um script de shell para gerenciar o carregamento do módulo para que outras logísticas, como tratamento e mitigação de erros ou conclusão do carregamento do módulo, possam ser informadas (ou ignoradas) se necessário.
É possível ignorar uma falha de carregamento do módulo de depuração que não está presente nos builds do usuário.
Para ignorar essa falha, defina a propriedade vendor.device.modules.ready
como
acionar estágios posteriores do fluxo de inicialização de scripts init rc
para continuar na inicialização
tela. Consulte o exemplo de script a seguir, se você tiver o código
em /vendor/etc/init.insmod.sh
:
#!/vendor/bin/sh
. . .
if [ $# -eq 1 ]; then
cfg_file=$1
else
# Set property even if there is no insmod config
# to unblock early-boot trigger
setprop vendor.common.modules.ready
setprop vendor.device.modules.ready
exit 1
fi
if [ -f $cfg_file ]; then
while IFS="|" read -r action arg
do
case $action in
"insmod") insmod $arg ;;
"setprop") setprop $arg 1 ;;
"enable") echo 1 > $arg ;;
"modprobe") modprobe -a -d /vendor/lib/modules $arg ;;
. . .
esac
done < $cfg_file
fi
No arquivo rc de hardware, o serviço one shot
pode ser especificado com:
service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
class main
user root
group root system
Disabled
oneshot
Outras otimizações podem ser feitas depois que os módulos passam do primeiro para o segundo estágio. Use o recurso de lista de bloqueio modprobe para dividir a segunda fluxo de inicialização do estágio para incluir o carregamento de módulo adiado de módulos não essenciais. O carregamento de módulos usados exclusivamente por um HAL específico pode ser adiado para carregar os módulos somente quando o HAL for iniciado.
Para melhorar os tempos de inicialização aparentes, escolha especificamente os módulos no
serviço de carregamento de módulo que são mais adequados para carregamento após a tela
de inicialização. Por exemplo, é possível carregar os módulos explicitamente para
decodificador de vídeo ou Wi-Fi depois que o fluxo de inicialização init tiver sido limpo
(sys.boot_complete
)
indicador de propriedade do Android, por exemplo). Verifique se os HALs para os módulos de carregamento
tardio bloqueiam por tempo suficiente quando os drivers do kernel não estão presentes.
Também é possível usar o comando wait<file>[<timeout>]
do init na inicialização
o script rc do fluxo espera que as entradas sysfs
selecionadas mostrem que os módulos do driver
concluiu as operações de sondagem. Um exemplo disso é esperar que o
driver de exibição conclua o carregamento em segundo plano da recuperação ou fastbootd
,
antes de apresentar os gráficos do menu.
Inicializar a frequência da CPU com um valor razoável no carregador de inicialização
Nem todos os SoCs/produtos podem inicializar a CPU na frequência mais alta devido a problemas de temperatura ou energia durante testes de loop de inicialização. No entanto, verifique se o bootloader define a frequência de todas as CPUs on-line o mais alto possível para um SoC ou produto. Isso é muito importante porque, com um kernel totalmente modular, a descompressão do init ramdisk ocorre antes que o driver CPUfreq possa ser carregado. Portanto, se a CPU for deixada na extremidade inferior de sua frequência, pelo carregador de inicialização, o tempo de descompactação do ramdisk pode demorar Kernel compilado estaticamente (depois de ajustar a diferença de tamanho do ramdisk) porque a frequência da CPU é muito baixa durante o trabalho intensivo (descompactação). O mesmo se aplica à memória e à frequência de interconexão.
Inicializar a frequência de CPUs grandes no carregador de inicialização
Antes que o driver CPUfreq
seja carregado, o kernel não tem conhecimento da
frequências da CPU e não escalona a capacidade programada da CPU para o
frequência. O kernel pode migrar linhas de execução para a CPU grande se a carga for
o suficiente na CPU pequena.
Verifique se as CPUs grandes têm pelo menos o mesmo desempenho que as CPUs pequenas para a frequência em que o carregador de inicialização as deixa. Por exemplo, se a CPU grande tem desempenho duas vezes maior do que uma CPU pequena para a mesma frequência, mas a o carregador de inicialização define a frequência da CPU pequena como 1,5 GHz e o para 300 MHz, o desempenho da inicialização diminuirá se o kernel move uma linha de execução para a CPU grande. Neste exemplo, se for seguro inicializar a CPU grande a 750 MHz, faça isso mesmo que você não pretenda usá-la explicitamente.
Os drivers não podem carregar o firmware na inicialização do primeiro estágio
Pode haver alguns casos inevitáveis em que é necessário carregar o firmware primeiro inicialização do estágio. No entanto, em geral, os drivers não devem carregar nenhum firmware na inicialização do primeiro estágio, especialmente no contexto de sondagem do dispositivo. Carregando firmware no init primeiro estágio trava o processo de inicialização se o firmware não estiver disponível no no ramdisk de primeiro estágio. E mesmo que o firmware esteja presente no primeiro estágio do ramdisk, ele ainda causa um atraso desnecessário.