PNG der Performancekiller

Status
Nicht offen für weitere Antworten.

Chris_1980

Bekanntes Mitglied
Ich mußte eben feststellen das ein png-Bild mit den Maßen 448*580px,
gespeichert als BufferedImage,
35ms braucht um auf den Bildschirm gebracht zu werden. :shock:
Selbst wenn das Bild nichts weiter ist als eine schwarze Fläche.
Das selbe Bild (ob nun einfach schwarz oder ein aufwendiges Foto) als jpg gespeichert braucht grade mal 0,5ms :meld:

MFG, Chris :wink:
 

Marco13

Top Contributor
Klingt erstmal seltsam - waren beide BufferedImages vom gleichen Typ? bei PNG könnte ja noch ein Alpha-Kanal dabei sein...
 

Wildcard

Top Contributor
Weiterhin interessant:
Wie zum Teufel willst du das messen und welches OS hat eine 0,5ms Genauigkeit bei der Zeitgebung? :shock:
Windoof liegt da eher bei 6-20 und Linux knapp darunter...
 

Chris_1980

Bekanntes Mitglied
Wenn bei einem Spiel ein loop statt gewollter 5ms auf einmal 35ms braucht merkt man das mit Sicherheit. :roll:

Ich hab mal daraus ein KSKB gebastelt:

Code:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class FPSTest extends JPanel{

	private int frame = 0;
	private long time, loopTime = 0 ;
	private double loopAverage = 0;
	private int fps = 0;
	private String out="working...";
	
	boolean loop = true;
	
	private ArrayList<String> log = new ArrayList<String>(500);
	
	private BufferedImage testPic;
	private VolatileImage bufferImage;
	
	public static final int JPG_TEST = 0, PNG_TEST = 1;
	
	public FPSTest(int test)
	{
		this.setIgnoreRepaint(true);
		this.setSize(448, 580);
		
		try
		{
			switch(test)
			{
				case JPG_TEST:
					testPic = ImageIO.read(getClass().getResource("test.jpg"));
				break;	
				
				case PNG_TEST:
					testPic = ImageIO.read(getClass().getResource("test.png"));
				break;	
			}
			
		}
		catch (IOException e)
		{			
			e.printStackTrace();
		}		
	}
	
	
	public void loop()
	{		
		bufferImage = createVolatileImage(448, 580);	
		time = System.nanoTime();
		long t;	
		
		while(loop)
		{
			t = System.nanoTime();	
			
			paintOffscreen();
			paintOnscreen();
			
			frame++;
			
			loopTime += (System.nanoTime() - t);
			
			if(t >= (time+1000000000))
			{
				fps =(int) (((double)frame*1000000000/(System.nanoTime()-time)));
				
				loopAverage = ((double)loopTime/frame);		
				
				out = "FPS: "+fps + "     LoopAverage: "+(loopAverage/1000000)+"ms";
	    		log.add(out);
	    		if(log.size() >= 20) loop = false;
				
				time = System.nanoTime();
				loopTime = 0;				
				frame = 0;
			}
		}
		
		for(int i=0 ; i<log.size() ; i++)
		{
			System.out.println(log.get(i));
		}
	}
	
	private void paintOnscreen()
	{		
		 do 
		 {
			 int returnCode =  bufferImage.validate(getGraphicsConfiguration());
			 if (returnCode == VolatileImage.IMAGE_RESTORED) 
			 {
				 // Contents need to be restored
				 paintOffscreen();      // restore contents
			 } 
			 else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) 
			 {
				 // old  bufferImage doesn't work with new GraphicsConfig; re-create it
				 bufferImage = createVolatileImage(448, 580);
				 paintOffscreen();
			 }

			 this.getGraphics().drawImage( bufferImage, 0, 0, this);

		 } 
		 while ( bufferImage.contentsLost());
		
		this.getGraphics().drawImage(bufferImage, 0, 0, this);

		frame++;
	}
	
	private void paintOffscreen()
	{	
        do 
        {
            if (bufferImage.validate(this.getGraphicsConfiguration()) ==
                VolatileImage.IMAGE_INCOMPATIBLE)
            {
                // old bufferImage doesn't work with new GraphicsConfig; re-create it
            	bufferImage = createVolatileImage(448, 580);
            }
            
            Graphics2D g = bufferImage.createGraphics();
            
    		g.drawImage(testPic, 0, 0, this);

    		g.setColor(Color.YELLOW);
    		    		
    		g.drawString(out, 2, 20);

    		g.dispose();    		
        }
        while (bufferImage.contentsLost());		
	}
	
	
	public static void main(String[] args)
	{
		JFrame frame = new JFrame("FPS-Test");
		frame.setLayout(null);
		frame.setSize(500, 650);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		FPSTest test1 = new FPSTest(PNG_TEST);
		System.out.println("PNG-Test:");
		frame.add(test1);
		frame.setVisible(true);
		
		test1.loop();

		
		frame.remove(test1);
		
		System.out.println("\n\n\nJPG-Test:");	
		
		FPSTest test2 = new FPSTest(JPG_TEST);
		frame.add(test2);
		frame.validate();

		test2.loop();
	}
}
Hier als Download mit den Bildern die ich dafür benutzt habe: FPSTest.zip

