Background: I am planning to port a library I have written from C++ to Java. The code deals with lists of size n of d-dimensional points and needs to compute scalar products, etc. I wanted to make my code independent of the storage format of points and introduced to this purpose an interface,
public interface PointSetAccessor
{
float coord(int p, int c);
}
that allows me to get the c-th coordinate (0 ≤ c < d) of the p-th point (0 ≤ p < n).
Problem: Since the code has to be really fast, I was wondering whether this would slow down the performance, in contrast to a straight access pattern like points[p][c]
, where points
is an array of n arrays, each of which holding the d point coordinates.
Surprisingly, the opposite was the case: the code (see below) is 20% faster with the "indirect" access through a PointSetAccessor
. (I measured this using time java -server -XX:+AggressiveOpts -cp bin Speedo
and got around 14s for the former and 11s for the latter version.)
Question: Any idea why this is so? Seems like Hotspot decides to optimise more aggressively or has more freedom to do so in the latter version?
Code (which computes non-sense):
public class Speedo
{
public interface PointSetAccessor
{
float coord(int p, int c);
}
public static final class ArrayPointSetAccessor implements PointSetAccessor
{
private final float[][] array;
public ArrayPointSetAccessor(float[][] array)
{
this.array = array;
}
public float coord(int point, int dim)
{
return array[point][dim];
}
}
public static void main(String[] args)
{
final int n = 50000;
final int d = 10;
// Generate n points in dimension d
final java.util.Random r = new java.util.Random(314);
final float[][] a = new float[n][d];
for (int i = 0; i < n; ++i)
for (int j = 0; j < d; ++j)
a[i][j] = r.nextFloat();
float result = 0.0f;
if (true)
{
// Direct version
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; ++j)
{
float prod = 0.0f;
for (int k = 0; k < d; ++k)
prod += a[i][k] * a[j][k];
result += prod;
}
}
else
{
// Accessor-based version
final PointSetAccessor ac = new ArrayPointSetAccessor(a);
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; ++j)
{
result += product(ac, d, i, j);
}
}
System.out.println("result = " + result);
}
private final static float product(PointSetAccessor ac, int d, int i, int j)
{
float prod = 0.0f;
for (int k = 0; k < d; ++k)
prod += ac.coord(i, k) * ac.coord(j, k);
return prod;
}
}