这里有一些代码。
public void blur(final int x, final int y, final int w, final int h) {
final Picture p = new Picture(this);
IntStream.range(x, x + w).parallel().forEach(i
-> IntStream.range(Y, Y + h).forEach(j
-> {
final Pixel pixel = this.getPixel(i, j);
final java.util.List<Pixel> others
= Arrays.asList(
p.getPixel(i - 1, j),
p.getPixel(i, j - 1),
p.getPixel(i, j + 1),
p.getPixel(i + 1, j),
p.getPixel(i - 1, j - 1),
p.getPixel(i + 1, j + 1),
p.getPixel(i - 1, j + 1),
p.getPixel(i + 1, j - 1),
pixel
);
pixel.setBlue((int) (others.stream()
.mapToInt(Pixel::getBlue).average().getAsDouble()));
pixel.setRed((int) (others.stream()
.mapToInt(Pixel::getRed).average().getAsDouble()));
pixel.setGreen((int) (others.stream()
.mapToInt(Pixel::getGreen).average().getAsDouble()));
})
);
}
某些语言为一系列整数提供并行for循环。 Java似乎没有,但我不觉得多线程正确的方式&#34; (比如fork-join等)
效率这么高吗?我发现这确实比标准for (int i ...
代码更快。我应该将哪个循环(流)并行?这是一个很好的编码实践吗?
答案 0 :(得分:2)
如果表现真的很重要。你应该关注:
特别是,后者取决于内存中像素的布局。它可以对性能产生重大影响,无论它们是逐行还是逐列对齐(例如由于false sharing)。出于这个原因,我建议使用显式并行化。
想象一下,您有一个针对顺序执行进行了优化的方法:
void blurSequential(Picture source, int x, int y, int w, int h);
然后您可以轻松地将图像划分为图块,并在每个图块上独立执行顺序方法。以下代码显示了如何实现它。您必须使用异步执行机制替换async
和await
伪指令,例如使用 ExecutorService 和 Future 。
void blurParallel(Picture source, int x, int y, int w, int h) {
int processors = Runtime.getRuntime().availableProcessors();
blurParallel(source, x, y, w, h, processors * 4);
}
void blurParallel(Picture source, int x, int y, int w, int h, int parallelism) {
if (parallelism <= 1) {
blurSequential(source, x, y, w, h);
} else if (w >= THRESHOLD_WIDTH) {
int m = w / 2;
async blurParallel(source, x, y, m, h, parallelism / 2);
blurParallel(source, x + m, y, w - m, h, parallelism / 2);
await
} else if (h >= THRESHOLD_HEIGHT) {
int m = h / 2;
async blurParallel(source, x, y, w, m, parallelism / 2);
blurParallel(source, x, y + m, w, h - m, parallelism / 2);
await
} else {
blurSequential(source, x, y, w, h);
}
}
答案 1 :(得分:0)
首先,我认为这段代码过于混乱,因此隐藏了它实际上正在做的事情。因此,我提出以下代码,其余部分的流不是所有内容的解决方案。
private List<Pixel> getNeighbours(final Picture picture, final IntTuple intTuple) {
List<Pixel> pixels = new ArrayList<>();
for (int x = intTuple.x - 1; x <= intTuple.x + 1; x++) {
for (int y = intTuple.y - 1; y <= intTuple.y + 1; y++) {
pixels.add(picture.getPixel(x, y));
}
}
return pixels;
}
private void averagePixel(final Picture picture, final IntTuple intTuple) {
double red = 0d;
double green = 0d;
double blue = 0d;
List<Pixel> neighbours = getNeighbours(picture, intTuple);
for (Pixel pixel : neighbours) {
red += pixel.getRed();
green += pixel.getGreen();
blue += pixel.getBlue();
}
Pixel pixel = picture.getPixel(intTuple.x, intTuple.y);
pixel.setRed((int)(red / neighbours.size()));
pixel.setGreen((int)(green / neighbours.size()));
pixel.setBlue((int)(blue / neighbours.size()));
}
IntStream.range(x, x + w)
.parallel()
.boxed()
.flatMap(xVal -> IntStream.range(y, y + h).parallel().mapToObj(yVal -> new IntTuple(xVal, yVal)))
.forEach(tuple -> averagePixel(picture, tuple));
这是做什么的:
IntSream
。Stream<Integer>
,因为没有flatMapToObj
。这是我认为你可以通过ForkJoinPool
获得性能的地方。IntStream
。IntStream
映射到IntTuple
。请注意,您仍然可以在平面地图操作开始时访问x值。Stream<IntTuple>
。averagePixel
操作。其他方法的特别说明:
getNeighbours
在x和y坐标上使用双循环以避免手动编码。averagePixel
不会使用流,因此可以一次性计算红色,绿色和蓝色值。我不认为你可以通过这种方式这样做。我希望这会有所帮助,请注意,此代码未经测试,可能包含实现错误。
答案 2 :(得分:0)
我不知道班级图片。
使用BufferedImage时,Image,Raster可以获得整个像素值数组,例如BufferImage.getRGB。我假设这些基础数据用于图片。
不需要计算列表others
上的平均值:可以立即求和&amp;增加一个柜台。粗略:
int counter = 0;
int sum = 0;
if (j > 0) {
sum += source.getPixel(i, j - 1);
++counter;
}
...
int blurred = sum / counter;
始终是新数据结构(int[9]
左右)的开销,并且进行并行是不值得的。