【问题标题】:Java: Concurrency slower than simple for-LoopJava:并发比简单的for循环慢
【发布时间】:2015-08-13 13:20:16
【问题描述】:

我必须用自定义图像的内容填充 BufferedImage,因为我想在 JFrame 中显示我的自定义图像。

在使用 Profiler 检查代码之前,我使用了一个简单的 for 循环:

for(int x = 0; x < width, x++)
    for(int y = 0; y < height; y++)
        bufferedImage.setRGB(x, height-y-1, toIntColor( customImage.get(x,y) ));

这行得通,但我决定同时尝试。此代码将图像分为列,并应并行复制每一列(代码 sn-p 简化):

final ExecutorService pool = Executors.newCachedThreadPool();
final int columns = Runtime.getRuntime().availableProcessors() +1;
final int columnWidth = getWidth() / columns;

for(int column = 0; column < columns; column++){
        final Rectangle tile = Rectangle.bottomLeftRightTop(
                0,
                columnWidth*column,
                columnWidth*(column+1),
                height
        );

        pool.execute(new ImageConverter(tile));
}


pool.shutdown();
pool.awaitTermination( timeoutSeconds, TimeUnit.SECONDS);

ImageConverterRunnable:

private final class ImageConverter implements Runnable {
    private final Rectangle tile;
    protected ImageConverter(Rectangle tile){ this.tile = tile;  }

    @Override public void run(){
        for(int x = tile.left; x < tile.right; x++)
            for(int y = tile.bottom; y < tile.top; y++)
                bufferedImage.setRGB(x, height-y-1, toIntColor( customImage.get(x,y) ));        }
}

我注意到并发解决方案所用的时间大约是简单 for 循环的两到三倍。 我已经搜索过类似的问题并在 Google 上搜索过,但没有找到任何东西。

为什么需要这么长时间? 是因为 awaitTermination() 行吗? 是否有更好的转换图像的解决方案?

在此先感谢约翰内斯 :)

编辑:

我进行了一些测试。 所有测量的转换都经过 3000 次图像转换的预热。

简单的 for 循环需要 7 到 8 毫秒来复制位图。

每个图像的并行图像复制需要 20 到 24 毫秒。没有预热需要 60 毫秒。

【问题讨论】:

  • 以相反的方式遍历像素(填充行而不是列)可能会提供更好的局部性,因此可能会更快。也可能存在比任何逐像素解决方案快得多的块复制操作。
  • 另外,让一个 Executor 始终可用,并使用 CountDownLatch 等待每次图像传输完成
  • 查看这个相关的example 以获得正确的同步。
  • 谢谢,我已经尝试过保留 Executor,但我不知道 CountDownLatch。我会改变的。 @biziclop 我将切换嵌套的 for 循环。顺便说一句,你知道为什么行可能比列快吗?
  • @Johannes 因为通常图像(尤其是BufferedImage)是按该顺序逐行存储的。因此,如果您以相同的顺序访问数据,与以不同的顺序跳过它们相比,它更有可能已经在缓存中。

标签: java image swing concurrency


【解决方案1】:

使用线程不会加快执行速度(常见的误解)。我的假设是使用线程的开销会导致您的程序运行速度变慢。上下文切换非常昂贵。

一般来说,线程在阻塞时很有用。我在您提供的内容中看不到任何阻止代码。

【讨论】:

  • 谢谢 :) 所以你建议坚持使用 for 循环而不是尝试优化并发设计?图片之间的转换是我的代码中唯一的瓶颈,所以我真的想把它做得更好。
  • 当然线程可以加速执行:如果问题可以并行化,足够大并且你有可用的内核
  • 我在同一个项目中使用线程,它总是提高性能,但显然不是图像转换方法。我认为图像复制可以并行化,但是我的代码中隐藏了一个错误
猜你喜欢
  • 1970-01-01
  • 2020-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多