No entanto, o mesmo Moore em 2003 publicou o trabalho “No Exponential is Forever: But“ Forever ”Can Be Delayed! ”, No qual ele admitiu que o crescimento exponencial das quantidades físicas por muito tempo é impossível. Somente a evolução dos transistores e de suas tecnologias de fabricação tornou possível estender a lei por mais várias gerações.
Em 2007, Moore afirmou que a lei obviamente deixaria de funcionar em breve devido à natureza atômica da matéria e à limitação da velocidade da luz. Atualmente, o tamanho máximo de um transistor em um processador é de 5 nanômetros. Existem também amostras de teste do processador de 3 nm, mas seu lançamento não começará até 2021. Isso sugere que em breve um novo aumento no número de transistores em um chip irá parar (até que um novo material seja descoberto ou o processo tecnológico seja radicalmente atualizado).
Uma das soluções para esse problema é a computação paralela. Este termo é entendido como uma forma de organização da computação computacional, na qual os programas são desenvolvidos como um conjunto de processos computacionais interagentes trabalhando em paralelo (simultaneamente).
A computação paralela pelo método de sincronização é dividida em dois tipos.
Na primeira variante, a interação dos processos ocorre por meio da memória compartilhada: um thread separado de execução é lançado em cada processador de um sistema multiprocessador. Todos os tópicos pertencem a um processo. Threads trocam dados por meio de uma área de memória compartilhada para um determinado processo. O número de threads corresponde ao número de processadores. Os fluxos são criados por meio de uma linguagem de programação (por exemplo, Java, C #, C ++ de C ++ 11, C de C11) ou usando bibliotecas. Nesse caso, é possível criar threads explicitamente (por exemplo, em C / C ++ usando PThreads), declarativamente (por exemplo, usando a biblioteca OpenMP) ou automaticamente - usando ferramentas de compilador embutidas (por exemplo, Fortran de alto desempenho). A variante descrita de programação paralela geralmente requer alguma forma de captura de controle (mutexes, semáforos,monitores) para coordenar fluxos entre eles.
Na segunda variante, a interação é realizada por meio de transmissão de mensagens. Um processo de thread único começa em cada processador em um sistema multiprocessador e se comunica com outros processos em execução em outros processadores usando mensagens. Os processos são criados explicitamente chamando a função apropriada do sistema operacional e as mensagens são trocadas usando uma biblioteca especial (por exemplo, a implementação do protocolo MPI) ou usando ferramentas de linguagem (por exemplo, Fortran de alto desempenho, Erlang ou occam).
Além das duas descritas acima, uma opção híbrida também é usada: em sistemas multiprocessadores com memória distribuída (DM-MIMD), onde cada nó do sistema é um multiprocessador com memória compartilhada (SM-MIMD), a seguinte abordagem pode ser usada. Um processo multiencadeado é lançado em cada nó do sistema, que distribui os encadeamentos entre os processadores desse nó. A troca de dados entre threads em um nó é realizada por meio de memória compartilhada e a troca de dados entre os nós é realizada por meio de transferência de mensagem. Nesse caso, o número de processos é determinado pelo número de nós e o número de threads é determinado pelo número de processadores em cada nó. O método híbrido de programação paralela é mais complicado (é necessário reescrever o programa paralelo de maneira especial), mas é mais eficiente no uso dos recursos de hardware de cada nó do sistema multiprocessador.
Neste artigo, proponho adaptar essa abordagem híbrida para paralelizar cálculos na linguagem Python. A principal característica do trabalho é o uso da tecnologia de contêineres docker. O framework em desenvolvimento terá uma arquitetura cliente-servidor, que inclui os seguintes elementos.
Do lado do cliente:
- Serializador: de acordo com o nome, serializa funções e suas variáveis (ou seja, permite que sejam salvas em um dispositivo externo ou rede e depois carregadas na memória do mesmo ou de outro nó). Também vale a pena destacar o decorador paralelo, que é uma função de wrapper que permite usar o serializador para funções de vários tipos.
- Aulas para configuração de conexão de servidor / cluster
- Ferramentas de linguagem adicionais para marcar funções a serem paralelizadas.
Lado do servidor:
- Deserializador - portanto, desserializa os dados recebidos (veja acima).
- Executor é uma classe que processa dados desserializados (funções e seus argumentos) e também instala as bibliotecas necessárias no ambiente virtual do interpretador Python.
A arquitetura geral do sistema em desenvolvimento é mostrada na figura.
Para a comunicação entre o cliente e o servidor, podem ser utilizados sockets ou a estrutura torcida, cuja interação será realizada através da API desenvolvida.
A implementação deste sistema pressupõe o uso da tecnologia docker. Isso permite que você forneça conveniência e alta velocidade de configuração de software para começar: basta iniciar o cluster docker-swarm, implantar a imagem docker no servidor selecionado e definir o número de replicações.
Outras vantagens importantes do uso da tecnologia docker são a criação de um ambiente de computação homogêneo por meio da virtualização de um sistema semelhante ao UNIX (Ubuntu - lightweight Alpine Linux), bem como a presença de um modo de enxame, que permite executar vários contêineres em servidores diferentes e equilibrar rapidamente a carga transferindo trabalhos para contêineres livres ...
A estrutura desenvolvida pode ser usada em várias áreas onde é necessário realizar grandes quantidades de cálculos na linguagem Python, incluindo aprendizado de máquina e tarefas de análise de dados profundos, bem como para tarefas mais simples, por exemplo, para verificação de decisão distribuída durante concursos de programação.