多线程图像处理比单线程慢?
Multithreaded Image Processing slower than Single Thread?
我正在 Objective-C 中处理图像并试用了 Grand Central Dispatch,结果很糟糕。 CPU 使用量翻倍,处理图像的时间翻倍。
- (void) processImage:(struct ImageData)image {
imageData = image;
[allyMinionManager prepareForPixelProcessing];
int cores = 4;
int section = imageData.imageHeight/cores;
if (section < 1) {
section = 1;
}
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < cores; i++) {
int yStart = section * i;
int yEnd = yStart + section;
if (i == cores - 1) {
yEnd = imageData.imageHeight;
}
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
for (int y = yStart; y < yEnd; y++) {
uint8_t *pixel = imageData.imageData + (y * imageData.imageWidth)*4;
for (int x = 0; x < imageData.imageWidth; x++) {
[allyMinionManager processPixel:pixel x:x y:y];
pixel += 4;
}
}
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
[allyMinionManager postPixelProcessing:imageData];
}
我在核心之间分离图像,然后等待分派的任务完成。如果我有一张 1200x800 的图像,每个线程应该处理 240,000 个像素。
核心设置为 1:95% CPU 使用率,16 毫秒处理时间
核心设置为 4:120% CPU 使用率,33 毫秒处理时间
知道为什么性能这么差吗?
(奖励问题:当我将编译器优化标志设置为最低时,我的 CPU 使用率在我的程序中从 45% 增加到 300%。这正常吗?)
我经常发现使用多线程代码通常无法获得理论上预期的好处。有递减的returns。有关更全面的讨论,请参阅 iOS Concurrency - Not reaching anywhere's near theoretical maximum。
请注意,您可以使用 dispatch_group_async
,这将消除手动进入和离开组的需要(如果您要分派的代码块本身是异步的,您只需要这样做)。更好的是,考虑到你在最后等待,你应该考虑使用 dispatch_apply
,它完全消除了组。
在 Mac 上使用 dispatch_apply
、运行 我发现 2 个线程占用了单线程实现所用时间的 78%,4 个占用了 47% 的时间,8 个占了 38% 的时间。因此,线程数量的增加带来了好处,但它们正在减少 returns。
在 iPhone 6+ 上,当我从一个线程转到两个线程时,我经历了类似的性能改进,但是当我转到四个或八个线程时,我没有意识到除此之外没有进一步的性能改进。
所以,最重要的是,我建议尝试 dispatch_apply
。如果设备上的性能提升很快就趋于平稳,也不要感到惊讶。所以尝试只使用 2 "cores" 看看结果是什么。
我正在 Objective-C 中处理图像并试用了 Grand Central Dispatch,结果很糟糕。 CPU 使用量翻倍,处理图像的时间翻倍。
- (void) processImage:(struct ImageData)image {
imageData = image;
[allyMinionManager prepareForPixelProcessing];
int cores = 4;
int section = imageData.imageHeight/cores;
if (section < 1) {
section = 1;
}
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < cores; i++) {
int yStart = section * i;
int yEnd = yStart + section;
if (i == cores - 1) {
yEnd = imageData.imageHeight;
}
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
for (int y = yStart; y < yEnd; y++) {
uint8_t *pixel = imageData.imageData + (y * imageData.imageWidth)*4;
for (int x = 0; x < imageData.imageWidth; x++) {
[allyMinionManager processPixel:pixel x:x y:y];
pixel += 4;
}
}
dispatch_group_leave(group);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
[allyMinionManager postPixelProcessing:imageData];
}
我在核心之间分离图像,然后等待分派的任务完成。如果我有一张 1200x800 的图像,每个线程应该处理 240,000 个像素。
核心设置为 1:95% CPU 使用率,16 毫秒处理时间
核心设置为 4:120% CPU 使用率,33 毫秒处理时间
知道为什么性能这么差吗?
(奖励问题:当我将编译器优化标志设置为最低时,我的 CPU 使用率在我的程序中从 45% 增加到 300%。这正常吗?)
我经常发现使用多线程代码通常无法获得理论上预期的好处。有递减的returns。有关更全面的讨论,请参阅 iOS Concurrency - Not reaching anywhere's near theoretical maximum。
请注意,您可以使用 dispatch_group_async
,这将消除手动进入和离开组的需要(如果您要分派的代码块本身是异步的,您只需要这样做)。更好的是,考虑到你在最后等待,你应该考虑使用 dispatch_apply
,它完全消除了组。
在 Mac 上使用 dispatch_apply
、运行 我发现 2 个线程占用了单线程实现所用时间的 78%,4 个占用了 47% 的时间,8 个占了 38% 的时间。因此,线程数量的增加带来了好处,但它们正在减少 returns。
在 iPhone 6+ 上,当我从一个线程转到两个线程时,我经历了类似的性能改进,但是当我转到四个或八个线程时,我没有意识到除此之外没有进一步的性能改进。
所以,最重要的是,我建议尝试 dispatch_apply
。如果设备上的性能提升很快就趋于平稳,也不要感到惊讶。所以尝试只使用 2 "cores" 看看结果是什么。