Last active
December 22, 2015 14:28
-
-
Save hendrikebbers/eb87093510b1f8e80415 to your computer and use it in GitHub Desktop.
JavaFX Performance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.sun.javafx.scene.control.skin.ButtonSkin; | |
import com.sun.javafx.scene.control.skin.TextFieldSkin; | |
import javafx.application.Application; | |
import javafx.application.Platform; | |
import javafx.beans.property.*; | |
import javafx.embed.swing.SwingNode; | |
import javafx.scene.Node; | |
import javafx.scene.Scene; | |
import javafx.scene.control.Button; | |
import javafx.scene.control.*; | |
import javafx.scene.control.TextField; | |
import javafx.scene.layout.FlowPane; | |
import javafx.scene.layout.VBox; | |
import javafx.scene.paint.Color; | |
import javafx.scene.shape.Rectangle; | |
import javafx.stage.Stage; | |
import javax.swing.*; | |
import java.awt.*; | |
import java.util.List; | |
import java.util.concurrent.Executors; | |
import java.util.function.Supplier; | |
import java.util.stream.Collectors; | |
import java.util.stream.IntStream; | |
import java.util.stream.Stream; | |
public class TextFieldPerformance extends Application { | |
public static void main(String[] args) { | |
launch(args); | |
} | |
private final FlowPane flow = new FlowPane(); | |
private long startTime; | |
private BooleanProperty createInBackground = new SimpleBooleanProperty(); | |
private IntegerProperty count = new SimpleIntegerProperty(); | |
private BooleanProperty parallelCreation = new SimpleBooleanProperty(); | |
private BooleanProperty initCss = new SimpleBooleanProperty(); | |
private BooleanProperty initSkin = new SimpleBooleanProperty(); | |
private ObjectProperty<NodeFactory> nodeFactory = new SimpleObjectProperty<>(); | |
@Override | |
public void start(Stage primaryStage) { | |
primaryStage.setTitle("JavaFX Node Creation Benchmark"); | |
Spinner<Integer> controlCountSpinner = new Spinner<>(100, 10000, 3000, 100); | |
controlCountSpinner.setTooltip(new Tooltip("Define the number of new nodes")); | |
count.bind(controlCountSpinner.valueProperty()); | |
CheckBox threadCheckBox = new CheckBox("Create Nodes in Background Thread"); | |
threadCheckBox.setTooltip(new Tooltip("If selected all nodes will be created in a background thread")); | |
createInBackground.bind(threadCheckBox.selectedProperty()); | |
CheckBox parallelCheckBox = new CheckBox("parallel"); | |
parallelCheckBox.setTooltip(new Tooltip("If selected all nodes will be created in multiple background thread (parallel stream)")); | |
parallelCheckBox.disableProperty().bind(threadCheckBox.selectedProperty().not()); | |
parallelCreation.bind(parallelCheckBox.selectedProperty()); | |
CheckBox initCssCheckBox = new CheckBox("Init CSS"); | |
initCssCheckBox.setTooltip(new Tooltip("If selected the inital CSS will be called before adding the node")); | |
initCss.bind(initCssCheckBox.selectedProperty()); | |
CheckBox initSkinCheckBox = new CheckBox("Init Skin"); | |
initSkinCheckBox.setTooltip(new Tooltip("If selected the Skin will be created before adding the node")); | |
initSkin.bind(initSkinCheckBox.selectedProperty()); | |
ChoiceBox<NodeFactory> nodeFactoryChoiceBox = new ChoiceBox<>(); | |
parallelCheckBox.setTooltip(new Tooltip("Defines the node type that will be created")); | |
nodeFactoryChoiceBox.getItems().add(new TextFieldFactory()); | |
nodeFactoryChoiceBox.getItems().add(new RectangleFactory()); | |
nodeFactoryChoiceBox.getItems().add(new ButtonFactory()); | |
nodeFactoryChoiceBox.getItems().add(new SwingTextFieldFactory()); | |
nodeFactoryChoiceBox.getSelectionModel().select(0); | |
nodeFactory.bind(nodeFactoryChoiceBox.valueProperty()); | |
Button createControlsButton = new Button("Create nodes"); | |
createControlsButton.setOnAction(event -> { | |
printSomeLines(); | |
startTime = System.currentTimeMillis(); | |
if (!createInBackground.get()) { | |
createNodesAndAdd(); | |
} else { | |
Executors.newSingleThreadExecutor().execute(() -> { | |
createNodesAndAdd(); | |
}); | |
} | |
}); | |
Button removeControlsButton = new Button("Remove all nodes"); | |
removeControlsButton.setOnAction(event -> { | |
printSomeLines(); | |
startTime = System.currentTimeMillis(); | |
measureRunTime("RemoveControls", () -> flow.getChildren().clear()); | |
}); | |
ToolBar toolBar = new ToolBar(nodeFactoryChoiceBox, controlCountSpinner, createControlsButton, removeControlsButton, initCssCheckBox, initSkinCheckBox, threadCheckBox, parallelCheckBox); | |
primaryStage.setScene(new Scene(new VBox(toolBar, flow), 1000, 1000)); | |
primaryStage.show(); | |
// stop Mesuring when elements are displayed | |
flow.needsLayoutProperty().addListener((observable, oldValue, newValue) -> { | |
if (startTime > 0 && !newValue) { | |
long duration = System.currentTimeMillis() - startTime; | |
System.out.println("Duration until controls are visible: " + duration); | |
System.out.println("Node Count: " + flow.getChildren().size()); | |
startTime = -1; | |
} | |
}); | |
} | |
private void createNodesAndAdd() { | |
List<Node> nodes = measureSupplyTime("NodeCreation", () -> { | |
IntStream stream = IntStream.range(0, count.get()); | |
if (parallelCreation.get()) { | |
stream = stream.parallel(); | |
} | |
return stream.mapToObj(i -> nodeFactory.get().create()).collect(Collectors.toList()); | |
}); | |
if (initSkin.get()) { | |
measureRunTime("InitSkin", () -> { | |
Stream<Node> stream = nodes.stream(); | |
if (parallelCreation.get()) { | |
stream = stream.parallel(); | |
} | |
stream.forEach(node -> nodeFactory.get().initSkin(node)); | |
}); | |
} | |
if (initCss.get()) { | |
measureRunTime("InitCSS", () -> { | |
Stream<Node> stream = nodes.stream(); | |
if (parallelCreation.get()) { | |
stream = stream.parallel(); | |
} | |
stream.forEach(node -> nodeFactory.get().initCSS(node)); | |
}); | |
} | |
if (!Platform.isFxApplicationThread()) { | |
Platform.runLater(() -> { | |
measureRunTime("AddToScene", () -> flow.getChildren().addAll(nodes)); | |
}); | |
} else { | |
measureRunTime("AddToScene", () -> flow.getChildren().addAll(nodes)); | |
} | |
} | |
private <T> T measureSupplyTime(String name, Supplier<T> supplier) { | |
long currentTime = System.currentTimeMillis(); | |
try { | |
return supplier.get(); | |
} finally { | |
System.out.println("Duration for " + name + ": " + (System.currentTimeMillis() - currentTime)); | |
} | |
} | |
private void measureRunTime(String name, Runnable runnable) { | |
measureSupplyTime(name, () -> { | |
runnable.run(); | |
return null; | |
}); | |
} | |
private void printSomeLines() { | |
System.out.println(); | |
System.out.println("----------------------------------------------------"); | |
} | |
private interface NodeFactory<T extends Node> { | |
T create(); | |
default void initCSS(T node) { | |
node.applyCss(); | |
} | |
void initSkin(T node); | |
} | |
private class TextFieldFactory implements NodeFactory<TextField> { | |
@Override | |
public TextField create() { | |
TextField textfield = new TextField(); | |
textfield.setMinSize(10, 10); | |
textfield.setPrefSize(10, 10); | |
textfield.setMaxSize(10, 10); | |
return textfield; | |
} | |
@Override | |
public void initSkin(TextField node) { | |
node.setSkin(new TextFieldSkin(node)); | |
} | |
@Override | |
public String toString() { | |
return "TextField"; | |
} | |
} | |
private class SwingTextFieldFactory implements NodeFactory<SwingNode> { | |
@Override | |
public SwingNode create() { | |
JTextField textfield = new JTextField(); | |
textfield.setMinimumSize(new Dimension(10, 10)); | |
textfield.setPreferredSize(new Dimension(10, 10)); | |
textfield.setMaximumSize(new Dimension(10, 10)); | |
SwingNode swingNode = new SwingNode(); | |
swingNode.setContent(textfield); | |
return swingNode; | |
} | |
@Override | |
public void initSkin(SwingNode node) { | |
} | |
@Override | |
public String toString() { | |
return "Swing TextField"; | |
} | |
} | |
private class RectangleFactory implements NodeFactory<Rectangle> { | |
@Override | |
public Rectangle create() { | |
Rectangle rectangle = new Rectangle(10, 10); | |
rectangle.setFill(new Color(Math.random(), Math.random(), Math.random(), 1.0)); | |
return rectangle; | |
} | |
@Override | |
public void initSkin(Rectangle node) { | |
} | |
@Override | |
public String toString() { | |
return "Rectangle"; | |
} | |
} | |
private class ButtonFactory implements NodeFactory<Button> { | |
@Override | |
public Button create() { | |
Button button = new Button(); | |
button.setMinSize(10, 10); | |
button.setPrefSize(10, 10); | |
button.setMaxSize(10, 10); | |
return button; | |
} | |
@Override | |
public void initSkin(Button node) { | |
node.setSkin(new ButtonSkin(node)); | |
} | |
@Override | |
public String toString() { | |
return "Button"; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment