.. _melhorando_o_script_de_submissao: Melhorando o Script de Submissão ================================ Esta seção tem o intuito de detalhar a construção de um script de submissão de job. Para isso, vários exemplos e opções de parâmetros serão mostrados. Opções do SBATCH ---------------- O ``SBATCH`` é uma instrução necessária do `Slurm `_. Eis algumas referências: * https://www.tchpc.tcd.ie/node/129 * https://slurm.schedmd.com/sbatch.html A tabela abaixo mostra alguns dos parâmetros do *sbatch*. =============== ========== ================================================================== Parâmetro Abreviação Significado --------------- ---------- ------------------------------------------------------------------ --time -t Tempo máximo de execução (min, min:seg, horas:min:seg, dias-horas) --cpus-per-task -c Número de CPUs por processo --ntasks -n Número total de processos --nodes -N Número mínimo de *worker nodes* --output -o Arquivo com o stdout (mensagens de saída) --error -e Arquivo com o stderr (mensagens de erro) --mem Memória por nó em MB --mem-per-cpu Memória por CPU em MB =============== ========== ================================================================== .. _script_job_nanny: Script job-nanny ---------------- Vamos supor que que um usuário chamado *Spock* (cujo login no GridUnesp seja **spock**) queira, adaptar seu script (denominado *vulcano.sh*) para começar a processar simulações de seu projeto de pesquisa. Para isso, ele precisa conhecer mais opções sobre o comando ``SBATCH`` e como construir um script de maneira adequada. Digamos ainda que *Spock* pretenda executar um script em linguagem *Python* que denominou de *dobra_4.py*, o qual lê os arquivos de input *energia.in* e *combustivel.in*, e produz o arquivo *light-speed.data* como resultado. Seu script de submissão tem então o seguinte conteúdo: .. code-block:: bash #!/bin/bash #SBATCH -t 24:00:00 -c 4 python dobra_4.py --input=energia.in,combustivel.in Algumas observações sobre esse script: * Como sabe que a execução do script *dobra_4.py* não deverá demorar mais do que 1 dia, requistou o tempo máximo de processamento de 24 horas (``-t 24:00:00``). * Solicitou ainda a alocação de 4 CPUs (``-c 4``) para realizar o processamento desse job. * Ao fim, resolveu utilizar uma versão do *Python* que ele próprio instalou (``/home/spock/installations/bin/python``). .. danger:: Aqui já é possível identificar alguns problemas no script de submissão: * Não foram informados os arquivos de input nem de output. * Também não foi informado o script ``job-nanny``. * Também não foram informadas as variáveis de ambiente para o *Python* a ser usado. O script ``job-nanny`` serve para evitar que as aplicações científicas façam leitura e escrita diretamente na partição ``/home/``. Uma vez que o job é submetido (a partir do ``/home/``) e começa a ser processado pelos *worker nodes* (nós de processamento), a leitura e escrita em disco deve ser feita em uma partição distinta do ``/home/``. Cabe ao scritp ``job-nanny`` escolher qual será essa partição (a depender das configurações do script de submissão). Do contrário, os *worker node* realizarão leitura e escrita diretamente no disco do ``/home/``, levando a uma sobrecarga na rede devido à grande transferência de arquivos entre esta partição e os nós. Além disso, o ``job-nanny`` também verifica outros parâmetros como **INPUT** e **OUTPUT**. A importância de informar quais são os arquivos/pastas de input reside no fato de evitar que todos os arquivos do diretório (da pasta) corrente sejam transferidos para a partição escolhida. Não se deve transferir arquivos desnecessários, que não farão parte da execução da simulação. Bem como é também importante informar quais são os arquivos/pastas de output pois, a depender da aplicação científica utilizada, muito "lixo" é produzido durante o processamento, não sendo de interesse do usuário ter esses arquivos e, ademais, ao garantir que a menor quantidade de dados possível seja transferida, evita degradar a rede. Por fim, ao usar um pacote instalado no próprio ``/home/``, é preciso informar ao sistema onde encontrar os binários, *libs* e/ou *headers* da instalação. Geralmente isso é feito passando o caminho de variáveis como **PATH**, **LD_LIBRARY_PATY** e **INCLUDE**. Mas pode ser necessário informar várias outras variáveis de ambiente. É preciso verificar as instruções do manual de instalação do pacote. Com base nas observações acima, um script *vulcano.sh* ideal para *Spock* seria: .. code-block:: bash #!/bin/bash #SBATCH -t 24:00:00 -c 4 export INPUT="energia.in combustivel.in dobra_4.py" export OUTPUT="light-speed.data" export PATH=/home/spock/installations/bin:$PATH export LD_LIBRARY_PATY=/home/spock/installations/lib:$LD_LIBRARY_PATY export INCLUDE=/home/spock/installations/include:$INCLUDE job-nanny python dobra_4.py --input=energia.in,combustivel.in Neste exemplo, estão sendo passados os arquivos *energia.in*, *combustivel.in* e *dobra_4.py* como **INPUT**, e *light-speed.data* como **OUTPUT** dentro das aspas dupla. Havendo mais de um arquivo de input/output, deve-se separar os nomes por espaço em branco. Digamos agora que *Spock* tenha decidido usar o pacote *Python* que instalou num ambiente *Conda* (veja a Seção :ref:`instalando_via_comandos_conda`). Neste caso, o script de submissão teria o seguinte conteúdo: .. code-block:: bash #!/bin/bash #SBATCH -t 24:00:00 -c 4 #SBATCH --mail-user=spock.vulcano@unesp.br ---mail-type=END,FAIL export INPUT="energia.in combustivel.in dobra_4.py" export OUTPUT="light-speed.data" module load anaconda3 source activate startrek job-nanny python dobra_4.py --input=energia.in,combustivel.in Note que não houve necessidade de informar as variáveis de ambiente já que bastou ativar o ambiente *Conda* anteriormente criado. Note também que, desta vez, *Spock* decidiu incluir seu e-mail (*spock.vulcano@unesp.br*) para ser informado quando a simulação finalizar com sucesso (**END**) ou caso seja cancelado devido a alguma falha (**FAIL**). .. tip:: Caso queira saber mais opções do parâmetro ``---mail-type``, consulte a página do `SBATCH `_. .. _parametros_do_job_nanny: Parâmetros do job-nanny ----------------------- O script ``job-nanny`` identifica os seguintes parâmetros: =============== =================== ============================================ Parâmetro Valor padrão Significado --------------- ------------------- -------------------------------------------- INPUT "*" Arquivos copiados para a área temporária para iniciar a simulação. OUTPUT "*" Arquivos copiados de volta para a pasta inicial (no ``/home/``) ao fim da simulação. CHECKPOINT "$OUTPUT" Arquivos copiados sistematicamente para a pasta inicial. WAIT_CHECKPOINT "10800" = 3horas Intervalo entre a cópia dos arquivos de checkpoint, em segundos. VERBOSE "0" = False Controla se informações sobre as operações do próprio script devem ser impressas no stdout CHECKPOINT_FUNC Avançado: Usado apenas em casos específicos onde é necessário realizar alguma operação antes do checkpoint. Consule VASP LARGE_FILES "false" Suporte a arquivos grandes. Para ponto de montagem no ``/store/``, usar LARGE_FILES="true". SHARED_FS Utiliza um sistema de arquivos compartilhado (útil para aplicações **MPI**). Se SHARED_FS="true", é utilizado o ponto de montagem no ``/store/``. =============== =================== ============================================ Sempre que a simulação utilizar MPI com :ref:`memoria_distribuida` entre 2 ou mais nós, ou LARGE_FILES="true", ou SHARED_FS="true", os jobs serão executados a partir da partição ``/store/``. Para informações sobre a utilização das partições ``/home/``, ``/tmp/`` ou ``/store/``, consulte a seção :ref:`informacao_sobre_o_storage`. Supondo que o usuário queira informar no parâmetro **INPUT** apenas 5 arquivos denominados * input_01.txt * input_02.txt * process.dat * file_a.csv * file_b.csv e que não se importe em receber todos os arquivos de output e, ainda, gostaria que os resultados da simulação fossem sincronizados com seu ``/home/`` a cada hora (em vez do padrão de 3 horas), o script de submissão poderia então ser configurado assim: .. code-block:: bash export INPUT="input_*.txt process.dat file*" export OUTPUT="*" export WAIT_CHECKPOINT="3600" .. _sistema_de_filas: Sistema de Filas ---------------- O cluster está dividido em filas, de forma a organizar os processos e equilibrar o tempo de espera com o tempo de execução. Cada fila significa que o job não ficará processando por um tempo maior do que aquele que foi solicitado. A tabela a seguir mostra o tempo máximo de processamento em cada fila. ====== ================== Fila Limite de execução ------ ------------------ short 24 horas (padrão) medium 7 dias long 30 dias ====== ================== Nenhum processo poderá alocar um recurso por mais de **30 dias**. .. important:: Cabe ao usuário informar o tempo máximo de processamento do seu job. Caso não informe, a simulação será executada até atingir o prazo padrão (24 horas). E caso o prazo limite seja atingido e o processamento não tenha finalizado, o job é automaticamente CANCELADO por *TIMEOUT*. Eis alguns exemplos: * Job será processado na fila **short** por até 50 minutos .. code-block:: bash #SBATCH -t 50:00 * Job será processado na fila **short** por até 23 horas .. code-block:: bash #SBATCH -t 23:00:00 * Job será processado na fila **medium** por até 50 horas (2 dias e 2 horas) .. code-block:: bash #SBATCH -t 50:00:00 * Job será processado na fila **medium** por até 5 dias e 10 horas .. code-block:: bash #SBATCH -t 5-10 * Job será processado na fila **long** por até 12 dias .. code-block:: bash #SBATCH -t 12-00 * Job será processado na fila **long** por até 30 dias .. code-block:: bash #SBATCH -t 30-00 * Não serão alocados recursos pois solicitou mais do que 30 dias (45 dias, na verdade). A execução desse job nunca será inicializada pelo *Slurm*. .. code-block:: bash #SBATCH -t 45-00 Recomenda-se que processos nas filas **medium** e **long** tenham sistemas de checkpoint pois a possibilidade de falha aumenta proporcionalmente ao tempo necessário para a execução. Também recomenda-se que essas simulações sejam testadas antes com processos na fila **short** (veja seção :ref:`otimizando_o_desempenho`). .. note:: Talvez algum usuáro poderia se perguntar se não seria suficiente informar o tempo máximo permitido em cada fila, ou seja, 24 horas, 7 dias ou 30 dias, em vez de se preocupar com o tempo máximo que acredita que seja necessário. Contudo, vale ressaltar que quantos mais recursos são solicitados, maior o tempo que o job do usuário poderá ficar esperando até que esses recursos sejam finalmente disponibilizados. E **TEMPO** também é um recurso. Sendo assim, podemos concluir que há uma certa "beleza" em fazer uma otimização do tempo necessário a ser alocado para o job. .. _informacao_sobre_o_storage: Informações sobre o Storage --------------------------- O GridUnesp possui vários *storages* (partições) para "armazenar" os arquivos dos usuários, sejam eles de entrada, saída ou temporários (gerados durante o processamento do job). Durante o processamento, o programa em exeução poderá manipular arquivos localizados em diferentes pontos de montagem. É fundamental que o usuário entenda cada um deles. .. danger:: A palavra **armazenar** acima está entre aspas porque o usuário não deve pensar que pode usar o cluster do GridUnesp como lugar para guardar dados. A capacidade em disco das partições, apesar de significativamente grande, é limitada. Assim, apenas arquivos/pastas estritamente necessários à realização de novas simulação é que podem ser mantidos. Os resultados devem ser movidos para um computador local. Veja mais informações na Seção :ref:`manutencao_dos_dados`. Abaixo está a lista dos pontos de montagen seguido do seu tamanho e escopo de acesso. +----------+---------+-----------------------------------------------+ | Ponto de | Tamanho | Escopo | | Montagem | | | +==========+=========+===============================================+ | /tmp/ | 120 GB | Job usando apenas 1 nó e arquivos pequenos | +----------+---------+-----------------------------------------------+ | /store/ | 32 TB | Job usando mais de 1 nó e/ou arquivos grandes | +----------+---------+-----------------------------------------------+ | /home/ | 40 TB | Preparação dos jobs para submissão | +----------+---------+-----------------------------------------------+ A partir daqui, vamos entender em detalhes o uso de cada partição. .. _particao_home: Partição: /home/ ++++++++++++++++ Este é o único ponto de montagem acessível pelo servidor de acesso (**access2.grid.unesp.br**). O usuário deve colocar aqui seus arquivos de entrada, assim como é onde serão depositados os arquivos produzidos nas simulações. .. _particao_tmp: Partição: /tmp/ +++++++++++++++ Utilizado para salvar arquivos temporários durante a execução do job. Este diretório é o menor de todos (120 GB) e é um disco localizado dentro do nó de processamento (*worker node*). Ou seja, se 2 jobs estão sendo executos em nós diferentes, cada processo irá acessar um ``/tmp/`` diferente. Não há compartilhamento entre os ``/tmp/`` de diferentes nós. Nos casos em que o job utiliza apenas 1 nó e, além disso, os arquivos de input e output são relativamente poucos e pequenos (não mais do que unidades de GB), então a partição ``/tmp/`` é a melhor opção. .. admonition:: O vídeo abaixo ilustra o que ocorre com o fluxo de dados ao usar a partição /tmp/. .. raw:: html Vamos supor então que que um usuário queira executar um aplicação científica (hipotética) chamada *test-Covid* que seja capaz de trabalhar com várias *threads* em um único nó. Além disso, após estudos de optimização, vamos supor que o usuário saiba que a simulação possa ter maior performance alocando 6 CPUs para cada processo (``-c 6``) e que terá duração máxima de 5 dias (``-t 5-00``). O script de submissão hipotético seria algo como: .. code-block:: bash #!/bin/bash #SBATCH -t 5-00 -c 6 export INPUT="teste-pcr.in teste-sangue.in" export OUTPUT="casos-positivos.out obitos.out" module load anaconda3 source activate sarscov2 job-nanny test-Covid -in=teste-pcr.in,teste-sangue.in -out=casos-positivos.out,obitos.out Analiando em detalhes o que ocorre com o fluxo de dados, sabemos ao menos que o script ``job-nanny`` irá copiar os arquivos de input para a partição ``/tmp/`` de um dos nós que foi alocado ao job, sendo lidos pelo programa *test-Covid* (instalado no ``/home/`` do usuário via ambiente *Conda*) e cujo processamento será feito nesse *worker node* especifico. Os outpus serão temporariamente escritos na partição ``/tmp/`` e copiados para o ``/home/`` a cada 3 horas. Já em caso de processos com múltiplos nós (ex: MPI), o usuário deve utilizar a partição ``/store/``. .. _particao_store: Partição: /store/ +++++++++++++++++ Caso a partição ``/tmp/`` não seja suficiente (devido ao seu tamanho limitado ou ao escopo do trabalho), então a alternativa é que os arquivos temporários utilizem a partição ``/store/``. Ela possui um escopo maior, sendo visível por todos os nós em que o job está em sendo executado. Seu espaço também é maior, contendo atualmente 32 TB. Assim, caso o script de submissão contenha instruções para uso de vários processos ou de vários nós, o programa em execução irá usar a partição ``/store/``. .. admonition:: O vídeo abaixo ilustra o que ocorre com o fluxo de dados ao usar a partição /store/. .. raw:: html Vamos supor então que que um usuário queira executar um aplicação científica (hipotética) chamada *test-Covid* que seja capaz de trabalhar com vários processos espalhados por vários nós. Digamos ainda que o usuário queira, para isso, usar 12 processos (``-n 12``) espalhados em 3 nós (``-N 3``). Além disso, após estudos de optimização, vamos supor que o usuário saiba que a simulação possa ter maior performance alocando 2 CPUs para cada processo (``-c 2``) e que terá duração máxima de 3 dias (``-t 3-00``). O script de submissão hipotético seria algo como: .. code-block:: bash #!/bin/bash #SBATCH -t 3-00 -n 12 -N 3 -c 2 export INPUT="teste-pcr.in teste-sangue.in" export OUTPUT="casos-positivos.out obitos.out" export WAIT_CHECKPOINT="5400" module load intel/compilers/2017 module load intel/mpi/2017 module load anaconda3 source activate sarscov2 job-nanny test-Covid -in=teste-pcr.in,teste-sangue.in -out=casos-positivos.out,obitos.out Analiando em detalhes o que ocorrerá com o fluxo de dados, sabemos ao menos que o script ``job-nanny`` irá copiar os arquivos de input para a partição ``/store/``, sendo lidos pelo programa *test-Covid* (instalado no ``/home/`` do usuário via ambiente *Conda*) e cujo processamento será feito por 3 *worker nodes*. Os outpus serão temporariamente escritos na partição ``/store/`` e copiados para o ``/home/`` a cada 1 hora e meia (5400 segundos). Ademais, para os processos serem espalhados em vários nós a partir do conceito de :ref:`memoria_distribuida`, foram usadas as ferramentas MPI da *Intel* através dos módulos. Por fim, é válido ainda informar que os 12 processos não serão espalhados igualmente para cada nó (4 processos por nó). Isto é, a maneira do *Slurm* distribuir os processos nos 3 *worker nodes* poderia ser *4,4,4*, assim como: *2,6,4*; *3,3,6*; *1,7,4*; *8,2,2*; etc. E como foram solicitadas 2 CPUs para cada processo, o *Slurm* alocou um total de 24 CPUs para essa simulação (2 CPUs para cada um dos 12 processos). .. note:: Em algumas situações (como quando a fila de jobs está consideravelmente grande), pode ser mais interessante ao usuário não especificar uma quantidade exata de *worker nodes*, mas delimitar quantidades mínima e máxima, deixando a decisão para o escalonadador do sistema (`Slurm `_). Como exemplo, digamos que o usuário deseje espalhar 18 processos entre 2 e 4 nós. Isso poderia ser feito da seguinte maneira: .. code-block:: bash #!/bin/bash #SBATCH -n 18 -N 2-4 Quando apenas um único valor é especificado, o sistema identifica as quantidades mínima e máxima de nós como sendo as mesmas: ``-N 4``, por exemplo, é equivalente a ``-N 4-4``.