So siehts bei mir aus:

  • PNG-Test:
    FPS: 37 LoopAverage: 26.71238245ms
    FPS: 42 LoopAverage: 23.763173195652175ms
    FPS: 41 LoopAverage: 23.8221935ms
    FPS: 42 LoopAverage: 23.636936652173915ms
    FPS: 42 LoopAverage: 23.661897304347825ms
    FPS: 42 LoopAverage: 23.706638239130434ms
    FPS: 38 LoopAverage: 25.75347264285714ms
    FPS: 41 LoopAverage: 24.269063363636363ms
    FPS: 41 LoopAverage: 23.812968068181817ms
    FPS: 41 LoopAverage: 23.973609363636363ms
    FPS: 42 LoopAverage: 23.702277695652175ms
    FPS: 42 LoopAverage: 23.68079693478261ms
    FPS: 42 LoopAverage: 23.504371782608697ms
    FPS: 41 LoopAverage: 24.24835229545455ms
    FPS: 42 LoopAverage: 23.473738826086958ms
    FPS: 42 LoopAverage: 23.665158543478263ms
    FPS: 40 LoopAverage: 24.46766025ms
    FPS: 43 LoopAverage: 22.78047606521739ms
    FPS: 44 LoopAverage: 22.632368979166667ms
    FPS: 44 LoopAverage: 22.668255791666667ms

    JPG-Test:
    FPS: 7669 LoopAverage: 0.12929569225755996ms
    FPS: 7831 LoopAverage: 0.1265894831503702ms
    FPS: 7883 LoopAverage: 0.12576443127060613ms
    FPS: 7855 LoopAverage: 0.1261469282260117ms
    FPS: 7907 LoopAverage: 0.12556141188369152ms
    FPS: 7904 LoopAverage: 0.1256353459787557ms
    FPS: 7938 LoopAverage: 0.12508518395870058ms
    FPS: 7905 LoopAverage: 0.12553890414769853ms
    FPS: 7926 LoopAverage: 0.12527676242118538ms
    FPS: 7877 LoopAverage: 0.12605681015228426ms
    FPS: 7918 LoopAverage: 0.12541177448258456ms
    FPS: 7890 LoopAverage: 0.12584906042563973ms
    FPS: 7922 LoopAverage: 0.12534590045420135ms
    FPS: 7892 LoopAverage: 0.1258120944782168ms
    FPS: 7928 LoopAverage: 0.12524763930912758ms
    FPS: 7889 LoopAverage: 0.12586945970603142ms
    FPS: 7919 LoopAverage: 0.1253843307245645ms
    FPS: 7897 LoopAverage: 0.12574538708860758ms
    FPS: 7903 LoopAverage: 0.12563200164432078ms
    FPS: 7749 LoopAverage: 0.12807452863777088ms

