尽管使用了更多线程,为什么我的程序还是更慢?

尽管使用了更多线程,为什么我的程序还是更慢?

我是新来的线程,我想测试我新获得的技能,通过一个简单的任务,使用创建图像多线程,有趣的部分是,在单线程上,程序比使用 4 个线程运行得更快(我相信这是我最高效的并行线程运行能力)我有一个 i3 处理器,使用 ubuntu 17,我的std::thread::hardware_concurrency 为 4。我的代码:

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

要构建它,请运行g++ file.cpp -o test -lpng -pthread(使用PNG++)。

答案1

互斥锁是一个转移注意力的东西——它是函数本地的,因此它实际上并没有锁定任何东西,因为每个线程最终都有一个单独的互斥锁。为了实际锁定,您需要将互斥变量移出 create_image。

但是,对映像的写入是独立的,因此实际上不需要锁定。也就是说,由于每次调用 create_image 都是针对一个单独的区域,因此写入不会重叠。您可以通过加入线程等待其完成来保证更改将被记录。

问题实际上是rand()。根据我的测试,它有自己的内部互斥锁,这导致了所有的速度减慢。从 rand() 更改为 rand_r(&seed) 会产生很大的差异。使用的线程越多,锁定(每次调用)的成本就越高,因此您会看到速度减慢。

话虽如此,在我的 CPU 上,PNG 的创建是该程序的主要成本。在不写入 PNG 图像的情况下,程序运行时间不到 2 秒(单线程),并且与所使用的核心数量几乎呈线性扩展。写入 PNG 图像时,时间会跳至超过 8 秒,因此写入 PNG 图像比创建图像花费的时间要长得多。

这是我想出的:

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

相关内容