DEV Community

High performance school - blog
High performance school - blog

Posted on

🇧🇷 Multi-Threading deixando a app mais lenta? como?

Image description

Introdução 🧐

O multi-threading é uma técnica comum para melhorar a performance de aplicativos, mas não é isenta de desafios. Este artigo se propõe a explorar, de maneira objetiva, as circunstâncias sob as quais o multi-threading pode não oferecer os benefícios esperados, utilizando um exemplo prático em Node.js.

Contextualização

A capacidade de executar várias threads simultaneamente pode, teoricamente, reduzir o tempo de execução de tarefas complexas. No entanto, a realidade é que o overhead de gerenciamento de threads e a contenção de recursos podem impactar negativamente a performance.

Exemplo Prático 👩🏾‍💻

Para ilustrar isso, consideremos um exemplo específico em Node.js, onde o objetivo é calcular números primos. O código abaixo foi utilizado para comparar a eficiência entre a execução single-threaded e multi-threaded.

const { 
    Worker, 
    isMainThread, 
    parentPort, 
    workerData } = require('worker_threads');
const { performance } = require('perf_hooks');

// Função para encontrar números primos em um intervalo
const findPrimes = (start, end) => {
    const primes = [];

    // Itera por todos os números no intervalo [start, end]
    for (let i = start; i <= end; i++) {
        let isPrime = true;

        // Verifica se o número 'i' é divisível por algum número 
        // entre 2 e sua raiz quadrada
        for (let j = 2, sqrt = Math.sqrt(i); j <= sqrt; j++) {
            if (i % j === 0) {
                isPrime = false;
                break;
            }
        }

        // Se 'i' for primo (e maior que 1), adiciona-o ao 
        // array de primos
        if (isPrime && i > 1) {
            primes.push(i);
        }
    }

    return primes;
}

// Função para calcular números primos em um único thread
function singleThread(max) {
    // Marca o início da medição de tempo
    const stStart = performance.now(); 

    // Chama a função para encontrar números primos no 
    // intervalo [2, max]
    findPrimes(2, max); 

     // Marca o final da medição de tempo
    const stEnd = performance.now();
    // Imprime o tempo decorrido em milissegundos
    console.log('Single-threaded:', stEnd - stStart, 'ms'); 
}

// Função para calcular números primos em vários threads
function multiThread(max, numbersPerThread, threadCount) {
    const mtStart = performance.now(); // Marca o início da medição de tempo
    let completed = 0;

    for (let i = 0; i < threadCount; i++) {
        const start = i * numbersPerThread + 2;
        const end = i === threadCount - 1 ? max : start + numbersPerThread - 1;

        // Cria um novo thread para calcular primos em um subintervalo
        const worker = new Worker(__filename, {
            workerData: { start, end }
        });

        // Quando o thread conclui, incrementa o contador 'completed'
        // e verifica se todos os threads terminaram para medir o tempo total
        worker.on('message', () => {
            completed++;
            if (completed === threadCount) {
                // Marca o final da medição de tempo quando todos os threads terminam
                const mtEnd = performance.now(); 
                 // Imprime o tempo decorrido em milissegundos
                console.log('Multi-threaded:', mtEnd - mtStart, 'ms');
            }
        });
    }
}

// Se estiver no thread principal
if (isMainThread) {
    // Define o número máximo no intervalo
    const max = 1e5; 
    // Define o número total de threads a serem usadas
    const threadCount = 4; 
    // Calcula quantos números cada thread deve processar
    const numbersPerThread = Math.ceil(max / threadCount); 
    // Calcula primos em um único thread
    singleThread(max);

    // Calcula primos em múltiplos threads
    multiThread(max, numbersPerThread, threadCount);
} else {
    // Se estiver em um thread
    // Recebe os dados de start e end do intervalo a ser processado
    const { start, end } = workerData; 
    // Chama a função para encontrar primos no intervalo especificado
    findPrimes(start, end); 
    // Envia uma mensagem de conclusão após o cálculo
    parentPort.postMessage(true); 
}

Enter fullscreen mode Exit fullscreen mode

Quando você executar o script completo, os resultados que você irá obter serão parecidos com:

`
Single-threaded: 17.95 ms

Multi-threaded: 75.41 ms`

😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱

