Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my Java program's performance drop significantly after startup?

I am writing a small Java application to analyze a large number of image files. For now, it finds the brightest image in a folder by averaging the brightness of every pixel in the image and comparing it to the other images in the folder.

Sometimes, I get a rate of 100+ images/second right after startup, but this almost always drops to < 20 images/second, and I'm not sure why. When it is at 100+ images/sec, the CPU usage is 100%, but then it drops to around 20%, which seems too low.

Here's the main class:

public class ImageAnalysis {

    public static final ConcurrentLinkedQueue<File> queue = new ConcurrentLinkedQueue<>();
    private static final ConcurrentLinkedQueue<ImageResult> results = new ConcurrentLinkedQueue<>();
    private static int size;
    private static AtomicInteger running = new AtomicInteger();
    private static AtomicInteger completed = new AtomicInteger();
    private static long lastPrint = 0;
    private static int completedAtLastPrint;

    public static void main(String[] args){
        File rio = new File(IO.CAPTURES_DIRECTORY.getAbsolutePath() + File.separator + "Rio de Janeiro");

        String month = "12";

        Collections.addAll(queue, rio.listFiles((dir, name) -> {
            return (name.substring(0, 2).equals(month));
        }));

        size = queue.size();

        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);

        for (int i = 0; i < 8; i++){
            AnalysisThread t = new AnalysisThread();
            t.setPriority(Thread.MAX_PRIORITY);
            executor.execute(t);
            running.incrementAndGet();
        }
    }

    public synchronized static void finished(){
        if (running.decrementAndGet() <= 0){

            ImageResult max = new ImageResult(null, 0);

            for (ImageResult r : results){
                if (r.averageBrightness > max.averageBrightness){
                    max = r;
                }
            }

            System.out.println("Max Red: " + max.averageBrightness + " File: " + max.file.getAbsolutePath());
        }
    }

    public synchronized static void finishedImage(ImageResult result){
        results.add(result);
        int c = completed.incrementAndGet();

        if (System.currentTimeMillis() - lastPrint > 10000){
            System.out.println("Completed: " + c + " / " + size + " = " + ((double) c / (double) size) * 100 + "%");
            System.out.println("Rate: " + ((double) c - (double) completedAtLastPrint) / 10D + " images / sec");
            completedAtLastPrint = c;

            lastPrint = System.currentTimeMillis();
        }
    }

}

And the thread class:

public class AnalysisThread extends Thread {

    @Override
    public void run() {
        while(!ImageAnalysis.queue.isEmpty()) {
            File f = ImageAnalysis.queue.poll();

            BufferedImage image;
            try {
                image = ImageIO.read(f);

                double color = 0;
                for (int x = 0; x < image.getWidth(); x++) {
                    for (int y = 0; y < image.getHeight(); y++) {
                        //Color c = new Color(image.getRGB(x, y));
                        color += image.getRGB(x,y);
                    }
                }

                color /= (image.getWidth() * image.getHeight());

                ImageAnalysis.finishedImage((new ImageResult(f, color)));

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        ImageAnalysis.finished();
    }
}
like image 452
Brian Voter Avatar asked Jan 23 '16 19:01

Brian Voter


People also ask

Why is my Java program so slow?

Poor application design, inefficient methods, loops in Java code, and badly constructed database queries are some of the common causes of poor Java application performance. Slowness can also stem from external accesses from the application code.

What causes performance issues in Java?

Most code-level problems are caused by code structure errors, such as long waits, incorrect iterations, inefficient code algorithms, and improper selection of data structures. Most often, programming problems manifest themselves as loops that occupy CPU cycles in the JVM.


2 Answers

You appear to have a mixed up both using a thread pool and creating threads of your own. I suggest you use on or the other. In fact I suggest you only use the fixed thread pool

Most likely what is happening is your threads are getting an exception which is being lost but killing the task which kills the thread.

I suggest you just the the thread pool, don't attempt to create your own threads, or queue as this is that the ExecutorService does for you. For each task, submit it to the pool, one per image and if you are not going to check the Error of any task, I suggest you trap all Throwable and log them otherwise you could get a RuntimeExcepion or Error and have no idea this happened.

If you have Java 8, a simpler approach would be to use parallelStream(). You can use this to analyse the images concurrently and collect the results without having to divide up the work and collect the results. e.g

List<ImageResults> results = Stream.of(rio.listFiles())
                                   .parallel()
                                   .filter(f -> checkFile(f))
                                   .map(f -> getResultsFor(f))
                                   .list(Collectors.toList());
like image 172
Peter Lawrey Avatar answered Sep 26 '22 16:09

Peter Lawrey


I see two reasons why you may experience CPU usage deterioration:

  • your tasks are very I/O intensive (reading images - ImageIO.read(f));
  • there is thread contention over the synchronized method that your threads access;

Further the sizes of the images may influence execution times.

To exploit parallelism efficiently I would suggest that you redesign your app and implement two kind of tasks that would be submitted to the executor:

  • the first tasks (producers) would be I/O intensive and will read the image data and queue it for in-memory processing;
  • the other (consumers) will pull and analyze the image information;

Then with some profiling you will be able to determine the correct ratio between producers and consumers.

like image 32
Lachezar Balev Avatar answered Sep 22 '22 16:09

Lachezar Balev