Por que meu programa está mais lento, apesar de usar mais threads? [fechadas]

1

Eu sou novo em threading , e eu queria testar minhas habilidades recém-adquiridas, com uma tarefa simples, criar uma imagem usando vários threads , a parte interessante é que , em um único thread, o programa roda mais rápido do que usando 4 threads (que é a minha capacidade de execução de threads paralela mais eficiente, eu acredito) Eu tenho um processador i3, usando o ubuntu 17, e meu std :: thread :: hardware_concurrency é 4 . meu código:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <png++/png.hpp>
#include <time.h>

std::vector<int> bounds(int max, int parts)
{
    std::vector<int> interval;
    int gap = max / parts;
    int left = max % parts;
    int nr1 = 0;
    int nr2;

    interval.push_back(nr1);
    for (int i = 0; i < parts; i++)
    {
        nr2 = nr1 + gap;
        if (i == parts - 1)
            nr2 += left;
        nr1 = nr2;
        interval.push_back(nr2);
    }
    return interval;
}

void create_image(png::image<png::rgb_pixel> &image, int start, int end)
{
    std::mutex my_mutex;
    std::lock_guard<std::mutex> locker(my_mutex);
    srand(time(NULL));
    for (int i = start; i < end; i++)
        for (int j = 0; j < image.get_height(); j++)
            image[i][j] = png::rgb_pixel(rand() % 256, 0, rand() % 256);
}

int main()
{
    png::image<png::rgb_pixel> png_image(6000, 6000);                  //Creating Image
    int parts = 1;                                                     //amount of parallel threads
    std::vector<int> my_vector = bounds(png_image.get_width(), parts); //interval vector
    std::vector<std::thread> workers;                                  //threads

    time_t start, end;
    time(&start); //measuring time
    for (int i = 0; i < parts - 1; i++)
    {
        workers.push_back(std::thread(create_image, std::ref(png_image), my_vector[i], my_vector[i + 1]));
    }
    for (int i = 0; i < parts - 1; i++)
        workers[i].join();

    create_image(png_image, my_vector[parts - 1], my_vector[parts]);

    png_image.write("test.png");
    time(&end);
    std::cout << (end - start) << " seconds\n";

    return 0;
}

Para criar isso, execute g++ file.cpp -o test -lpng -pthread (com png ++ ).

    
por Gameerik 18.05.2018 / 18:48

1 resposta

4

O mutex é um arenque vermelho - é local para a função e, portanto, não está bloqueando nada, já que acaba sendo um mutex separado para cada thread. Para realmente bloquear, você precisaria mover a variável mutex de create_image.

No entanto, as gravações na imagem são independentes, portanto, o bloqueio não é realmente necessário. Ou seja, como cada chamada para create_image é para uma região separada, as gravações não se sobrepõem. Você garante que as alterações serão registradas juntando os tópicos para aguardar sua conclusão.

O problema é realmente rand (). Do meu teste, ele tem seu próprio bloqueio mutex interno que está causando toda a lentidão. Mudar de rand () para rand_r (& seed) faz toda a diferença. Quanto mais threads estiverem em uso, mais caro será o bloqueio (por chamada) e, assim, você verá uma lentidão.

Dito isto, na minha CPU, a criação do PNG é o custo dominante neste programa. Sem gravar a imagem PNG, o programa é executado em menos de 2s (thread único) e escala quase linearmente com o número de núcleos usados. Ao escrever a imagem PNG, esse tempo passa para mais de 8 segundos, por isso, a gravação da imagem PNG demora muito mais do que a criação da imagem.

Aqui está o que eu criei:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <png++/png.hpp>
#include <time.h>

std::vector<int> bounds(int max, int parts)
{
    std::vector<int> interval;
    int gap = max / parts;
    int left = max % parts;
    int nr1 = 0;
    int nr2;

    interval.push_back(nr1);
    for (int i = 0; i < parts; i++)
    {
        nr2 = nr1 + gap;
        if (i == parts - 1)
            nr2 += left;
        nr1 = nr2;
        interval.push_back(nr2);
    }
    return interval;
}

void create_image(png::image<png::rgb_pixel> &image, int start, int end)
{
    unsigned int seed = time(NULL);
    for (int i = start; i < end; i++)
        for (int j = 0; j < image.get_height(); j++)
            image[i][j] = png::rgb_pixel(rand_r(&seed) % 256, 0, rand_r(&seed) % 256);
}

int main()
{
    png::image<png::rgb_pixel> png_image(6000, 6000);                  //Creating Image
    int parts = 1;                                                     //amount of parallel threads
    std::vector<int> my_vector = bounds(png_image.get_width(), parts); //interval vector
    std::vector<std::thread> workers;                                  //threads

    time_t start, end;
    time(&start); //measuring time
    for (int i = 0; i < parts; i++)
    {
        workers.push_back(std::thread(create_image, std::ref(png_image), my_vector[i], my_vector[i + 1]));
    }
    for (int i = 0; i < parts; i++)
        workers[i].join();

    png_image.write("test.png");
    time(&end);
    std::cout << (end - start) << " seconds\n";

    return 0;
}
    
por 18.05.2018 / 19:28