Hello!
Frequent garbage collection in JME 3.1 worries me a little. I have been profiling my application and here is a list of allocation hot spots: objects, which are GCed within some minutes:
Vsync is off, about 600 fps for renderer and 120 fps for BulletPhysics. Scene has about 2000 objects spread among ~40 batch nodes.
Leader of objects allocation is ArrayList.iterator
(38%) and there are 2 hot spots for It: Material
and Technique
. There is an article about performance and avoiding garbage collection. Notably test 9 “Iteration over ArrayList” is interesting.
There is a small contribution, where iteration over a list is changed to manual iteration for 2 hot spots.
Old code:
com.jme3.material.Material
private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> overrides, int unit) {
for (MatParamOverride override : overrides) {
VarType type = override.getVarType();
MatParam paramDef = def.getMaterialParam(override.getName());
if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) {
continue;
}
Uniform uniform = shader.getUniform(override.getPrefixedName());
if (override.getValue() != null) {
if (type.isTextureType()) {
renderer.setTexture(unit, (Texture) override.getValue());
uniform.setValue(VarType.Int, unit);
unit++;
} else {
uniform.setValue(type, override.getValue());
}
} else {
uniform.clearValue();
}
}
return unit;
}
com.jme3.material.Technique
private void applyOverrides(DefineList defineList, List<MatParamOverride> overrides) {
for (MatParamOverride override : overrides) {
if (!override.isEnabled()) {
continue;
}
Integer defineId = def.getShaderParamDefineId(override.name);
if (defineId != null) {
if (def.getDefineIdType(defineId) == override.type) {
defineList.set(defineId, override.type, override.value);
}
}
}
}
New code:
com.jme3.material.Material
private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> overrides, int unit) {
MatParamOverride override;
// manual iteration is used to avoid iterator allocation and to increase iteration performance
for (int i = 0, listSize = overrides.size(); i < listSize; i++) {
override = overrides.get(i);
VarType type = override.getVarType();
MatParam paramDef = def.getMaterialParam(override.getName());
if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) {
continue;
}
Uniform uniform = shader.getUniform(override.getPrefixedName());
if (override.getValue() != null) {
if (type.isTextureType()) {
renderer.setTexture(unit, (Texture) override.getValue());
uniform.setValue(VarType.Int, unit);
unit++;
} else {
uniform.setValue(type, override.getValue());
}
} else {
uniform.clearValue();
}
}
return unit;
}
com.jme3.material.Technique
private void applyOverrides(DefineList defineList, List<MatParamOverride> overrides) {
MatParamOverride override;
// manual iteration is used to avoid iterator allocation and to increase iteration performance
for (int i = 0, listSize = overrides.size(); i < listSize; i++) {
override = overrides.get(i);
if (!override.isEnabled()) {
continue;
}
Integer defineId = def.getShaderParamDefineId(override.name);
if (defineId != null) {
if (def.getDefineIdType(defineId) == override.type) {
defineList.set(defineId, override.type, override.value);
}
}
}
}
Diff:
jme3-core/src/main/java/com/jme3/material/Material.java
@@ -750,7 +750,12 @@ public void selectTechnique(String name, RenderManager renderManager) {
}
private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> overrides, int unit) {
- for (MatParamOverride override : overrides) {
+ MatParamOverride override;
+
+ // manual iteration is used to avoid iterator allocation and to increase iteration performance
+ for (int i = 0, listSize = overrides.size(); i < listSize; i++) {
+ override = overrides.get(i);
+
VarType type = override.getVarType();
MatParam paramDef = def.getMaterialParam(override.getName());
jme3-core/src/main/java/com/jme3/material/Technique.java
@@ -111,7 +111,12 @@ final void notifyTechniqueSwitched() {
}
private void applyOverrides(DefineList defineList, List<MatParamOverride> overrides) {
- for (MatParamOverride override : overrides) {
+ MatParamOverride override;
+
+ // manual iteration is used to avoid iterator allocation and to increase iteration performance
+ for (int i = 0, listSize = overrides.size(); i < listSize; i++) {
+ override = overrides.get(i);
+
if (!override.isEnabled()) {
continue;
}
Allocation hot spots after this update:
Now ArrayList.iterator
allocation is insignificant in comparison with the rest.
If to turn on vsync, fps will be changed from ~600 to 60. Situation with GC objects will be better (iteration update is applied), because update is 10 times less frequent, but now 1st place for GC is LWJGL:
I guess it is hard to do anything with it. Maybe things are better in LWJGL 3.