* Optimization to prevent garbage generation by replacing Arrays.sort() with SortUtil.msort()

* Replaced "evil" code from SortUtil.msort() with "good" code


git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7662 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent f9079171f1
commit 88a5109865
  1. 15
      engine/src/core/com/jme3/renderer/queue/GeometryList.java
  2. 211
      engine/src/core/com/jme3/util/SortUtil.java

@ -34,6 +34,7 @@ package com.jme3.renderer.queue;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.util.SortUtil;
import java.util.Arrays; import java.util.Arrays;
/** /**
@ -48,12 +49,14 @@ public class GeometryList {
private static final int DEFAULT_SIZE = 32; private static final int DEFAULT_SIZE = 32;
private Geometry[] geometries; private Geometry[] geometries;
private Geometry[] geometries2;
private int size; private int size;
private GeometryComparator comparator; private GeometryComparator comparator;
public GeometryList(GeometryComparator comparator) { public GeometryList(GeometryComparator comparator) {
size = 0; size = 0;
geometries = new Geometry[DEFAULT_SIZE]; geometries = new Geometry[DEFAULT_SIZE];
geometries2 = new Geometry[DEFAULT_SIZE];
this.comparator = comparator; this.comparator = comparator;
} }
@ -80,6 +83,8 @@ public class GeometryList {
Geometry[] temp = new Geometry[size * 2]; Geometry[] temp = new Geometry[size * 2];
System.arraycopy(geometries, 0, temp, 0, size); System.arraycopy(geometries, 0, temp, 0, size);
geometries = temp; // original list replaced by double-size list geometries = temp; // original list replaced by double-size list
geometries2 = new Geometry[size * 2];
} }
geometries[size++] = g; geometries[size++] = g;
} }
@ -101,8 +106,14 @@ public class GeometryList {
public void sort() { public void sort() {
if (size > 1) { if (size > 1) {
// sort the spatial list using the comparator // sort the spatial list using the comparator
Arrays.sort(geometries, 0, size, comparator);
// Arrays.sort(geometries, comparator); // SortUtil.qsort(geometries, 0, size, comparator);
// Arrays.sort(geometries, 0, size, comparator);
System.arraycopy(geometries, 0, geometries2, 0, size);
SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
} }
} }
} }

@ -29,7 +29,6 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.util; package com.jme3.util;
import java.util.Arrays; import java.util.Arrays;
@ -39,48 +38,123 @@ import java.util.Comparator;
* Quick and merge sort implementations that create no garbage, unlike {@link * Quick and merge sort implementations that create no garbage, unlike {@link
* Arrays#sort}. The merge sort is stable, the quick sort is not. * Arrays#sort}. The merge sort is stable, the quick sort is not.
*/ */
public class SortUtil public class SortUtil {
{
/**
* The size at or below which we will use insertion sort because it's
* probably faster.
*/
private static final int INSERTION_SORT_THRESHOLD = 7;
/**
procedure optimizedGnomeSort(a[])
pos := 1
last := 0
while pos < length(a)
if (a[pos] >= a[pos-1])
if (last != 0)
pos := last
last := 0
end if
pos := pos + 1
else
swap a[pos] and a[pos-1]
if (pos > 1)
if (last == 0)
last := pos
end if
pos := pos - 1
else
pos := pos + 1
end if
end if
end while
end procedure
*/
public static void gsort(Object[] a, Comparator comp) { public static void gsort(Object[] a, Comparator comp) {
int p = 0; int pos = 1;
int l = a.length; int last = 0;
while (p < l){ int length = a.length;
int pm1 = p-1;
if (p == 0 || comp.compare(a[p], a[pm1]) >= 0){ while (pos < length){
p++; if ( comp.compare(a[pos], a[pos-1]) >= 0 ){
if (last != 0){
pos = last;
last = 0;
}
pos ++;
}else{ }else{
Object t = a[p]; Object tmp = a[pos];
a[p] = a[pm1]; a[pos] = a[pos-1];
a[pm1] = t; a[pos-1] = tmp;
p--;
if (pos > 1){
if (last == 0){
last = pos;
}
pos --;
}else{
pos ++;
}
} }
} }
// int p = 0;
// int l = a.length;
// while (p < l) {
// int pm1 = p - 1;
// if (p == 0 || comp.compare(a[p], a[pm1]) >= 0) {
// p++;
// } else {
// Object t = a[p];
// a[p] = a[pm1];
// a[pm1] = t;
// p--;
// }
// }
} }
private static void test(Float[] original, Float[] sorted, Comparator<Float> ic) { private static void test(Float[] original, Float[] sorted, Comparator<Float> ic) {
long time, dt;
time = System.nanoTime();
for (int i = 0; i < 1000000; i++) { for (int i = 0; i < 1000000; i++) {
System.arraycopy(original, 0, sorted, 0, original.length); System.arraycopy(original, 0, sorted, 0, original.length);
gsort(sorted, ic); gsort(sorted, ic);
} }
dt = System.nanoTime() - time;
System.out.println("GSort " + (dt/1000000.0) + " ms");
time = System.nanoTime();
for (int i = 0; i < 1000000; i++) { for (int i = 0; i < 1000000; i++) {
System.arraycopy(original, 0, sorted, 0, original.length); System.arraycopy(original, 0, sorted, 0, original.length);
qsort(sorted, ic); qsort(sorted, ic);
} }
dt = System.nanoTime() - time;
System.out.println("QSort " + (dt/1000000.0) + " ms");
time = System.nanoTime();
for (int i = 0; i < 1000000; i++) { for (int i = 0; i < 1000000; i++) {
System.arraycopy(original, 0, sorted, 0, original.length); System.arraycopy(original, 0, sorted, 0, original.length);
msort(original, sorted, ic); msort(original, sorted, ic);
} }
dt = System.nanoTime() - time;
System.out.println("MSort " + (dt/1000000.0) + " ms");
time = System.nanoTime();
for (int i = 0; i < 1000000; i++) { for (int i = 0; i < 1000000; i++) {
System.arraycopy(original, 0, sorted, 0, original.length); System.arraycopy(original, 0, sorted, 0, original.length);
Arrays.sort(sorted, ic); Arrays.sort(sorted, ic);
} }
dt = System.nanoTime() - time;
System.out.println("ASort " + (dt/1000000.0) + " ms");
} }
public static void main(String[] args) { public static void main(String[] args) {
Comparator<Float> ic = new Comparator<Float>() { Comparator<Float> ic = new Comparator<Float>() {
public int compare(Float o1, Float o2) { public int compare(Float o1, Float o2) {
return (int) (o1 - o2); return (int) (o1 - o2);
} }
@ -98,8 +172,7 @@ public class SortUtil
/** /**
* Quick sorts the supplied array using the specified comparator. * Quick sorts the supplied array using the specified comparator.
*/ */
public static void qsort (Object[] a, Comparator comp) public static void qsort(Object[] a, Comparator comp) {
{
qsort(a, 0, a.length - 1, comp); qsort(a, 0, a.length - 1, comp);
} }
@ -110,8 +183,7 @@ public class SortUtil
* @param hi0 the index of the highest element to include in the sort. * @param hi0 the index of the highest element to include in the sort.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static void qsort (Object[] a, int lo0, int hi0, Comparator comp) public static void qsort(Object[] a, int lo0, int hi0, Comparator comp) {
{
// bail out if we're already done // bail out if we're already done
if (hi0 <= lo0) { if (hi0 <= lo0) {
return; return;
@ -122,7 +194,9 @@ public class SortUtil
if (hi0 - lo0 == 1) { if (hi0 - lo0 == 1) {
// if they're not already sorted, swap them // if they're not already sorted, swap them
if (comp.compare(a[hi0], a[lo0]) < 0) { if (comp.compare(a[hi0], a[lo0]) < 0) {
t = a[lo0]; a[lo0] = a[hi0]; a[hi0] = t; t = a[lo0];
a[lo0] = a[hi0];
a[hi0] = t;
} }
return; return;
} }
@ -145,7 +219,9 @@ public class SortUtil
// swap the two elements or bail out of the loop // swap the two elements or bail out of the loop
if (hi > lo) { if (hi > lo) {
t = a[lo]; a[lo] = a[hi]; a[hi] = t; t = a[lo];
a[lo] = a[hi];
a[hi] = t;
} else { } else {
break; break;
} }
@ -164,8 +240,7 @@ public class SortUtil
} }
} }
public static void qsort (int[] a, int lo0, int hi0, Comparator comp) public static void qsort(int[] a, int lo0, int hi0, Comparator comp) {
{
// bail out if we're already done // bail out if we're already done
if (hi0 <= lo0) { if (hi0 <= lo0) {
return; return;
@ -176,7 +251,9 @@ public class SortUtil
if (hi0 - lo0 == 1) { if (hi0 - lo0 == 1) {
// if they're not already sorted, swap them // if they're not already sorted, swap them
if (comp.compare(a[hi0], a[lo0]) < 0) { if (comp.compare(a[hi0], a[lo0]) < 0) {
t = a[lo0]; a[lo0] = a[hi0]; a[hi0] = t; t = a[lo0];
a[lo0] = a[hi0];
a[hi0] = t;
} }
return; return;
} }
@ -199,7 +276,9 @@ public class SortUtil
// swap the two elements or bail out of the loop // swap the two elements or bail out of the loop
if (hi > lo) { if (hi > lo) {
t = a[lo]; a[lo] = a[hi]; a[hi] = t; t = a[lo];
a[lo] = a[hi];
a[hi] = t;
} else { } else {
break; break;
} }
@ -219,73 +298,55 @@ public class SortUtil
} }
/** /**
* Merge sorts the supplied array using the specified comparator. * Merge sort
*
* @param src contains the elements to be sorted.
* @param dest must contain the same values as the src array.
*/ */
public static void msort (Object[] src, Object[] dest, Comparator comp) public static void msort(Object[] src, Object[] dest, Comparator comp){
{ msort(src, dest, 0, src.length - 1, comp);
msort(src, dest, 0, src.length, 0, comp);
} }
/** /**
* Merge sorts the supplied array using the specified comparator. * Merge sort
* *
* @param src contains the elements to be sorted. * @param src Source array
* @param dest must contain the same values as the src array. * @param dest Destination array
* @param low Index of beginning element
* @param high Index of end element
* @param comp Comparator
*/ */
public static void msort(Object[] src, Object[] dest, int low, int high, public static void msort(Object[] src, Object[] dest, int low, int high,
Comparator comp) Comparator comp) {
{ if(low < high) {
msort(src, dest, low, high, 0, comp); int center = (low + high) / 2;
msort(src, dest, low, center, comp);
msort(src, dest, center + 1, high, comp);
merge(src, dest, low, center + 1, high, comp);
} }
/** Implements the actual merge sort. */
@SuppressWarnings("unchecked")
protected static void msort (Object[] src, Object[] dest, int low,
int high, int offset, Comparator comp)
{
// use an insertion sort on small arrays
int length = high - low;
if (length < INSERTION_SORT_THRESHOLD) {
for (int ii = low; ii < high; ii++) {
for (int jj = ii;
jj > low && comp.compare(dest[jj-1], dest[jj]) > 0; jj--) {
Object temp = dest[jj];
dest[jj] = dest[jj-1];
dest[jj-1] = temp;
} }
private static void merge(Object[] src, Object[] dest,
int low, int middle, int high, Comparator comp) {
int leftEnd = middle - 1;
int pos = low;
int numElements = high - low + 1;
while (low <= leftEnd && middle <= high) {
if (comp.compare(src[low], src[middle]) <= 0) {
dest[pos++] = src[low++];
} else {
dest[pos++] = src[middle++];
} }
return;
} }
// recursively sort each half of dest into src while (low <= leftEnd) {
int destLow = low, destHigh = high; dest[pos++] = src[low++];
low += offset;
high += offset;
int mid = (low + high) >> 1;
msort(dest, src, low, mid, -offset, comp);
msort(dest, src, mid, high, -offset, comp);
// if the list is already sorted, just copy from src to dest; this
// optimization results in faster sorts for nearly ordered lists
if (comp.compare(src[mid-1], src[mid]) <= 0) {
System.arraycopy(src, low, dest, destLow, length);
return;
} }
// merge the sorted halves (now in src) into dest while (middle <= high) {
for (int ii = destLow, pp = low, qq = mid; ii < destHigh; ii++) { dest[pos++] = src[middle++];
if (qq >= high || pp < mid && comp.compare(src[pp], src[qq]) <= 0) {
dest[ii] = src[pp++];
} else {
dest[ii] = src[qq++];
} }
for (int i = 0; i < numElements; i++, high--) {
src[high] = dest[high];
} }
} }
/** The size at or below which we will use insertion sort because it's
* probably faster. */
private static final int INSERTION_SORT_THRESHOLD = 7;
} }

Loading…
Cancel
Save