Etwas andere Werte, da noch schlankere Testbedingungen, aber IMO absolut eindeutig. :wink:[/u]
 

Chris_1980

Bekanntes Mitglied
Ups... zweimal auf den Knopf gedrückt :gaen:

Edit 1:
@ Beim PNG ist ein Alpha dabei. Mal gucken wie es ohne aus sieht. Gestern habe ich die Bilder mit ImageIO.write

als PNG gespeichert. Eins war mikrige 939Byte groß, eben einfach nur Schwarz. Aber das Ergebnis war das Selbe. Aber mal gucken wie es ohne Alpha in dem KSKB hier aussieht...

Edit 2:

Ohne Alphakanal gibts keinerlei Veränderung:

PNG-Test ohne AlphaKanal:
FPS: 38 LoopAverage: 26.20190026190476ms
FPS: 42 LoopAverage: 23.783682195652176ms
FPS: 42 LoopAverage: 23.708277956521737ms
FPS: 42 LoopAverage: 23.598548195652175ms
FPS: 42 LoopAverage: 23.663609913043476ms
FPS: 42 LoopAverage: 23.55084347826087ms
FPS: 37 LoopAverage: 26.79767955ms
FPS: 36 LoopAverage: 27.129907550000002ms
FPS: 41 LoopAverage: 23.839834608695654ms
FPS: 41 LoopAverage: 24.096472977272725ms
FPS: 42 LoopAverage: 23.576290043478263ms
FPS: 42 LoopAverage: 23.608556739130435ms
FPS: 40 LoopAverage: 24.66364125ms
FPS: 41 LoopAverage: 24.286980772727272ms
FPS: 42 LoopAverage: 23.773497608695653ms
FPS: 42 LoopAverage: 23.6903925ms
FPS: 41 LoopAverage: 23.81774654347826ms
FPS: 42 LoopAverage: 23.60215563043478ms
FPS: 42 LoopAverage: 23.71024563043478ms
FPS: 42 LoopAverage: 23.553151369565217ms
 

Wildcard

Top Contributor
Davon abgesehen das diese Messgenauigkeit völlig illusorisch ist muss man für ausgeglichene Bedingungen sorgen:
Code:
	public FPSTest(int test)
	{
		this.setIgnoreRepaint(true);
		this.setSize(448, 580);
		
		try
		{
			switch(test)
			{
				case JPG_TEST:
					
					BufferedImage temp = ImageIO.read(getClass().getResource("test.jpg"));
					testPic = new BufferedImage(temp.getWidth(),temp.getHeight(),BufferedImage.TYPE_3BYTE_BGR);
					Graphics g = testPic.createGraphics();
					g.drawImage(temp, 0, 0, null);
					g.dispose();
				break;	
				
				case PNG_TEST:
					temp = ImageIO.read(getClass().getResource("test.png"));
					testPic = new BufferedImage(temp.getWidth(),temp.getHeight(),BufferedImage.TYPE_3BYTE_BGR);
					g = testPic.createGraphics();
					g.drawImage(temp, 0, 0, null);
					g.dispose();
				break;	
				
			}
			
		}
		catch (IOException e)
		{			
			e.printStackTrace();
		}		
	}
Ergebnis:
JPG-Test:
FPS: 636 LoopAverage: 1.5711870595611286ms
FPS: 676 LoopAverage: 1.4772942147058823ms
FPS: 679 LoopAverage: 1.4709691568914958ms
FPS: 682 LoopAverage: 1.4648440451895042ms
FPS: 678 LoopAverage: 1.4726326392961877ms
FPS: 656 LoopAverage: 1.521318290909091ms
FPS: 664 LoopAverage: 1.503249883233533ms
FPS: 685 LoopAverage: 1.4568358677325581ms
FPS: 685 LoopAverage: 1.4573811933139535ms
FPS: 637 LoopAverage: 1.566499284375ms
FPS: 684 LoopAverage: 1.459842300872093ms
FPS: 681 LoopAverage: 1.4653925423976608ms
FPS: 683 LoopAverage: 1.461461530612245ms
FPS: 685 LoopAverage: 1.4574737790697674ms
FPS: 648 LoopAverage: 1.5403151273006135ms
FPS: 685 LoopAverage: 1.4571627601744186ms
FPS: 686 LoopAverage: 1.4561647652173912ms
FPS: 686 LoopAverage: 1.4566817869565216ms
FPS: 683 LoopAverage: 1.4615303513119533ms
FPS: 641 LoopAverage: 1.5582040372670807ms



