线程似乎正在减慢图像处理C++ 11的速度

cha*_*255 0 c++ multithreading c++11

我正在编写一个函数来更改图像中像素的值.它的工作方式是将每个像素着色的任务分成多个线程.例如,如果有4个线程,那么每个线程将每4个像素着色.我觉得奇怪的是,线程方法比在单个循环中执行速度慢大约1/10秒.我无法弄清楚为什么这是因为我有一个四核CPU并且线程之间没有真正的同步.我希望它快4倍,减去一点开销.我在这里做错了吗?

请注意,我设置nthreads = 1来测量单循环方法.

FYI栅格是类中的指针,指向动态像素阵列.

void RGBImage::shade(Shader sh, size_t sx, size_t sy, size_t ex, size_t ey)
{
    validate();
    if(ex == 0)
        ex = width;
    if(ey == 0)
        ey = height;

    if(sx < 0 || sx >= width || sx >= ex || ex > width || sy < 0 || sy >= height || sy >= ey
            || ey > height)
        throw std::invalid_argument("Bounds Invalid");

    size_t w = ex - sx;
    size_t h = ey - sy;
    size_t nthreads = std::thread::hardware_concurrency();
    if(nthreads > MAX_THREADS)
        nthreads = MAX_THREADS;
    else if(nthreads < 1)
        nthreads = 1;

    size_t load_per_thread = w * h / nthreads;
    if(load_per_thread < MIN_THREAD_LOAD)
        nthreads = (w * h) / MIN_THREAD_LOAD;

    clock_t start = clock();
    if(nthreads > 1)
    {
        std::unique_ptr<std::thread[]> threads(new std::thread[nthreads]);
        for(size_t i = 0; i < nthreads; i++)
            threads[i] = std::thread([=]()
            {   
                for(size_t p = i; p < (w * h); p += nthreads)
                {   
                    size_t x = sx + p % w;
                    size_t y = sy + p / w;
                    sh(raster[y * width + x], x, y);
                }
            });
        for(size_t i = 0; i < nthreads; i++)
            threads[i].join();
    }
    else
    {
        for(size_t p = 0; p < (w * h); ++p)
        {
            size_t x = sx + p % w;
            size_t y = sy + p / w;
            sh(raster[y * width + x], x, y);
        }
    }
    std::cout << ((float)(clock() - start) / CLOCKS_PER_SEC) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

我接受了一些建议,改变了我的功能.

void RGBImage::shade(Shader sh, bool threads)
{
    validate();
    clock_t c = clock();
    if(threads)
    {
        int nthreads = std::thread::hardware_concurrency();
        size_t pix = width * height;
        if(nthreads < 1)
            nthreads = 1;
        else if(nthreads > MAX_THREADS)
            nthreads = MAX_THREADS;
        if(pix / nthreads < MIN_THREAD_LOAD)
            nthreads = pix / MIN_THREAD_LOAD;

        size_t pix_per_threads = pix / nthreads;

        std::unique_ptr<std::thread[]> t(new std::thread[nthreads]);
        for(int i = 0; i < nthreads; i++)
        {
            t[i] = std::thread([=]()
            {
                size_t offset = i * pix_per_threads;
                size_t x = offset % width;
                size_t y = offset / width;
                sh(raster + offset, *this, x, y, 
                        i == nthreads - 1 ? pix_per_threads + (width * height) % nthreads : pix_per_threads);
            });
        }
        for(int i = 0; i < nthreads; i++)
            t[i].join();
    }
    else
    {
        sh(raster, *this, 0, 0, width * height);
    }
    std::cout << ((float)(clock() - c) / CLOCKS_PER_SEC) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

现在它运行速度提高了大约10倍,但线程版本仍然较慢.

Yak*_*ont 6

你所做的是最大化线程之间的争用.

你想最小化它.

线程应该一次(或更多)在扫描线上工作.将图像分成大约相等数量扫描线的n个块(图像左侧),并告诉每个线程在第n个扫描线块上工作.

std::vector<std::thread> threads;
threads.reserve(nthreads);
for(size_t i = 0; i < nthreads; i++) {
  size_t v_start = (h*i)/nthreads;
  size_t v_end = (h*(i+1))/nthreads;
  threads.push_back(std::thread([=]()
  {   
    for(size_t y = v_start; y < v_end; ++y)
    {   
      for (size_t x = 0; x < w; ++x) {
        sh(raster[y * width + x], x, y);
      }
    }
  }));
}
for(auto&& thread:threads)
  thread.join();
Run Code Online (Sandbox Code Playgroud)

另一种方法是获取ppl(并行模式库)并使用它.它将根据当前负载和硬件规格动态平衡线程数,并可能使用线程池来降低线程启动成本.

一个严重的问题是你的Shader sh.你不希望std::function在每个像素的基础上调用任何像函数指针一样昂贵的东西(甚至更昂贵的a ).

我的一般规则是我写了一个"for each pixel"函数,它将像素操作作为a F&&,并在将像素着色器包装在(in-header-file)扫描行中之后将其传递给"for each scanline"函数操作.然后,每个扫描线将间接成本降低到一次.此外,编译器可能能够在像素操作之间进行优化(例如,执行SIMD),而不能以这种方式优化每像素调用.

"interleave"解决方案的最后一个问题是它使编译器无法对代码进行矢量化.矢量化可以轻松实现3-4倍的加速.