Sto scrivendo una piccola applicazione Java per analizzare un gran numero di file di immagine. Per ora, trova l'immagine più luminosa in una cartella calcolando la media di luminosità di ogni pixel nell'immagine e confrontandola con le altre immagini nella cartella.Perché le prestazioni del mio programma Java diminuiscono significativamente dopo l'avvio?
A volte, ottengo un tasso di 100+ immagini/secondo subito dopo l'avvio, ma questo scende quasi sempre a < 20 immagini al secondo, e non sono sicuro del perché. Quando è a 100+ immagini al secondo, l'utilizzo della CPU è al 100%, ma poi scende a circa il 20%, il che sembra troppo basso.
Ecco la classe principale:
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();
}
}
}
E la classe thread:
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();
}
}
Puoi eseguire la tua app con un profiler come jvisualvm e usare il suo campionatore per misurare quale metodo impiega più tempo. Inoltre, provare a eseguirlo in un singolo thread e osservare eventuali differenze. I thread potrebbero bloccarsi a vicenda su determinate operazioni. –
La tua classe estende 'Thread,' e stai impostando la sua priorità, ma la stai eseguendo tramite un 'Executor', che lo tratta come un 'Runnable', quindi tutto questo è inutile. Se vuoi influenzare la priorità devi definire un 'ThreadFactory'. Altrimenti la tua classe potrebbe anche implementare Runnable. – EJP
Hai provato: 'ImageIO.setUseCache (false);'? – user3707125