Хочу переписать свою поделку с унылого C++ на Java или даже сразу на Scala. В программе очень много операций над разными двумерными объектами, поэтому большая часть данных - это координаты на плоскости и их преобразования.
Решил проверить насколько быстро такие операции будут работать в в Java:
import java.util.ArrayList;
import java.util.List;
public class CalcVectors {
    private static class Vector2D {
        public final double x, y;
        private Vector2D(double x, double y) {
            this.x = x;
            this.y = y;
        }
        public Vector2D add(Vector2D v) {
            return new Vector2D(x + v.x, y + v.y);
        }
        public Vector2D mult(Transform2D t) {
            return new Vector2D(x * t.cos - y * t.sin + t.tx,
                                y * t.sin + y * t.cos + t.ty);
        }
        @Override
        public String toString() {
            return "Vector2D(" + "x=" + x + ", y=" + y + ')';
        }
    }
    private static class Transform2D {
        public final double cos, sin, tx, ty;
        private Transform2D(double angle, double tx, double ty) {
            this.cos = Math.cos(angle);
            this.sin = Math.sin(angle);
            this.tx = tx;
            this.ty = ty;
        }
    }
    private static Vector2D calc(List<Vector2D> l, Transform2D t) {
        Vector2D res = new Vector2D(0, 0);
        for (Vector2D v : l) {
            res = res.add(v.mult(t));
        }
        return res;
    }
    private static void run(String str, List<Vector2D> list, Transform2D transf) {
        System.out.println("Start of " + str);
        Vector2D vector = null;
        int repeat = 1000;
        long started = System.nanoTime();
        for (int i = 0; i < repeat; ++i) {
            vector = calc(list, transf);
        }
        double totalTime = (System.nanoTime() - started)*1e-9;
        System.out.println("Res = " + vector);
        System.out.println("Total time = " + totalTime + " (sec)");
        System.out.println("Average time = " + totalTime/repeat + " (sec)");
        System.out.println("End of " + str);
    }
    public static void main(String[] args) {
        final int size = 1000000;
        List<Vector2D> list = new ArrayList<Vector2D>(size);
        for (int i = 0; i < size; ++i) {
            double d = (1.0*i)/size;
            list.add(new Vector2D(d, d));
        }
        Transform2D transf = new Transform2D(1.0, -2.5, 5.0);
        run("Warm Up", list, transf);
        System.out.println();
        run("Actual", list, transf);
    }
}
$ java -server -XX:+PrintCompilation CalcVectors
    208    1             java.lang.Object::<init> (1 bytes)
    210    2             java.util.ArrayList::add (29 bytes)
    220    3             java.util.ArrayList::ensureCapacityInternal (26 bytes)
    222    4             CalcVectors$Vector2D::<init> (7 bytes)
    222    5             CalcVectors$Vector2D::<init> (15 bytes)
    223    1 %           CalcVectors::main @ 12 (90 bytes)
    622    1 %           CalcVectors::main @ -2 (90 bytes)   made not entrant
Start of Warm Up
    630    6             java.util.ArrayList::access$100 (5 bytes)
    636    7             java.util.ArrayList$Itr::hasNext (20 bytes)
    639    8             java.util.ArrayList$Itr::next (66 bytes)
    642   10             java.util.ArrayList::access$200 (5 bytes)
    644    9             java.util.ArrayList$Itr::checkForComodification (23 bytes)
    652   11             CalcVectors$Vector2D::mult (56 bytes)
    656   12             CalcVectors$Vector2D::add (26 bytes)
    659    2 %           CalcVectors::calc @ 18 (54 bytes)
   1219   13             CalcVectors::calc (54 bytes)
Res = Vector2D(x=-2650584.18888554, y=5690885.954451363)
Total time = 32.180277053000005 (sec)
Average time = 0.03218027705300001 (sec)
End of Warm Up
Start of Actual
Res = Vector2D(x=-2650584.18888554, y=5690885.954451363)
Total time = 31.393999127 (sec)
Average time = 0.031393999127 (sec)
End of Actual
#include <cmath>
#include <ctime>
#include <vector>
#include <iostream>
struct vector2d {
    double x, y;
    vector2d(double x = 0, double y = 0) : x(x), y(y) {}
};
vector2d operator+(const vector2d& v0, const vector2d& v1) {
    return vector2d(v0.x + v1.x, v0.y + v1.y);
}
struct transform2d {
    double cos, sin, tx, ty;
    transform2d(double angle, double tx, double ty)
    : cos(std::cos(angle)), sin(std::sin(angle)), tx(tx), ty(ty) {}
};
vector2d operator*(const vector2d& v, const transform2d& t) {
    double x = v.x*t.cos - v.y*t.sin + t.tx;
    double y = v.x*t.sin + v.y*t.cos + t.ty;
    return vector2d(x, y);
}
vector2d calc(const std::vector<vector2d>& vects, const transform2d& transf) {
    vector2d v;
    for (size_t i = 0; i < vects.size(); ++i) {
        v = v + vects[i]*transf;
    }
    return v;
}
int main() {
    std::vector<vector2d> vects(1000000);
    for (size_t i = 0; i < vects.size(); ++i) {
        double x = (1.0*i)/vects.size();
        vects[i] = vector2d(x, x);
    }
    transform2d tr(1.0, -2.5, 5.0);
    const int repeat = 1000;
    vector2d res;
    std::clock_t start = std::clock();
    for (int i = 0; i < repeat; ++i) {
        res = calc(vects, tr);
    }
    double total = ((double)(std::clock() - start))/CLOCKS_PER_SEC;
    std::cout << "Res = vector2d(" << res.x << "," << res.y << ")\n"
              << "Total time = " << total << " (sec)\n" 
              << "Average time = " << total/repeat << " (sec)" << std::endl;
}
$ ./a.out 
Res = vector2d(-2.65058e+06,5.69089e+06)
Total time = 8.52 (sec)
Average time = 0.00852 (sec)
Кроме того, по памяти у Java также все гораздо хуже получилось: более 100Мб из кучи, в то время как C++ вариант 16Мб с копейками, что и ожидалось, т.к. один миллион структур vector2d как раз 16Мб и занимает.
Наверняка что-то сделал не так в Java варианте (ведь Java должна быть быстра почти как C++). Подскажите, как нужно правильно переписать Java вариант, чтобы приблизиться к C++?











