When programming in Java using the Eclipse IDE, I sometimes use the debug function. Quite a lot of the time I like to change variables in real time, whilst the programs are running. However, quite often I find that changing some variables won't actually affect the currently running program.
My question is: are there certain rules for debugging? which variables are in scope to the debugger or something?
I'm sorry if this is a stupid question. I'm fairly new to debugging in Eclipse, and programming in general.
The code below is a sample. I'm sorry if it's hard to read or whatever but here's the issue: In the Ball class, whenever I alter the final variables such as PARTICLES_PER_CLICK or SPEED, they are updated in real time and I can see the difference in the program window, however when I alter the RADIUS variable, it does nothing, even though it's in the same class as the other two variables.
public class Main {
public static final int WIDTH = 1280;
public static final int HEIGHT = 720;
public static Ball[] ball = new Ball[100000];
public Main() {
try {
Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
Display.create();
Display.setTitle("Game Engine");
} catch (LWJGLException e) {
e.printStackTrace();
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, WIDTH, HEIGHT, 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
boolean[] doOnce = new boolean[10];
boolean gravity = false;
while (!Display.isCloseRequested()) {
if (Mouse.isButtonDown(1) || Mouse.isButtonDown(1)) {
if (!doOnce[0]) {
Ball.createParticles();
doOnce[0]=true;
}
} else {
doOnce[0] = false;
}
glClear(GL_COLOR_BUFFER_BIT);
for (int i = 1; i <= Ball.ballAmount; i++) {
ball[i].updatePosition(gravity);
if (Mouse.isButtonDown(0)) {
gravity = true;
} else {
gravity = false;
}
if (ball[i].position.x > 0 && ball[i].position.y > 0 && ball[i].position.x < WIDTH && ball[i].position.y < HEIGHT) {
glBegin(GL_TRIANGLE_FAN);
glVertex2d(ball[i].position.x, ball[i].position.y);
for(int u=0;u<=360;u+=5){
glVertex2d(ball[i].position.x+Math.cos(u)*Ball.RADIUS, ball[i].position.y+Math.sin(u)*Ball.RADIUS);
}
glEnd();
}
}
System.out.println("Particle Amount: " + Ball.ballAmount);
Display.update();
Display.sync(60);
}
Display.destroy();
System.exit(0);
}
public static void main(String[] args) {
new Main();
}
}
class Ball {
public Vector2f position, velocity;
public static final int RADIUS = 10;
public static final int INITIAL_SPEED = 5;
public static final int SPEED = 2;
public static final int PARTICLES_PER_CLICK = 50;
public static int ballAmount = 0;
public double r, g, b;
public static Random rnd = new Random();
public Ball(double x, double y) {
int angle = rnd.nextInt(360);
position = new Vector2f((float) x, (float) y);
velocity = new Vector2f((float) Math.cos(angle) * rnd.nextFloat() * INITIAL_SPEED, (float) Math.sin(angle) * rnd.nextFloat() * INITIAL_SPEED);
this.r = rnd.nextDouble();
this.g = rnd.nextDouble();
this.b = rnd.nextDouble();
}
public void updatePosition(boolean gravity) {
this.position.x += this.velocity.x * SPEED;
this.position.y += this.velocity.y * SPEED;
if (gravity) {
double dx = this.position.x - Mouse.getX();
double dy = this.position.y - (Main.HEIGHT - Mouse.getY());
double distance = Math.sqrt(dx * dx + dy * dy);
this.velocity.x -= (this.position.x - Mouse.getX()) / distance;
this.velocity.y -= (this.position.y - (Main.HEIGHT - Mouse.getY())) / distance;
} else {
this.velocity.x *= 0.99;
this.velocity.y *= 0.99;
}
}
public static void createParticles() {
for (int i = 1; i <= PARTICLES_PER_CLICK; i++) {
ballAmount += 1;
Main.ball[ballAmount] = new Ball(Mouse.getX(),Main.HEIGHT- Mouse.getY());
}
}
}
If the optimizer sees a final variable, it knows it's value will not change. What it does with that knowledge is up to it. It might do nothing (like it seems to happen in your case with PARTICLES_PER_CLICK or SPEED), or it might simply replace all occurrences of that variable with the actual value everywhere. There are no special rules, beyond do not change the values of final variables.
There are no special rules, beyond do not change the values of final variables.
Actually, Eclipse 4.23 (Q2 2022, seven years later) is now clearer:
Warning about changing final fields
Since Eclipse 3.1 Eclipse Java debugger allows changes on final field values.
While technically possible, the consequences of such changes are not trivial, could affect seemingly unrelated code and lead to various dangerous effects.
Therefore, with Eclipse 4.23 Java debugger shows now a new warning:
This warning is enabled by default and can be disabled via preferences:
Additionally, "org.eclipse.debug.ui.variableValueEditors" extension point is updated to allow custom products contribute their own "variableValueEditor" implementations to existing debug models and have even more control over final fields modifications.
Related
Hello Stack Overflow people :)
I'm a huge newbie when it comes to coding, and I've just ran into a problem that my brain just won't get over...
Before I start blabbering about this issue, I'll paste my code so as to give a little bit of context (sorry in advance if looking at it makes you wanna puke). The main focus of the issue is commented and should therefore be fairly visible :
Main
ArrayList<Individual> individuals = new ArrayList<Individual>();
void setup()
{
size(500,500);
for(int i = 0; i < 2; i++)
{
individuals.add(new Individual());
}
println(frameRate);
}
void draw()
{
background(230);
for(int i = 0; i < individuals.size(); i++)
{
individuals.get(i).move();
individuals.get(i).increaseTimers();
individuals.get(i).display();
}
}
Individual
class Individual
{
float x;
float y;
int size = 5;
Timer rdyBreed; /* Here is the object that appears to be shared
between individuals of the ArrayList */
float breedRate;
float breedLimit;
Individual()
{
x = random(0, width);
y = random(0, height);
rdyBreed = new Timer("rdyBreed", 0);
breedRate = random(.2, 3);
breedLimit = random(10, 20);
}
void move()
{
int i = (int)random(0, 1.999);
int j = (int)random(0, 1.999);
if (i == 0)
{
x = x + 1;
} else
{
x = x - 1;
}
if (j == 0)
{
y = y + 1;
} else
{
y = y - 1;
}
checkWalls();
}
void checkWalls()
{
if (x < size/2)
{
x = width - size/2;
}
if (x > width - size/2)
{
x = size/2;
}
if (y < size/2)
{
y = width - size/2;
}
if (y > width - size/2)
{
y = size/2;
}
}
void display()
{
noStroke();
if (!rdyBreed.finished)
{
fill(255, 0, 0);
} else
{
fill(0, 255, 0);
}
rect(x - size/2, y - size/2, size, size);
}
void increaseTimers()
{
updateBreedTimer();
}
void updateBreedTimer()
{
rdyBreed.increase(frameRate/1000);
rdyBreed.checkLimit(breedLimit);
rdyBreed.display(x, y);
}
}
Timer
class Timer
{
float t;
String name;
boolean finished = false;
Timer(String name, float t)
{
this.t = t;
this.name = name;
}
void increase(float step)
{
if (!finished)
{
t = t + step;
}
}
void checkLimit(float limit)
{
if (t >= limit)
{
t = 0;
finished = true;
}
}
void display(float x, float y)
{
textAlign(RIGHT);
textSize(12);
text(nf(t, 2, 1), x - 2, y - 2);
}
}
Now that that's done, let's get to my question.
Basically, I'm trying to create some sort of a personal Conway's Game of Life, and I'm encountering a lot of issues right off the bat.
Now my idea when writing this piece of code was that every individual making up the small simulated "society" would have different timers and values for different life events, like mating to have children for example.
Problem is, I'm not a huge pro at object-oriented programming, and I'm therefore quite clueless as to why the objects are not having each their own Timer but both a reference to the same timer.
I would guess making an ArrayList of timers and using polymorphism to my advantage could make a change, but I'm not really certain of it or really how to do it so... yeah, I need help.
Thanks in advance :)
EDIT : Here is a screenshot of the debugger. The values keep being the same with each iteration of the updates.
Screenshot
What makes you think they reference the same Timer object? The values of t displayed in the debugger are going to be the same until one of them reaches the breedLimit and gets set to 0, because they're being initialized at the same time.
Try this and see that the values of t are different.
void setup() {
size(500,500);
}
void mouseClicked() {
individuals.add(new Individual());
}
I'd recommend setting the breakpoint somewhere around here:
t = 0;
finished = true;
They do not share the same timer, you create a new Timer object for each Individual.
class Individual {
// ...
Timer rdyBreed;
Individual() {
// ...
rdyBreed = new Timer("rdyBreed", 0);
//...
The only way they could be sharing the same Timer is if you were setting rdyBreed elsewhere, but since you don't want that I recommend making it final.
If you did want to share the same Timer instance across all individuals then you could declare it static.
How do I add some code into my multiplayer game that I am creating using ThinMatrix's tutorials on YouTube? I have been following his tutorials when I decided to go... well... on a tangent and start adding my own things to my game. Could someone help me with some code that updates the position of player entities in the game? I already know how to do (basic) networking.
Thank you!
Edit: Code added from comments
Camera Class:
package entities;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.util.vector.Vector3f;
public class Camera {
private float distanceFromPlayer = 35;
private float angleAroundPlayer = 0;
private Vector3f position = new Vector3f(0, 0, 0);
private float pitch = 20;
private float yaw = 0;
private float roll;
private Player player;
public Camera(Player player){
this.player = player;
}
public void move(){
calculateZoom();
calculatePitch();
calculateAngleAroundPlayer();
float horizontalDistance = calculateHorizontalDistance();
float verticalDistance = calculateVerticalDistance();
calculateCameraPosition(horizontalDistance, verticalDistance);
this.yaw = 180 - (player.getRotY() + angleAroundPlayer);
}
public Vector3f getPosition() {
return position;
}
public float getPitch() {
return pitch;
}
public float getYaw() {
return yaw;
}
public float getRoll() {
return roll;
}
private void calculateCameraPosition(float horizDistance, float verticDistance){
float theta = player.getRotY() + angleAroundPlayer;
float offsetX = (float) (horizDistance * Math.sin(Math.toRadians(theta)));
float offsetZ = (float) (horizDistance * Math.cos(Math.toRadians(theta)));
position.x = player.getPosition().x - offsetX;
position.z = player.getPosition().z - offsetZ;
position.y = player.getPosition().y + verticDistance + 4;
}
private float calculateHorizontalDistance(){
return (float) (distanceFromPlayer * Math.cos(Math.toRadians(pitch+4)));
}
private float calculateVerticalDistance(){
return (float) (distanceFromPlayer * Math.sin(Math.toRadians(pitch+4)));
}
private void calculateZoom(){
float zoomLevel = Mouse.getDWheel() * 0.03f;
distanceFromPlayer -= zoomLevel;
if(distanceFromPlayer<5){
distanceFromPlayer = 5;
}
}
private void calculatePitch(){
if(Mouse.isButtonDown(1)){
float pitchChange = Mouse.getDY() * 0.2f;
pitch -= pitchChange;
if(pitch < 0){
pitch = 0;
}else if(pitch > 90){
pitch = 90;
}
}
}
private void calculateAngleAroundPlayer(){
if(Mouse.isButtonDown(0)){
float angleChange = Mouse.getDX() * 0.3f;
angleAroundPlayer -= angleChange;
}
}
}
Player Class:
package entities;
import models.TexturedModel;
import org.lwjgl.input.Keyboard;
import org.lwjgl.util.vector.Vector3f;
import renderEngine.DisplayManager;
import terrains.Terrain;
public class Player extends Entity {
private static final float RUN_SPEED = 40;
private static final float TURN_SPEED = 160;
private static final float GRAVITY = -50;
private static final float JUMP_POWER = 18;
private float currentSpeed = 0;
private float currentTurnSpeed = 0;
private float upwardsSpeed = 0;
private boolean isInAir = false;
public Player(TexturedModel model, Vector3f position, float rotX, float rotY, float rotZ,
float scale) {
super(model, position, rotX, rotY, rotZ, scale);
}
public void move(Terrain terrain) {
checkInputs();
super.increaseRotation(0, currentTurnSpeed * DisplayManager.getFrameTimeSeconds(), 0);
float distance = currentSpeed * DisplayManager.getFrameTimeSeconds();
float dx = (float) (distance * Math.sin(Math.toRadians(super.getRotY())));
float dz = (float) (distance * Math.cos(Math.toRadians(super.getRotY())));
super.increasePosition(dx, 0, dz);
upwardsSpeed += GRAVITY * DisplayManager.getFrameTimeSeconds();
super.increasePosition(0, upwardsSpeed * DisplayManager.getFrameTimeSeconds(), 0);
float terrainHeight = terrain.getHeightOfTerrain(getPosition().x, getPosition().z);
if (super.getPosition().y < terrainHeight) {
upwardsSpeed = 0;
isInAir = false;
super.getPosition().y = terrainHeight;
}
}
private void jump() {
if (!isInAir) {
this.upwardsSpeed = JUMP_POWER;
isInAir = true;
}
}
private void checkInputs() {
if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
this.currentSpeed = RUN_SPEED;
} else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
this.currentSpeed = -RUN_SPEED;
} else {
this.currentSpeed = 0;
}
if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
this.currentTurnSpeed = -TURN_SPEED;
} else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
this.currentTurnSpeed = TURN_SPEED;
} else {
this.currentTurnSpeed = 0;
}
if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
jump();
}
}
}
You need to make it so your player class can return the current locations and doing so makes things easy as all you need to do is create another instance of the game with new packages and run it but on the server end you need to send the players current location and make this update consistently as this would use a coordinate system but this is very basic so you will already need an entity in the game such as a player entity in the server side of the game and doing so you need this entity to update its position so you could do something like player.getPosition() and then serverSideModel = player.getPosition() as it would set both models to the same position if that makes sence
The task you want to achieve is a simple one, but because of the way your code is written it has become an unnecessarily hard task to achieve. Fixing your code is not something that you are ready for without understanding more about Object Oriented code and efficient networking.
So at this point I will not attempt to fix your code but I will simply suggest a way to make it work somewhat, and be aware that you will have a lot of issues later if you continue this project.
First we need a simple way to deal with updating the position of a Player entity. We can do this by adding a method to your Player class a bit like so:
public void moveByFloatInput(fload newX, float newY, float newY) {
//Here you need to get the current terrain object for this player entity
Terrain currentTerrain = getTerrain();
//Now update the terrain object with your new x,y,z points (not sure what methods Terrain contains, you may need to make changes or expose more variables)
currentTerrain.setNewPosition(newX, newY, newZ);
//Now call the normal move method using the updated terrain with a new position
move(updatedTerrainObject);
}
Note: there may be a better way to do this, but I have no idea how the terrain class is constructed, and what methods can be called. Also note that you will want to pass the direction the player is looking, but to keep this example simple I have not included any of that.
Now when we receive the x y and z info from a client/server we can update a player entity, for example if you where using an InputStreamReader (not the best idea, but it will help you get started):
float x;
float y;
float z;
while ((message = myBufferedReaderInputFromSocket.readLine()) != null) {
if (message != null) {
//check if X
if (message.startsWith("moveX"))
//save new X so we can update a player entity
x = Float.parseFloat(message.substring(5));
//check if Y
if (message.startsWith("moveY"))
//save new Y so we can update a player entity
y = Float.parseFloat(message.substring(5));
//check if Z
if (message.startsWith("moveZ"))
//save new Z so we can update a player entity
z = Float.parseFloat(message.substring(5));
}
//when x, y and y all have a new position we can update a player entity
if(x != null && y != null && z != null)
{
//call our new player move method and change that player entities position
playerEntityForThisSocket.moveByFloatInput(x, y, z);
//reset x,y,z to null so that the server can receive the next movement
x = null;
y = null;
z = null;
}
}
Then for sending over a socket you can do it a bit like:
//you could create a Print writer for your socket:
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
//then simply send a players new position info
//inculde the "moveX" keyword before the Float x,y,z so that the server/client at the other end knows how to process the info:
out.println(moveX + newXposition);
out.println(moveY + newYposition);
out.println(moveZ + newZposition);
If this does not make sense then I suggest that you complete the basic Java tutorials before you continue coding, because the tutorials contain the essential building blocks of coding with Java, and with that knowledge that you can build on to make a great project, but without that knowledge you are doomed to have never ending issues:
http://docs.oracle.com/javase/tutorial/index.html
I am doing an assignment from the Java Exposure textbook, which was written in 2007. This book includes some code that I usually update to use some of the more recent features (just basic stuff). However, in this one I am running into a problem. All I tried to do is replace the show with setVisible(true) and change the Frame to a JFrame and add a gfx.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);. However, I noticed that this wouldn't actually cause the window to close. If I clicked many times, maybe 1/30 tries it would close. If I reduced the delay from 10 to 1, it usually closed within 2 tries. This of course led me to believe that the delay method is causing this erratic behavior. I tried Thread.sleep, but of course that didn't work. Is there any simply way to get this code so that the frame will close when I hit the close button? If there isn't, what would be the less simple way of doing it?
Here is the code:
// Lab30st.java
// The Screen Saver Program
// Student Version
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.JOptionPane;
public class Lab30st
{
public static void main(String args[])
{
GfxApp gfx = new GfxApp();
gfx.setSize(800,600);
gfx.addWindowListener(new WindowAdapter() {public void
windowClosing(WindowEvent e) {System.exit(0);}});
gfx.show();
}
}
class GfxApp extends Frame
{
private int circleCount, circleSize;
public GfxApp()
{
circleCount = 50;
circleSize = 30;
}
class Coord
{
private int xPos;
private int yPos;
public Coord(int x, int y)
{
xPos = x;
yPos = y;
}
}
public void paint(Graphics g)
{
int incX = 5;
int incY = 5;
int diameter = 30;
int timeDelay = 10;
Circle c = new Circle(g,diameter,incX,incY,timeDelay);
for (int k = 1; k <= 2000; k++)
{
c.drawCircle(g);
c.hitEdge();
}
}
}
class Circle
{
private int tlX; // top-left X coordinate
private int tlY; // top-left Y coordinate
private int incX; // increment movement of X coordinate
private int incY; // increment movement of Y coordinate
private boolean addX; // flag to determine add/subtract of increment for X
private boolean addY; // flag to determine add/subtract of increment for Y
private int size; // diameter of the circle
private int timeDelay; // time delay until next circle is drawn
public Circle(Graphics g, int s, int x, int y, int td)
{
incX = x;
incY = y;
size = s;
addX = true;
addY = false;
tlX = 400;
tlY = 300;
timeDelay = td;
}
public void delay(int n)
{
long startDelay = System.currentTimeMillis();
long endDelay = 0;
while (endDelay - startDelay < n)
endDelay = System.currentTimeMillis();
}
public void drawCircle(Graphics g)
{
g.setColor(Color.blue);
g.drawOval(tlX,tlY,size,size);
delay(timeDelay);
if (addX)
tlX+=incX;
else
tlX-=incX;
if (addY)
tlY+=incY;
else
tlY-=incY;
}
public void newData()
{
incX = (int) Math.round(Math.random() * 7 + 5);
incY = (int) Math.round(Math.random() * 7 + 5);
}
public void hitEdge()
{
boolean flag = false;
if (tlX < incX)
{
addX = true;
flag = true;
}
if (tlX > 800 - (30 + incX))
{
addX = false;
flag = true;
}
if (tlY < incY + 30) // The +30 is due to the fact that the title bar covers the top 30 pixels of the window
{
addY = true;
flag = true;
}
if (tlY > 600 - (30 + incY))
{
addY = false;
flag = true;
}
if (flag)
newData();
}
}
You are "freezing" the Event Dispatch Thread with
public void delay(int n)
{
long startDelay = System.currentTimeMillis();
long endDelay = 0;
while (endDelay - startDelay < n)
endDelay = System.currentTimeMillis();
}
This means that all the other stuff that is trying to happen (like closing the window) has to wait until the thread comes out of the "sleep".
basically you shouldn't be doing the delay in the EDT, it should be on a different thread and then ask the EDT thread to update.
Your "busy wait" delay may cause other problems too. You can improve the behavior by using Thread.sleep()
See Java Event-Dispatching Thread explanation
That's terrible.
You need to restructure the whole code.
Let's start with the really bad:
delay is (almost) a busy wait, I haven't seen busy waits since BASIC was modern. It basically holds the CPU hostage to the thread, not only does it do nothing, no other thread (almost) can use the time slice. The reason I say almost is that calling the system time function causes a context switch that could allow other threads to run, but it is still bad.
The still pretty bad:
Replacing with Thread.sleep. Better yes, no busy wait, but you are still holding the one and only UI thread. This means no other UI work can happen up to and including closing the main window.
What needs to happen:
Get an external timer (e.g. javax.swing.Timer) to trigger the draw event and do next part of the animation.
Search for "Java smooth animation" there are many examples of how to do this, double buffer and all.
I'm writing a multithreaded fractal drawing program with JavaFX 2.2 and now I need some guidance.
What I'm trying to achieve is to create a Task or Service (haven't decided yet) which then fires up some other tasks that actually do the calculation and return sections of the whole image when ready. When all the pieces are returned to the initiating task it puts together the pieces and returns it to the main thread so it can be visualized.
Obviously, all this must happen without ever blocking the UI.
The problem is I can't figure out how these tasks could communicate with each other. For example, I need to update the progress property of the initiating task based on the average progress of the tasks inside it (or something like this), so their progress properties should be bound to the progress property of the initiating task somehow. The image pieces should be put in a list or some container and redrawn on a separate image when all of them are available.
I have already written a simpler (though still experimental) version of this program that creates only one task that calculates the whole fractal. The progress is bound to the progressBar of the GUI. The return value is handled by an EventHandler on success of the task.
I'm not asking for a complete solution but some ideas with maybe a little bit of example code would really help me.
This is the class that should be modified:
package fractal;
import fractalUtil.DefaultPalette;
import fractalUtil.PaletteInterface;
import javafx.concurrent.Task;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import org.apache.commons.math3.complex.Complex;
/**
*
* #author athelionas
*/
public abstract class AbstractFractal extends Task implements FractalInterface {
private PaletteInterface palette;
protected final int width, height, order, iterations;
protected final double scale, xReal, xIm, xCenter, yCenter, zoom;
protected final boolean julia;
protected AbstractFractal(final int width, final int height, final double xReal, final double xIm, final double xCenter, final double yCenter, final int order, final boolean julia, final int iterations, final double zoom) {
this.width = width;
this.height = height;
this.xReal = xReal;
this.xIm = xIm;
this.xCenter = xCenter;
this.yCenter = yCenter;
this.order = order;
this.julia = julia;
this.iterations = iterations;
this.zoom = zoom;
this.scale = (double) width / (double) height;
palette = new DefaultPalette();
}
#Override
public final void setPalette(final PaletteInterface palette) {
this.palette = palette;
}
#Override
public abstract Complex formula(final Complex z, final Complex c, final int order, final Complex center);
#Override
public final Color calculatePoint(final Complex z, final Complex c, final int order, final Complex center, final int iterations) {
Complex zTemp = z;
int iter = iterations;
while (zTemp.abs() <= 2.0 && iter > 0) {
zTemp = formula(zTemp, c, order, center);
iter--;
}
if (iter == 0) {
return Color.rgb(0, 0, 0);
} else {
return palette.pickColor((double) (iterations - iter) / (double) iterations);
}
}
#Override
public final WritableImage call() {
Complex z;
Complex c;
Complex center = new Complex(xCenter, yCenter);
final WritableImage image = new WritableImage(width, height);
if (julia) {
c = new Complex(xReal, xIm);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
z = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom));
image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations));
}
}
} else {
z = new Complex(xReal, xIm);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
c = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom));
image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations));
}
updateProgress(y, height);
}
}
return image;
}
}
Use binding and Task. This way you don't need to care about threading at all. All you need is to create a binding which will normalize each progress according to threads number and summ them up. E.g.
progressBar.progressProperty().bind(
task1.progressProperty().multiply(0.5).add(
task2.progressProperty().multiply(0.5)));
It's a bit trickier for unknown number of threads. See next example:
public class MultiProgressTask extends Application {
private static final int THREADS_NUM = 10;
// this is our Task which produces a Node and track progress
private static class MyTask extends Task<Node> {
private final int delay = new Random().nextInt(1000) + 100;
{ System.out.println("I update progress every " + delay); }
#Override
protected Node call() throws Exception {
updateProgress(0, 5);
for (int i = 0; i < 5; i++) {
System.out.println(i);
Thread.sleep(delay); // imitating activity
updateProgress(i+1, 5);
}
System.out.println("done");
return new Rectangle(20, 20, Color.RED);
}
};
#Override
public void start(Stage primaryStage) {
ProgressBar pb = new ProgressBar(0);
pb.setMinWidth(300);
final VBox root = new VBox();
root.getChildren().add(pb);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
DoubleBinding progress = null;
for (int i = 0; i < THREADS_NUM; i++) {
final MyTask mt = new MyTask();
// here goes binding creation
DoubleBinding scaledProgress = mt.progressProperty().divide(THREADS_NUM);
if (progress == null) {
progress = scaledProgress;
} else {
progress = progress.add(scaledProgress);
}
// here you process the result of MyTask
mt.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
root.getChildren().add((Node)t.getSource().getValue());
}
});
new Thread(mt).start();
}
pb.progressProperty().bind(progress);
}
public static void main(String[] args) { launch(args); }
}
This is a pretty interesting problem :)
If we remove the issue of thread safety for a moment, you could pass in a double property (or whatever the progress property is bound to) and update that with the progress which would then update the progress indicator. Two problems with that:
Multiple tasks could increment the property at the same time.
The changes must be fired on the javafx thread.
I would wrap the property in it's own class with a simple API:
class ProgressModel {
private final SimpleDoubleProperty progress;
public void increment(finally double increment) {
Platform.runLater(new Runnable() {
progress.set(progress.doubleValue() + increment);
}
}
public void bindPropertyToProgress(DoubleProperty property) {
property.bind(progress);
}
}
In the above code, all updates will run on the javafx thread sequentially so it is thread safe plus no locks. I have done similar background tasks like this and performance has been good (realtime to the user's eyes) although if you're updating thousands of times a second this might not be the case! You will just need to measure. I've not shown the boiler plate code to make it a bit more readable.
in order to implement some image analysis algorithms without having to worry too much on the data type (i.e. without having too much duplicate code), I'm setting up the visitor pattern for primitive arrays in Java.
In the example below, I've defined two types of visitors
a primitive type, where the signature of the visit method is visit(int, int double)
a generic type, where the signature of the visit method is visit(int, int Double).
Appart from this, both visitors do exactly the same operations. My idea was to try and measure the cost of boxing/unboxing.
So here is the full program
public class VisitorsBenchmark {
public interface Array2DGenericVisitor<TYPE, RET> {
void begin(int width, int height);
RET end();
void visit(int x, int y, TYPE value);
}
public interface Array2DPrimitiveVisitor<RET> {
void begin(final int width, final int height);
RET end();
void visit(final int x, final int y, final double value);
}
public static <RET>
RET
accept(final int width,
final int height,
final double[] data,
final Array2DGenericVisitor<Double, RET> visitor) {
final int size = width * height;
visitor.begin(width, height);
for (int i = 0, x = 0, y = 0; i < size; i++) {
visitor.visit(x, y, data[i]);
x++;
if (x == width) {
x = 0;
y++;
if (y == height) {
y = 0;
}
}
}
return visitor.end();
}
public static <RET> RET accept(final int width,
final int height,
final double[] data,
final Array2DPrimitiveVisitor<RET> visitor) {
final int size = width * height;
visitor.begin(width, height);
for (int i = 0, x = 0, y = 0; i < size; i++) {
visitor.visit(x, y, data[i]);
x++;
if (x == width) {
x = 0;
y++;
if (y == height) {
y = 0;
}
}
}
return visitor.end();
}
private static final Array2DGenericVisitor<Double, double[]> generic;
private static final Array2DPrimitiveVisitor<double[]> primitive;
static {
generic = new Array2DGenericVisitor<Double, double[]>() {
private double[] sum;
#Override
public void begin(final int width, final int height) {
final int length = (int) Math.ceil(Math.hypot(WIDTH, HEIGHT));
sum = new double[length];
}
#Override
public void visit(final int x, final int y, final Double value) {
final int r = (int) Math.round(Math.sqrt(x * x + y * y));
sum[r] += value;
}
#Override
public double[] end() {
return sum;
}
};
primitive = new Array2DPrimitiveVisitor<double[]>() {
private double[] sum;
#Override
public void begin(final int width, final int height) {
final int length = (int) Math.ceil(Math.hypot(WIDTH, HEIGHT));
sum = new double[length];
}
#Override
public void visit(final int x, final int y, final double value) {
final int r = (int) Math.round(Math.sqrt(x * x + y * y));
sum[r] += value;
}
#Override
public double[] end() {
return sum;
}
};
}
private static final int WIDTH = 300;
private static final int HEIGHT = 300;
private static final int NUM_ITERATIONS_PREHEATING = 10000;
private static final int NUM_ITERATIONS_BENCHMARKING = 10000;
public static void main(String[] args) {
final double[] data = new double[WIDTH * HEIGHT];
for (int i = 0; i < data.length; i++) {
data[i] = Math.random();
}
/*
* Pre-heating.
*/
for (int i = 0; i < NUM_ITERATIONS_PREHEATING; i++) {
accept(WIDTH, HEIGHT, data, generic);
}
for (int i = 0; i < NUM_ITERATIONS_PREHEATING; i++) {
accept(WIDTH, HEIGHT, data, primitive);
}
/*
* Benchmarking proper.
*/
double[] sumPrimitive = null;
double[] sumGeneric = null;
double aux = System.nanoTime();
for (int i = 0; i < NUM_ITERATIONS_BENCHMARKING; i++) {
sumGeneric = accept(WIDTH, HEIGHT, data, generic);
}
final double timeGeneric = System.nanoTime() - aux;
aux = System.nanoTime();
for (int i = 0; i < NUM_ITERATIONS_BENCHMARKING; i++) {
sumPrimitive = accept(WIDTH, HEIGHT, data, primitive);
}
final double timePrimitive = System.nanoTime() - aux;
System.out.println("prim = " + timePrimitive);
System.out.println("generic = " + timeGeneric);
System.out.println("generic / primitive = "
+ (timeGeneric / timePrimitive));
}
}
I know that the JIT is pretty clever, so I was not too surprised when both visitors turned out to perform equally well.
What is more surprising, is that the generic visitor seems to perform slightly faster than the primitive, which is unexpected. I know benchmarking can sometimes be difficult, so I must have done something wrong. Can you spot the error?
Thanks a lot for your help!!!
Sébastien
[EDIT] I've updated the code to account for a pre-heating phase (in order to let the JIT compiler do its work). This does not change the results, which are consistently below 1 (0.95 - 0.98).
I know benchmarking can sometimes be difficult, so I must have done something wrong. Can you spot the error?
I think that the problem is that your benchmarking does not take account of JVM warmup. Put the take the body of your main method and put it into another method. Then have your main method call that new method repeatedly in a loop. Finally, examine the results, and discard the first few that are distorted by JIT compilation and other warmup effects.
Small tips:
Do not use Math.random() to perform benchmarks as the results are non-deterministic. You need smth like new Random(xxx).
Always print the result of the operation. Mixing benchmark types in a single execution is bad practice as it can lead to different call site optimizations (not your case, though)
double aux = System.nanoTime(); -- not all longs fit into doubles - properly.
post the specification of the environment and the hardware you perform the benchmarks on
print 'staring test' while enabled printing the compilation -XX:-PrintCompilation and the garbage collection -verbosegc -XX:+PrintGCDetails - the GC can kick in during the 'wrong' test just enough to skew the results.
Edit:
I did check the generated assembler and none of them is the real reason. There is no allocation for Double.valueOf() as the method is inlined altogether and optimized away - it uses only the CPU registers. However w/o the hardware spec/JVM there is no real answer.
I found a JVM (1.6.0.26) where the generic version (Double) has better loop unroll(!), due to deeper analysis (obviously needed to EA the Double.valueOf()) and possibly constant folding of WIDTH/HEIGHT. Change the WIDTH/HEIGHT to some prime numbers and the results should differ.
The bottom line is: do not use microbenchmarks unless you know how the JVM optimizes and check the generated machine code.
Disclaimer: I am no JVM engineer
This is a totally "wild assed guess" but I think it has to do with copying bytes onto the stack. Passing a primitive double involves copying 8 bytes on the stack. Passing a Double only takes copying the pointer.