Nate February 2016

Why is multithreading slowing down

I'm trying to save a byte array of RGB values as a png image as follows:

byte[] imgArray = ...;
int canvasSize = 512;

ColorModel c = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), null, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);

Image image = Toolkit.getDefaultToolkit().createImage(
            new MemoryImageSource(canvasSize, canvasSize, c, imgArray, 0, canvasSize));

BufferedImage bimage = new BufferedImage(canvasSize, canvasSize, BufferedImage.TYPE_BYTE_GRAY);

// Draw the image on to the buffered image
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(image, 0, 0, null); //This is what takes all the time
bGr.dispose();

ImageIO.write(bimage, "PNG", new File(uniqueFileName));

I'm using a FixedThreadpool to save multiple images simultaneously. The more threads I use (up to the number of free cores I have on my computer), the longer the saving process takes. Running on 6 threads takes almost twice as long as running on one thread.

Why is this taking so much longer with multiple threads? Memory swaps? Can I avoid the problem?

Also, if there is a better way for me to be saving a png from an array, please let me know.

Edited to show pictures are being saved as distinct images, not overwriting each other.

Answers


SubOptimal February 2016

Initially I thought also the main reason might be concurrent write operations. As the amount to write is less than 2 MB there should be normally no bottleneck on disk I/O. After some investigation I found the reason. The ImageIO is using in your case a method which is synchronized (sun.java2d.cmm.lcms.LCMSTransform.doTransform).

I used this small code to confirm the behavior you discovered and to find the lock conditions.

package sub.optimal.jai;

import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.MemoryImageSource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.imageio.ImageIO;

public class ThreadedOutput implements Callable<Boolean> {

    private final String fileName;

    private ThreadedOutput(String name) {
        this.fileName = name;
    }

    @Override
    public Boolean call() throws Exception {
        Thread.currentThread().setName("convert: " + fileName);
        return this.storeImage();
    }

    public boolean storeImage() throws IOException {
        byte[] imgArray = new byte[512 * 512];
        int canvasSize = 512;
        int value = 0;
        for (int i = 0; i < imgArray.length; i++) {
            imgArray[i] = (byte) value;
            value = (++value & 0xFF);
        }

        ColorModel colorModel = new ComponentColorModel(
                ColorSpace.getInstance(ColorSpace.CS_GRAY), null, false,
                false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);

        Image image = Toolkit.getDefaultToolkit().createImage(
                new M 


badsamaritan February 2016

I think this is caused by different type of optimization. You are trying to save multiple images at once in one path - this means a requirement of queuing saving operations - this is a IO-bound task, not CPU-bound. Multiple saving threads may not be very helpful here. Also within very small (in terms of CPU power requirements) operations delegating threads to do the job may only give extra overhead resulting in extending time needed to finish the task, not shortening. Hope this helps :)


Markus Kull February 2016

Lets say your persistent storage (hard disk, usb stick, ssd) writes at 50MB/s. If you write 50MB, then it will always take exactly 1 second, regardless number of threads/cores. That is called a bandwidth bottleneck.

In reality there will also be other bottlenecks. Memory, CPU, or most commonly seek times. Hard disks require milliseconds for seeking to a given block. Writing multiple files simultaneously will cause more seeks and thus may slow down all writes. (Large) Buffers may help there.

Post Status

Asked in February 2016
Viewed 3,778 times
Voted 9
Answered 3 times

Search




Leave an answer