lundi 30 mai 2022

Threads are pushing all of work onto one thread

Hey so I am having some issue again with my code, whenever I attempt to run it the code does run smoothly as it should, though for whatever reason it appears as if all the work is being pushed onto the one thread when in reality it shouild be evenly split between each thread. What I want it to do instead is to give each thread a slice of the mandelbrot to generate, rather than pushing it all onto the one thread. The main issue I feel is within the main function and not the others as they are all working as intended, all help is much appreciated.

#include <cstdint>
#include <cstdlib>
#include <complex>
#include <fstream>
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>

// Import things we need from the standard library
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::complex;
using std::cout;
using std::endl;
using std::ofstream;

// Define the alias "the_clock" for the clock type we're going to use.
typedef std::chrono::steady_clock the_clock;


// The size of the image to generate.
const int WIDTH = 1920;
const int HEIGHT = 1200;

// The number of times to iterate before we assume that a point isn't in the
// Mandelbrot set.
// (You may need to turn this up if you zoom further into the set.)
const int MAX_ITERATIONS = 500;

// The image data.
// Each pixel is represented as 0xRRGGBB.
uint32_t image[HEIGHT][WIDTH];

std::atomic<double> progress;
std::atomic_bool progressDone = false;
std::mutex locking;
std::condition_variable conditionMet;
std::atomic_int partsDone = 0;
int noOfThreads;

// Write the image to a TGA file with the given name.
// Format specification: http://www.gamers.org/dEngine/quake3/TGA.txt

void progress_bar() {

    while (!progressDone) {

        cout << "Current Progress is at: " << progress.load() << "%\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

    }

    cout << "Mandelbrot is finished! Take a look.";

}

void write_tga(const char *filename)
{
    ofstream outfile(filename, ofstream::binary);

    uint8_t header[18] = {
        0, // no image ID
        0, // no colour map
        2, // uncompressed 24-bit image
        0, 0, 0, 0, 0, // empty colour map specification
        0, 0, // X origin
        0, 0, // Y origin
        WIDTH & 0xFF, (WIDTH >> 8) & 0xFF, // width
        HEIGHT & 0xFF, (HEIGHT >> 8) & 0xFF, // height
        24, // bits per pixel
        0, // image descriptor
    };
    outfile.write((const char *)header, 18);

    for (int y = 0; y < HEIGHT; ++y)
    {
        for (int x = 0; x < WIDTH; ++x)
        {
            uint8_t pixel[3] = {
                image[y][x] & 0xFF, // blue channel
                (image[y][x] >> 8) & 0xFF, // green channel
                (image[y][x] >> 16) & 0xFF, // red channel
            };
            outfile.write((const char *)pixel, 3);
        }
    }

    outfile.close();
    if (!outfile)
    {
        // An error has occurred at some point since we opened the file.
        cout << "Error writing to " << filename << endl;
        exit(1);
    }
}


// Render the Mandelbrot set into the image array.
// The parameters specify the region on the complex plane to plot.
void compute_mandelbrot(double left, double right, double top, double bottom, double start, double finish)
{
    for (int y = start; y < finish; ++y)
    {
        for (int x = 0; x < WIDTH; ++x)
        {
            // Work out the point in the complex plane that
            // corresponds to this pixel in the output image.
            complex<double> c(left + (x * (right - left) / WIDTH),
                top + (y * (bottom - top) / HEIGHT));

            // Start off z at (0, 0).
            complex<double> z(0.0, 0.0);

            // Iterate z = z^2 + c until z moves more than 2 units
            // away from (0, 0), or we've iterated too many times.
            int iterations = 0;
            while (abs(z) < 2.0 && iterations < MAX_ITERATIONS)
            {
                z = (z * z) + c;

                ++iterations;
            }

            if (iterations == MAX_ITERATIONS)
            {
                // z didn't escape from the circle.
                // This point is in the Mandelbrot set.
                image[y][x] = 0x000000; // black
            }
            else if (iterations == 0) {

                image[y][x] = 0xFFFFFF;

            }
            else
            {
                // z escaped within less than MAX_ITERATIONS
                // iterations. This point isn't in the set.
                image[y][x] = 0xFFFFFF; // white
                image[y][x] = 16711680 | iterations << 8 | iterations;
            }

            progress = progress + double((1.0 / (WIDTH*HEIGHT)) * 100.0);
        }
    }
    partsDone += 1;
}


int main(int argc, char *argv[])
{
    cout << "Please wait..." << endl;

    cout << "How many threads do you want to use?: ";
    std::cin >> noOfThreads;

    // Start timing
    std::vector<std::thread*> threads;
    the_clock::time_point start = the_clock::now();

    std::thread progressive(progress_bar);

    for (int slice = 0; slice < noOfThreads; slice++) {

        // This shows the whole set.
        threads.push_back(new std::thread(compute_mandelbrot, -2.0, 1.0, 1.125, -1.125, HEIGHT * (slice / noOfThreads), HEIGHT * ((slice + 1) / noOfThreads)));

        // This zooms in on an interesting bit of detail.
        //compute_mandelbrot(-0.751085, -0.734975, 0.118378, 0.134488, 0, HEIGHT/16);

    }

    // Stop timing

    for (std::thread* t : threads) {

        t->join();
        cout << "My ID is: " << t->get_id() << endl;
        delete t;

    }

    if (partsDone == noOfThreads) {

        progressDone = true;

    }

    conditionMet.notify_one();

    progressive.join();

    the_clock::time_point end = the_clock::now();

    // Compute the difference between the two times in milliseconds
    auto time_taken = duration_cast<milliseconds>(end - start).count();
    cout << "Computing the Mandelbrot set took " << time_taken << " ms." << endl;

    write_tga("output.tga");

    std::this_thread::sleep_for(milliseconds(3000));

    return 0;
}

Aucun commentaire:

Enregistrer un commentaire