Mir ist etwas seltsames aufgefallen, für das ich keine Erklärung finde. Ich habe eine Klasse TimerPanel, die von JPanel erbt. Darin fliegen 1000 ganz billige Bälle umher, die von JComponent erben. Die neue Position dieser Bälle, wird mit setLocation(x,y) gesetzt. Mit System.nanoTime() werden die Zeiten, die die setLocation Aufrufe benötigen gemessen und anschliessend angezeigt.
Wenn ich das Panel nun in ein JFrame lege, dauern die Aufrufe von setLocation bei mir 3 ms. Wenn ich das gleiche Panel in ein JApplet lege, dauert es 60 ms, also 20 Mal mehr. Wieso?
Folgendes Programm kann als Applikation mit einem JFrame oder als JApplet gestartet werden. In beiden Fällen, wird eine Instanz von TimerPanel erstellt und in den jeweiligen Container gelegt.
Das KSKB ist leider etwas gross geworden.
Das Applet kann man auch hier im Browser starten oder downloaden und als Applikation (jar) starten. TimerApplet Informationen und Download
Hier wird auch noch die Zeit, die für das Zeichnen benötigt wird angezeigt und man kann Bälle hinzufügen oder entfernen.
Wieso bringt ein Panel in einem JApplet nicht dieselbe Leistung, wie in einem JFrame?
Wenn ich das Panel nun in ein JFrame lege, dauern die Aufrufe von setLocation bei mir 3 ms. Wenn ich das gleiche Panel in ein JApplet lege, dauert es 60 ms, also 20 Mal mehr. Wieso?
Folgendes Programm kann als Applikation mit einem JFrame oder als JApplet gestartet werden. In beiden Fällen, wird eine Instanz von TimerPanel erstellt und in den jeweiligen Container gelegt.
Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JApplet;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TimerApplet extends JApplet {
// setup frame
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Performance Test Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TimerPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
// setup applet
public void init() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
getContentPane().add(new TimerPanel());
setSize(getPreferredSize());
}
});
}
/**
* Panel für die Bälle, das auch die Zeit misst
*/
private static class TimerPanel extends JPanel {
private static final int MAX_SPEED = 250; // pixel per second
private static final int INIT_BALLS = 1000;
private static final int INIT_DELAY = 20; // in ms
// step time
private long locdt; // meassured in ns
private int n;
private ArrayList<Ball> balls;
public TimerPanel() {
// setup panel
setLayout(null);
setBackground(Color.WHITE);
setSize(600,400);
setPreferredSize(new Dimension(600,400));
// init balls
balls = new ArrayList<Ball>();
addRandomBalls(INIT_BALLS);
// init timer
new Timer(INIT_DELAY,new ActionListener() {
public void actionPerformed(ActionEvent e) {
step(INIT_DELAY);
repaint();
}
}).start();
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.RED);
g.drawString("Bälle: "+n,0,12);
g.drawString("Bälle: "+n,1,12);
g.drawString("setLocation: "+locdt/1000000+" ms",0,24);
g.drawString("setLocation: "+locdt/1000000+" ms",1,24);
}
/**
* Bewege alle Bälle so weit, wie sie in der Zeit dt kommen. Summiere
* dabei die benötigte Zeit für die setLocation Aufrufe auf
* @param dt - wie viele Millisekunden sich die Bälle bewegt haben
*/
private void step(long dt) {
locdt = 0; // reset time
for (Ball ball:balls) {
locdt += ball.step(dt); // sum time for setLocation calls
// collision with border handling
if (ball.vx<0 && ball.getX()<0) ball.vx = -ball.vx;
if (ball.vy<0 && ball.getY()<0) ball.vy = -ball.vy;
if (ball.vx>0 && ball.getX()+ball.size>=getWidth()) ball.vx = -ball.vx;
if (ball.vy>0 && ball.getY()+ball.size>=getHeight()) ball.vy = -ball.vy;
}
}
/**
* Füge m Bälle hinzu
*/
public void addRandomBalls(int m) {
Random rand = new Random();
for (int i=0;i<m;i++) {
Ball ball = new Ball(rand.nextInt(600),
rand.nextInt(400),
rand.nextInt(MAX_SPEED*2)-MAX_SPEED,
rand.nextInt(MAX_SPEED*2)-MAX_SPEED,
rand.nextInt(20)+4,
new Color(rand.nextInt(256*256*256)));
balls.add(ball);
add(ball);
}
n += m;
}
}
/**
* Klasse für die Bälle
*/
private static class Ball extends JComponent {
private float x, y;
private float vx, vy;
private int size;
private Color col;
public Ball(int x, int y, float vx, float vy, int size, Color col) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.size = size;
this.col = col;
setLocation(x, y);
setSize(size,size);
}
/**
* Bewege dich soweit, wie du in dt kommst und gibt die benötigte Zeit
* für setLocation zurück
* @param dt - wie viele Millisekunden der Ball gekommen ist
* @return - Zeit für den setLocation Aufruf
*/
private long step(long dt) {
x = x+vx*dt/1000;
y = y+vy*dt/1000;
int xx = (int) Math.round(x);
int yy = (int) Math.round(y);
// meassure time for setLocation
long t0 = System.nanoTime();
setLocation(xx,yy);
return System.nanoTime()-t0; // time for setLocation
}
public void paintComponent(Graphics g) {
g.setColor(col);
g.fillOval(0,0,size,size);
}
}
}
Das Applet kann man auch hier im Browser starten oder downloaden und als Applikation (jar) starten. TimerApplet Informationen und Download
Hier wird auch noch die Zeit, die für das Zeichnen benötigt wird angezeigt und man kann Bälle hinzufügen oder entfernen.
Wieso bringt ein Panel in einem JApplet nicht dieselbe Leistung, wie in einem JFrame?