我正在编写一个函数来改变图像中像素的值。它的工作方式是将每个像素的阴影分割成多个线程。例如,如果有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;
}我听取了一些建议,改变了我的职能。
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;
}现在,它的运行速度大约快了10倍,但线程版本的运行速度仍然较慢。
发布于 2015-04-15 19:26:03
您所做的是最大化线程之间的争用。
你想把它降到最低。
线程应该一次(或更多)在scanline上工作。将您的图像划分为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();另一种方法是获取ppl (并行模式库)并使用它。它将根据当前负载和硬件规范动态平衡线程数,并可能使用线程池来降低线程启动成本。
一个严重的问题是您的Shader sh。您不希望在每像素的基础上调用像函数指针(或者更昂贵的,std::function)那样昂贵的东西。
我的一般规则是编写一个“每个像素”函数,该函数将像素操作作为一个F&&,并将其传递给“为每个扫描线”函数,然后将像素着色器包装在一个(在头文件中)扫描行操作中。然后将间接的成本降低到每条扫描线一次。此外,编译器可以优化像素之间的操作(例如,执行SIMD),而每个像素的调用不能这样优化。
与您的“交织”解决方案的最后一个问题是,它使编译器无法向量化您的代码。矢量化可以很容易地提供3-4倍的加速比。
发布于 2015-04-16 03:29:50
这个答案很简单。线程解决方案实际上更快。它只是消耗了更多的时钟()时间,而clock()函数对计时线程没有好处。
发布于 2015-04-15 19:25:35
在C++中,您可以利用这些内核的并行性,也可以使用amp (加速大规模并行)。我将投票支持后者。
示例放大器项目:http://austin.codeplex.com/
https://msdn.microsoft.com/en-us/library/hh265137.aspx http://blogs.msdn.com/b/nativeconcurrency/archive/2012/08/30/learn-c-amp.aspx
https://stackoverflow.com/questions/29658908
复制相似问题