#java #performance #opengl #garbage-collection #cloneable
#java #Производительность #opengl #сбор мусора #клонируемый
Вопрос:
Я создаю 3D-игру OpenGL LWJGL, и я заменил класс для 3D-векторов с плавающей точкой на его общую версию и реализовал метод «clone ()» из «Cloneable». После этого производительность значительно падает (использование GC снизилось с менее чем 1% до 10%). Вот пример кода для vector edition до и после изменения:
Перед:
public class Vec3f {
public float x, y, z;
...
public Vec3f add(Vec3f v) {
return new Vec3f(x v.x, y v.y, z v.z);
}
public Vec3f addThis(Vec3f v) {
x = v.x;
y = v.y;
z = v.z;
}
}
После:
public abstract class Vec<V extends Vec<V>> implements Cloneable {
private Class<V> klass;
protected float[] coords;
protected Vec(int dim, Class<V> klass) {
this(dim, new float[dim], klass);
}
public V clone() {
try {
V c = klass.newInstance();
c.coords = this.coords.clone();
return c;
}
catch(InstantiationException e1) {}
catch(IllegalAccessException e2) {}
return null;
}
...
public V add(V that) {
V sum = this.clone();
sum.addThis(that);
return sum;
}
public void addThis(V that) {
for (int i = 0; i < coords.length; i ) {
coords[i] = that.coords[i];
}
}
}
public class Vec3 extends Vec<Vec3> {
public Vec3() {
super(3, Vec3.class);
}
}
Но это не имеет никакого смысла вообще, так как код на самом деле делает то же самое.
Комментарии:
1. Создание и циклический просмотр массива — это не «то же самое», что чтение нескольких переменных.
2. Такая ли большая разница в создании 3 переменных (до) и создании массива с 3 элементами (после)?
3. Это яблоки и апельсины. Раньше у вас был объект с 3 переменными. Теперь вы создаете новый массив при каждом вызове
add
.4. Есть предложения о том, как сохранить функциональность generics и избавиться от создания массива?
5. Хорошо, я только что заметил, что вы также создаете новый экземпляр
Vec
при каждом вызове. Это, вероятно, даже дороже, чемclone
.Vec
Должно ли быть неизменяемым? В любом случае, я не понимаю, какое отношение generics имеет к созданию массива. Та же логика, которую вы используете для копирования массива, может использоваться для копирования отдельных переменных.
Ответ №1:
Производительность GC зависит от количества живых объектов в куче. Вторая версия вашего кода создает больше объектов, что создаст больше работы для GC.
Вторая версия кода, вероятно, также будет выполняться медленнее, она использует отражение, которое имеет некоторые накладные расходы, и, вероятно, также будет страдать от большего количества пропусков кэша процессора, поскольку она требует большего количества погони за указателем.
То есть использование x, y, z в качестве полей будет быстрее, чем использование массива, на который ссылается класс Vec3F.
Комментарии:
1. Но разве отражение не выполняется во время компиляции и, следовательно, не влияет на производительность?
2. Не в Java, нет. Возможно, вы думаете об общих. Накладные расходы на отражение становятся все ниже и ниже, и есть некоторые оптимизации точек доступа, которые могут полностью их устранить. Но это пока не распространено.
3. Отражение @user2340939 по определению является операцией во время выполнения. Из Википедии: «В информатике отражение — это способность компьютерной программы проверять (см. Тип introspection) и изменять структуру и поведение (в частности, значения, метаданные, свойства и функции) программы во время выполнения». Я согласен с Крисом, хотя вы, вероятно, путаете отражение с чем-то другим.
4. Да, я ошибся в reflection == generics, но теперь это решено. Итак, что было бы лучшим способом решить эту проблему, например, значительно повысить производительность?
5. Вы расширяете рамки исходного вопроса, и, честно говоря, я не верю, что здесь достаточно информации, чтобы отдать должное. Однако вот некоторые рекомендации, которые помогут вам начать. Для традиционного решения Java ваш первый подход был хорош. Он следовал хорошим принципам OO, был простым и неизменяемым. Однако, если скорость теперь является вашей целью, вам нужно будет начать выборочно удалять часть этого хорошего дизайна и приблизиться к аппаратному обеспечению. Неизменяемые классы хороши, но могут привести к созданию фантомных объектов. При работе с большим количеством чисел графические процессоры всегда работают с необработанными массивами.