Возможно ли сделать эти циклы с параллельными потоками? - PullRequest
0 голосов
/ 26 января 2020

Если у меня есть этот код, который выполняется во вложенном l oop

int height = ...;
int width = ...;
int sourceIncrement = ...;
int destIncrement = ...;
int sourceOffset = ...;
int destOffset = ...;
int  sourceArray[] = ...;
byte destArray[] = ...;

for (int i = 0; i < height; i++) {                              
    for (int j = 0; j < width; j++) {               
        int pixel = sourceArray[sourceOffset ++];           
        destArray[destOffset ++] = (byte) (pixel      );
        destArray[destOffset ++] = (byte) (pixel >>  8);
        destArray[destOffset ++] = (byte) (pixel >> 16);
        destArray[destOffset ++] = (byte) (pixel >> 24);
    }                                           
    sourceOffset += sourceIncrement;                      
    destOffset += destIncrement;                     
}                                 

, и я хочу, чтобы он работал параллельно, я попытаюсь сделать это с потоками

IntStream.range(0, height).parallel().forEach(y -> {    
    IntStream.range(0, width).parallel().forEach(x -> {
        int pixel = sourceArray[sourceOffset ++];              
        destArray[destOffset ++] = (byte) (pixel      );   
        destArray[destOffset ++] = (byte) (pixel >>  8);   
        destArray[destOffset ++] = (byte) (pixel >> 16);   
        destArray[destOffset ++] = (byte) (pixel >> 24);   
    });                                            
    sourceOffset += sourceIncrement;                         
    destOffset += destIncrement;                        
}); 

но это неверно, потому что смещения будут увеличиваться до того, как код, который зависит от них, будет запущен. Можно ли на самом деле заставить его работать параллельно правильно?

1 Ответ

1 голос
/ 27 января 2020

Не выполнять ручное копирование.

Вы можете использовать, например,

IntBuffer src = IntBuffer.wrap(sourceArray, sourceOffset, sourceArray.length-sourceOffset);
IntBuffer dst = ByteBuffer.wrap(destArray, destOffset, destArray.length - destOffset)
        .order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();

for(int i = 0; i < height; i++) {
    dst.put(src.limit(src.position()+width));
    src.limit(src.capacity()).position(src.position() + sourceIncrement);
    dst.position(dst.position() + (destIncrement >> 2));
}

Это предполагает, что destIncrement описывает пиксельные единицы, то есть кратно четырем. Кроме того, предполагается, что массивы достаточно длинные, что составляет sourceIncrement соответственно. destIncrement место для последней строки, которая не записана, но буфер не позволяет установить позицию после последней строки в противном случае.

Если у массивов нет этой комнаты, вы бы необходимо выйти из l oop в последнем ряду, прежде чем увеличивать позиции:

if(height > 0) {
    IntBuffer src = IntBuffer.wrap(sourceArray, sourceOffset,
                                                sourceArray.length - sourceOffset);
    IntBuffer dst = ByteBuffer.wrap(destArray, destOffset, destArray.length - destOffset)
            .order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();

    for(int i = 0; ;) {
        dst.put(src.limit(src.position()+width));
        if(++i == height) break;
        src.limit(src.capacity()).position(src.position() + sourceIncrement);
        dst.position(dst.position() + (destIncrement >> 2));
    }
}

Не ясно, выиграет ли это от параллельной обработки вообще, но для завершения, вот параллельный вариант:

IntBuffer src = IntBuffer.wrap(sourceArray, sourceOffset, (width+sourceIncrement)*height);
IntBuffer dst = ByteBuffer.wrap(destArray, destOffset, (width*4+destIncrement)*height)
        .order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();

int srcRow = width + sourceIncrement, dstRow = width + (destIncrement>>2);
IntStream.range(0, height).parallel()
    .forEach(y -> dst.slice().position(y * dstRow)
        .put(src.slice().position(y * srcRow).limit(y * srcRow + width))
    );

Поскольку положение и лимит буфера не являются потокобезопасными, мы должны создать локальный буфер. Это решение использует slice(), который инкапсулирует начальные значения sourceOffset соотв. destOffset, упрощающий расчет позиции и лимита для операции перевода. Вычисление их до перевода также гарантирует, что позиция никогда не будет продвигаться дальше, чем width последнего ряда.

...