PNG-Test:
FPS: 650 LoopAverage: 1.5352279250764524ms
FPS: 686 LoopAverage: 1.4552566086956522ms
FPS: 687 LoopAverage: 1.4529824420289856ms
FPS: 687 LoopAverage: 1.4529103637681158ms
FPS: 649 LoopAverage: 1.5393227806748466ms
FPS: 686 LoopAverage: 1.4559542173913045ms
FPS: 685 LoopAverage: 1.458115784883721ms
FPS: 682 LoopAverage: 1.4638072376093294ms
FPS: 688 LoopAverage: 1.4523251604046241ms
FPS: 653 LoopAverage: 1.5285862713414633ms
FPS: 686 LoopAverage: 1.4558582608695652ms
FPS: 687 LoopAverage: 1.4547084173913043ms
FPS: 687 LoopAverage: 1.4538451855072463ms
FPS: 685 LoopAverage: 1.4578376104651163ms
FPS: 686 LoopAverage: 1.455535584057971ms
FPS: 681 LoopAverage: 1.4655632777777778ms
FPS: 686 LoopAverage: 1.4560845739130435ms
FPS: 687 LoopAverage: 1.4532508420289856ms
FPS: 687 LoopAverage: 1.4536788ms
FPS: 688 LoopAverage: 1.4516001156069365ms

Was lernen wir daraus?
Es ist absolut egal welches Bild zum einlesen verwendet wird, einzig die Image-Capabilities spielen eine Rolle was ja auch intuitiv einleuchtend sein sollte.
 

Chris_1980

Bekanntes Mitglied
Nun, welch eine Überraschung. Das wenn man ein png quasi in ein JPG konvertiert, man auch gleiche Werte rausbekommt ist wohl nicht sehr verwunderlich.
Dennoch ist der Aspekt mit dem konvertieren ganz interessant. Mal sehen was los ist wenn ich ein BufferedImage mit PNG, was scheinbar vom Typ Custom ist zu einem vom Typ ARGB mache. Denn es gibt wohl nur einen Grund PNG überhaupt zu benutzen, und zwar die Transparenz.

Fraglich finde ich allerdings was du mit "völlig illusorisch" meinst.
 

Wildcard

Top Contributor
Chris_1980 hat gesagt.:
Nun, welch eine Überraschung. Das wenn man ein png quasi in ein JPG konvertiert, man auch gleiche Werte rausbekommt ist wohl nicht sehr verwunderlich.
Du kannst eben schlecht ein viel aufwendigeres ColorModel mit einem wesentlich weniger komplexen Vergleichen und dich wundern wenn das eine langsamer als das andere ist.
Es ist deine Aufgabe als Programmierer deine Bilder (sofern du auf Performance wert legst) auf ein ColorModel zu beschränken das du wirklich brauchst, denn ImageIO wird dir immer das mit zurückgeben das alle Möglichkeiten des Dateiformats unterstützt.

Fraglich finde ich allerdings was du mit "völlig illusorisch" meinst.
Vor allem 2 Dinge:
Du hast keine 700 FPS. Welcher Bildschirm sollte das denn schaffen?
Wenn ich von deinem Dateiformat auf dein OS schließe verwendest du Windows.
Die Windows API für Systemzeit ist allerdings bekanntermaßen verbuggt und ist nicht in der Lage Zeiten genauer als ca. 10ms aufzulösen.
Deine Messwerte suggerieren allerdings eine deutlich höhere Genauigkeit. Wer sich darauf verlässt ist verlassen...

