Performance von JavaFx Gui vs Swing
Schrieb ich zwei einfache Programme, beide ziehen die gleiche Sierpinski-Dreieck:
Ein Programm wurde implementiert mit swing, und eine mit javafx.
Es gibt einen sehr großen performance-Unterschied, swing Implementierung konsequent viel schneller:
(In diesem Testfall : Schwingen über 1 sec. Javafx über 12 Sekunden)
Ist es zu erwarten, oder gibt es etwas sehr falsch mit meinem javafx-Implementierung ?
Swing Implementierung
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SimpleSrpnskTriSw {
private Triangles triPanel;
SimpleSrpnskTriSw(int numberOfLevels){
JFrame frame = new JFrame("Sierpinski Triangles (swing)");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
triPanel = new Triangles();
frame.add(triPanel, BorderLayout.CENTER);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
triPanel.draw(numberOfLevels);
}
class Triangles extends JPanel{
private static final int PANEL_WIDTH =600, PANEL_HEIGHT = 600;
private static final int TRI_WIDTH= 500, TRI_HEIGHT= 500;
private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH)/2;
private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT)/2;
private int countTriangles;
private long startTime;
boolean working;
private int numberOfLevels = 0;
Triangles() {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
startTime = System.currentTimeMillis();
countTriangles = 0;
working = true;
draw();
}
void draw(int numLevels) {
numberOfLevels = numLevels;
working = true;
draw();
}
void draw() {
startTime = System.currentTimeMillis();
countTriangles = 0;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
repaint();
}
});
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(new Font("Ariel", Font.PLAIN, 14));
if(working) {
g.setColor(getBackground());
g.fillRect(0,0,PANEL_WIDTH,PANEL_HEIGHT);
g.setColor(getForeground());
g.drawString("Working.........", 15, 15);
working = false;
return;
}
if(numberOfLevels <= 0 ) {
return;
}
Point top = new Point(PANEL_WIDTH/2, TOP_GAP);
Point left = new Point(SIDE_GAP, TOP_GAP+ TRI_HEIGHT);
Point right = new Point(SIDE_GAP + TRI_WIDTH, TOP_GAP+ TRI_HEIGHT);
BufferedImage bi = getBufferedImage(top, left, right);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(bi,0,0, this);
g.drawString("Number of triangles: "+ countTriangles, 15, 15);
g.drawString("Time : "+ (System.currentTimeMillis()- startTime)+ " mili seconds", 15, 35);
g.drawString("Levels: "+ numberOfLevels, 15, 50);
}
private BufferedImage getBufferedImage(Point top, Point left, Point right) {
BufferedImage bi = new BufferedImage(PANEL_WIDTH,PANEL_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
drawTriangle(bi, numberOfLevels, top, left, right);
return bi;
}
private void drawTriangle(BufferedImage bi, int levels, Point top, Point left, Point right) {
if(levels < 0) {
return ;
}
countTriangles++;
Graphics g = bi.getGraphics();
g.setColor(Color.RED);
Polygon tri = new Polygon();
tri.addPoint(top.x, top.y); //use top,left right rather than fixed points
tri.addPoint(left.x, left.y);
tri.addPoint(right.x, right.y);
g.drawPolygon(tri);
//Get the midpoint on each edge in the triangle
Point p12 = midpoint(top, left);
Point p23 = midpoint(left, right);
Point p31 = midpoint(right, top);
//recurse on 3 triangular areas
drawTriangle(bi, levels - 1, top, p12, p31);
drawTriangle(bi, levels - 1, p12, left, p23);
drawTriangle(bi, levels - 1, p31, p23, right);
}
private Point midpoint(Point p1, Point p2) {
return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
}
}
public static void main(String[] args) {
new SimpleSrpnskTriSw(13);
}
}
JavaFx-Implementierung
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class SimpleSrpnskTriFx extends Application {
private final int PADDING = 5;
private static int numberOfLevels;
public static void launch(String... args){
numberOfLevels = 8;
if((args != null) && (args.length > 0)) {
try {
int num = Integer.parseInt(args[0]);
numberOfLevels = num ;
} catch (NumberFormatException ex) {
ex.printStackTrace();
return;
}
}
Application.launch(args);
}
@Override
public void start(Stage stage) {
stage.setOnCloseRequest((ae) -> {
Platform.exit();
System.exit(0);
});
stage.setTitle("Sierpinski Triangles (fx)");
BorderPane mainPane = new BorderPane();
mainPane.setPadding(new Insets(PADDING));
Pane triPanel = new Triangles();
BorderPane.setAlignment(triPanel, Pos.CENTER);
mainPane.setCenter(triPanel);
Scene scene = new Scene(mainPane);
stage.setScene(scene);
stage.centerOnScreen();
stage.setResizable(false);
stage.show();
}
class Triangles extends AnchorPane{
private static final int PANEL_WIDTH =600, PANEL_HEIGHT = 600;
private static final int TRI_WIDTH= 500, TRI_HEIGHT= 500;
private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH)/2;
private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT)/2;
private int countTriangles;
private long startTime;
private Point2D top, left, right;
private Canvas canvas;
private Canvas backgroundCanvas;
private GraphicsContext gc;
Triangles(){
setPrefSize(PANEL_WIDTH, PANEL_HEIGHT);
canvas = getCanvas();
backgroundCanvas = getCanvas();
gc = backgroundCanvas.getGraphicsContext2D();
getChildren().add(canvas);
draw(numberOfLevels);
}
void draw(int numberLevels) {
Platform.runLater(new Runnable() {
@Override
public void run() {
canvas.getGraphicsContext2D().fillText("Working....",5,15);
setStartPoints();
startTime = System.currentTimeMillis();
countTriangles = 0;
RunTask task = new RunTask(numberLevels, top, left, right);
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
});
}
private void drawTriangle( int levels, Point2D top, Point2D left, Point2D right) {
if(levels < 0) {//add stop criteria
return ;
}
gc.strokePolygon( //implementing with strokeLine did not make much difference
new double[]{
top.getX(),left.getX(),right.getX()
},
new double[]{
top.getY(),left.getY(), right.getY()
},
3);
countTriangles++;
//Get the midpoint on each edge in the triangle
Point2D p12 = midpoint(top, left);
Point2D p23 = midpoint(left, right);
Point2D p31 = midpoint(right, top);
//recurse on 3 triangular areas
drawTriangle(levels - 1, top, p12, p31);
drawTriangle(levels - 1, p12, left, p23);
drawTriangle(levels - 1, p31, p23, right);
}
private void setStartPoints() {
top = new Point2D(getPrefWidth()/2, TOP_GAP);
left = new Point2D(SIDE_GAP, TOP_GAP + TRI_HEIGHT);
right = new Point2D(SIDE_GAP + TRI_WIDTH, TOP_GAP + TRI_WIDTH);
}
private Point2D midpoint(Point2D p1, Point2D p2) {
return new Point2D((p1.getX() + p2.getX()) /
2, (p1.getY() + p2.getY()) / 2);
}
private void updateGraphics(boolean success){
if(success) {
copyCanvas();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.fillText("Number of triangles: "+ countTriangles,5,15);
gc.fillText("Time : "+ (System.currentTimeMillis()- startTime )+ " mili seconds", 5,35);
gc.fillText("Levels: "+ numberOfLevels,5,55);
}
}
private Canvas getCanvas() {
Canvas canvas = new Canvas();
canvas.widthProperty().bind(widthProperty());
canvas.heightProperty().bind(heightProperty());
canvas.getGraphicsContext2D().setStroke(Color.RED);
canvas.getGraphicsContext2D().setLineWidth(0.3f);
return canvas;
}
private void copyCanvas() {
WritableImage image = backgroundCanvas.snapshot(null, null);
canvas.getGraphicsContext2D().drawImage(image, 0, 0);
}
/**
*/
class RunTask extends Task<Void>{
private int levels;
private Point2D top, left;
private Point2D right;
RunTask(int levels, Point2D top, Point2D left, Point2D right){
this.levels = levels;
this.top = top;
this.left = left;
this.right = right;
startTime = System.currentTimeMillis();
countTriangles = 0;
}
@Override public Void call() {
drawTriangle(levels,top, left, right);
return null;
}
@Override
protected void succeeded() {
updateGraphics(true);
super.succeeded();
}
@Override
protected void failed() {
updateGraphics(false);
}
}
}
public static void main(String[] args) {
launch("13");
}
}
- Der Unterschied in der Leistung (jenseits jeder kleine Unterschied) ist fast immer aufgrund der Unerfahrenheit in das toolkit, das langsamer ist. Da ich aber Schwingen anstatt mit Java-FX, überlasse ich die Java-FX-gurus zu erkennen, die Ineffizienzen in diesem code.
- Dies kann vollständig aus, aber im Versuch, um auszuschließen, alle nicht verwandten Fragen kann es interessant sein, zu verwenden
Point2D.Double
für die Swing-Implementierung. Meine Vermutung ist, dass es nicht zu viel Unterschied, aber man kann nicht helfen, aber Frage mich, wie viel von einem Effekt mitdouble
stattint
hat auf der JavaFX-version der Leistung... - Danke für die Anregung. Ich lief einen test mit
java.awt.Point
in der javafx-Implementierung, um die Auswirkungen der reduzierten Genauigkeit. Es hat eine kleine änderung der Verringerung der Gesamt-Zeit von ~12 sec ~11. - die Bewertung korrekt ist. Ihre Unerfahrenheit mit JavaFX, ähnlich wie in Ihrem Swing-code, der korrigiert werden muss, bevor performance-Probleme zurückgeführt werden können, um das toolkit.
- Die Swing-Beispiel drückt das Bild auf 360.000 Pixel; JavaFX Beispiel Schlaganfälle fast 2,4 Millionen Polygone. Haben Sie versucht, dieses Beispiel mit den Dreiecken?
- Ich habe nicht versucht zu konvertieren, Fraktale Bäume, um Dreiecke, aber ich werde. Können Sie erklären, wo sehen Sie einen Unterschied zwischen den beiden Implementierung ? Die Anzahl der Dreiecke gezählt ist die gleiche in beiden.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dem Swing-Beispiel flacht das Bild zu 6002 = 360,000 Pixel. Im Gegensatz dazu, JavaFX Beispiel Schlaganfälle fast 2,4 Millionen überlappende Polygone als schließlich gerendert. Beachten Sie, dass Ihre JavaFX Beispiel Maßnahmen beide die Zeit zum erstellen der Fraktale und der Zeit zu machen, es in die scene graph.
Wenn Sie wollen, um die Erhaltung der Striche bestehend aus den fraktalen, schreiben das Ergebnis in eine
Canvas
, wie gezeigt hier.Wenn eine flache
Image
ausreichend ist, verfassen Sie das Ergebnis in einerBufferedImage
, konvertieren Sie es in eine JavaFXImage
, und zeigen Sie Sie in einemImageView
, wie unten gezeigt. JavaFX-Ergebnis ist über einen zweiten schneller als die Swing-Beispiel auf meiner hardware.Weil
SwingFXUtils.toFXImage
macht einen kopieren, hintergrundTask<Image>
könnte weiterhin zum aktualisieren einer einzelnenBufferedImage
während die Veröffentlichung vorläufigerImage
Ergebnisse überupdateValue()
.java.awt
Technologie (Polygon, BufferedImage, Grafiken). Die Verwendung vonjavafx
Technologie ist beschränkt auf die Anzeige von dem endgültigen Ergebnis. Ein code, der verwendet "javafx" - Technologie im ganzen, wie ich Sie gestellt , oder diesen code was ist eigentlich Ihr Fraktale Bäume umgewandelt Dreiecke : läuft viel langsamer als die vergleichbarenswing
version. Also meine grundlegende dilemma bleibt immer noch offen.java.awt
mehr, als würde ich ablehnenjava.util
. Ich verstehen nicht das dilemma: einTask<Image>
lassen, Sie halten das hinzufügen neuer Details zu denBufferedImage
für jede neue Ebene; im Gegensatz, dieTask<Canvas>
Beispiel, fängt für jede Ebene, die Rekonstruktion alle vorherigen Stufen..