javaarraysfloating-pointwrapper

Using an array of length 2 or using an object to wrap two floats for high efficiency?


I am writing an app that requires high efficiency. It's an app that deals constantly with floats. I've been told to avoid constants (static final fields). So basically I have a method like this one:

public void dealWithFloats(float x, float y)
{
...
}

Since it's always a couple of floats I was wondering if I should use an array or an object like Point.

If I use an array it would be basically like this:

public float[] getFloats(){
float[] array = new float[2];
float[0] = getX();
float[1] = getY();
return array;
}

Solution

  • I've been told to avoid constants (static final fields).

    This is such a bizarre fly-by WTF statement. Why not? In context of your post, perhaps you've been told this for performance reasons: That the person who told you this wants you to write:

    foo(5.5);
    

    instead of:

    private static final float FOO = 5.5;
    
    ...
    
    foo(SOME_CONSTANT);
    

    Because the second one needs to do a field lookup which is slow. This is completely false - the second snippet is exactly as fast as the first. Run javap to prove it - constants are inlined. This kind of knee-jerk ideas about how performance goes is exactly how not to write optimized code. You write your code 'clean' (clean meaning: Properly abstracted, easy to maintain, easy to understand, easy to test), then you write a test case that simulates expected load, then run profilers to find the hot path, and with the profiler in hand, you tweak code (which, if your code is properly written, will be easy) and rerun the profiler to see if your flailings have an effect.

    Failure to operate in the above manner means you are going to fail, and badly. You can't optimize code with ideas like 'avoid constants'.

    Since it's always a couple of floats I was wondering if I should use an array or an object like Point.

    A new float[2] and a new Point() where Point is defined as record Point(float x, float y) have similar performance characteristics.

    If you want efficiency it is plausible/likely that avoiding that and going with 2 actual floats is more efficient. This:

    for (int i = 0; i < 1_000_000; i++) {
      float x = 1.2;
      float y = 3.4;
      someMethod(x, y);
    }
    

    is considerably simpler for the system to run than:

    float[] floats = new float[2];
    for (int i = 0; i < 1_000_000; i++) {
      floats[0] = 1.2;
      floats[1] = 3.4;
      someMethod(floats);
    }
    

    Because the second one has a layer of indirection (an object on heap) whereas the first one is all-stack. So, the first one is 'faster'. Except, the JVM is very good at optimizing and may well optimize the second to be just as fast. This gets us back to: use profilers and tools like JMH; performance tweaking MUST have objective numbers or it is pointless.

    I'd be extremely surprises if the second one is somehow faster. That does not seem possible (but, again, profilers, you need proof for all of this stuff).