Alleine schon das Vorhandensein mehrere Threads lässt diese ganzen Messungen wesentlich komplizierter werden als es auf den ersten Blick scheint. Letztlich fehlen dir schlicht die Möglichkeiten zu erkennen was AWT, das OS und zuletzt die Hardware tatsächlich aus deinen Anweisungen macht.
Es steht nicht zur Debate das ein Custom-Type langsamer als 3-byte-bgr ist, aus diesen Zahlen jedoch echte Geschwindigkeitsfaktoren ablesen zu wollen ist nicht drin.
 

Chris_1980

Bekanntes Mitglied
Achso, das meinst du mit illusorisch. Das mein Schirm grade mal 60 Frames bringt ist schon klar.
Es ging mir ja auch nur darum aufzuzeigen das dort ein imenser Unterschied ist.
Egal wie ungenau die Zahlen sein mögen, die Differenz sagt auf jeden Fall was aus. Bei dem gleichen Weg der Messung und über eine Quersumme kann ich auf jedenfall sagen das das eine XFach solange braucht wie das andere.

Mein Fazit zu der Geschichte ist somit:
Ein PNG ist in Natura nicht zu gebrauchen. Wenn ich die schöne Transparenz eines solchen Bildes brauche, wandle ich das Image in Eins vom Typ ARGB um.
Das ist zwar immernoch ca. 4 mal langsammer wie Eins vom Typ 3B-BGR , dafür aber Transparent.
Wenn ich keine transparenz benötige nehme ich also nurnoch jpg, denn da brauch ich keinen Konvertierungskrams zu veranstallten.

Vielen Dank für die interessante Diskussion Mr. Wildcard. :)
 
S

SlaterB

Gast
hier werden doch keine einzelnen Zeichenvorgänge gemessen,
sondern nur alle 1 Sekunde eine Statistik erstellt,
was stören da 10 ms Abweichung?
die Auswertung scheint mir sehr genau, so wie gewünscht,

was diese abstrakten Zahlen wie 700 FPS nun bedeuten, kann man nicht herauslesen, aber zumindest scheinen sie doch konstant und nicht zufällig und bei anderen Bildern auch weit höher oder niedriger liegen zu können,
insofern eine sinnvolle Arbeitsmessung (welcher Arbeit auch immer) abhängig vom Bildtyp
 

Wildcard

Top Contributor
Chris_1980 hat gesagt.:
Wenn ich keine transparenz benötige nehme ich also nurnoch jpg, denn da brauch ich keinen Konvertierungskrams zu veranstallten.
JPG hat halt den gravierenden Nachteil der Verlusthaftigkeit und ist daher für scharfe Kanten und Schrift nicht zu gebrauchen.
 

Wildcard

Top Contributor
SlaterB hat gesagt.:
hier werden doch keine einzelnen Zeichenvorgänge gemessen,
sondern nur alle 1 Sekunde eine Statistik erstellt,
was stören da 10 ms Abweichung?
die Auswertung scheint mir sehr genau, so wie gewünscht,
Ich denke wir sind uns einig darüber das es hauptsächlich darum geht wie lange es dauert ein bestimmtes Image zu zeichnen.
Sehen wir uns dazu die API-Doc von Graphics#drawImage an:
* This method returns immediately in all cases, even if the
* complete image has not yet been loaded, and it has not been dithered
* and converted for the current output device.
Was wird hier also gemessen?
Aus eben diesen Gründen behaupte ich das man hier gerade keine Faktoren ablesen kann.
Das messen von echter Performance im Zeichen-Bereich ist nicht trivial.
 

Wildcard

Top Contributor
Um das nochmal für alle die über diesen Thread stolpern klar zu stellen:
Unbehandelte pngs sind tatsächlich sogar deutlich langsamer als andere Images (vor allem bei VolatileImages).
Der Grund dafür ist das nur bitmasken Transparenz (wie bei gif) Hardware-Beschleunigt laufen kann und ein png daher nicht von VolatileImage profitiert.
Einzig und alleine der resultierende Faktor ist ein strittiger Punkt...
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
T Performancekiller? Allgemeine Java-Themen 12

Ähnliche Java Themen

Neue Themen


Oben