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;
}