Contrariando as expectativas iniciais, a execução multi-threaded foi mais lenta.

Análise 👀

A razão para isso pode ser atribuída ao overhead associado à criação e gerenciamento de threads. Cada thread requer recursos para ser iniciada, executada e, posteriormente, encerrada. Em tarefas que não são intensivamente paralelizáveis ou que são relativamente rápidas, o custo de gerenciamento de threads pode superar os benefícios do processamento paralelo.

Além disso, a contenção de recursos, onde várias threads competem pelos mesmos recursos do sistema, pode levar a um aumento no tempo de execução, especialmente em sistemas com recursos limitados.

Considerações

É essencial, portanto, avaliar a adequação do multi-threading para cada tarefa específica. Fatores como a natureza da tarefa, o ambiente de execução e os recursos disponíveis desempenham um papel crucial na determinação da eficácia do multi-threading.

A Necessidade de Benchmarking

Um passo essencial e muitas vezes subestimado na implementação do multi-threading é a realização de testes de benchmark rigorosos. Estes testes não são apenas uma formalidade, mas uma necessidade para avaliar objetivamente o impacto real na performance. Os desenvolvedores devem se equipar com dados concretos para tomar decisões informadas, evitando suposições e generalizações.

Volume de Dados: Um Fator Crítico

O volume de dados é um elemento inseparável desta discussão. Testes de benchmark realizados com um volume de dados baixo podem não refletir a realidade operacional e, consequentemente, oferecer uma visão distorcida da eficácia do multi-threading. É imperativo que os testes sejam conduzidos em um ambiente que simule as condições reais de operação, com volumes de dados significativos.

A razão para isso é clara: o overhead de gerenciamento de threads pode ser minimizado ou justificado quando lidamos com volumes de dados grandes. Em contrapartida, para volumes de dados menores, o custo de inicialização, execução e terminação de threads pode superar os benefícios percebidos, como observado em nosso exemplo prático.

Recomendação Final

Portanto, a recomendação é clara. Antes de embarcar na jornada do multi-threading, invista tempo e recursos em testes de benchmark detalhados, com um volume de dados que reflita as condições reais de uso. Este passo é fundamental para garantir que a implementação de multi-threading traga benefícios tangíveis em termos de eficiência e performance, e para evitar surpresas indesejadas após a implementação.

Ao adotar uma abordagem metódica, baseada em dados e adaptada às especificidades de cada aplicação e conjunto de dados, os desenvolvedores podem maximizar os benefícios do multi-threading, transformando-o em um aliado valioso na otimização contínua da performance de aplicativos.

Image description

Newsletter

Gostou desse conteúdo? Aproveita pra se inscrever na nossa newsletter https://highperformanceletterbr.substack.com/
Postamos conteúdos sobre performance e otimização todas as semanas!

Primeiro curso de performance backend js do brasil(ou do mundo)

🚀 Domine o Desempenho Backend com JavaScript! 🚀

🎓 Apresentamos o PRIMEIRO Curso de Performance Backend em JavaScript no Brasil! 🇧🇷

🔥 Seja um dos pioneiros a explorar o mundo do desenvolvimento backend com foco em eficiência e velocidade. Este curso exclusivo é sua porta de entrada para se tornar um especialista em otimização e desempenho de aplicações JavaScript.

🌟 O Que Oferecemos:

Instrutores Especialista: Aprenda com um profissional que atua com esse assunto no dia a dia.
Conteúdo Avançado: Desde fundamentos até técnicas avançadas de otimização.
Projetos Práticos: Coloque suas habilidades à prova com desafios reais.
Comunidade e Networking: Junte-se a uma comunidade vibrante de desenvolvedores.
🛠️ Domine Ferramentas e Técnicas Essenciais: Conteúdo prático sobre as melhores tools de monitoramento e análise de recursos

Análise de Performance;

Diagnóstico;
Otimização de Código;
Gerenciamento Eficiente de Memória;
Eventos e streaming;

🚀 Transforme a forma que os outros devs te veem:

Esteja na vanguarda do desenvolvimento backend. As habilidades que você adquirirá são inestimáveis e altamente procuradas no mercado de trabalho para posições senior.

✅ Acesse: https://highperformanceschool.hotmart.host/

Top comments (0)