Bug 93467 - PointList performance hit due to unnecessary re-alloc in ensureCapacity and toIntArray
Summary: PointList performance hit due to unnecessary re-alloc in ensureCapacity and t...
Status: RESOLVED FIXED
Alias: None
Product: GEF
Classification: Tools
Component: GEF-Legacy Draw2d (show other bugs)
Version: 3.1   Edit
Hardware: All Windows XP
: P3 major (vote)
Target Milestone: 3.1.0 M7   Edit
Assignee: gef-inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords: performance
Depends on:
Blocks:
 
Reported: 2005-05-02 18:51 EDT by Winnie Lai CLA
Modified: 2005-05-04 09:43 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Winnie Lai CLA 2005-05-02 18:51:00 EDT
PointList performance hit due to unnecessary re-allocation and arrayCopy in 
ensureCapacity and toIntArray.

Problem # 1 - ensureCapacity
ensureCapacity is invoked when addPoint() is used to add a point. It however 
do new int and arrayCopy if (newSize < size). This loses the advantage of pre-
allocation using the ctor PointList(int size) to allocate the points[] to a 
given capacity, because size is always < newSize. Adding points subsequently 
will re-alloc and arrayCopy for each additional point.
It should "new int" and arrayCopy if (newSize * 2 > points.length)

Client Test code:
PointList pl = new PointList(10000);
for (int i = 0; i < 10000; i++) {
 pl.add(...); // will see new int and arrayCopy for each iteration
}

Suggested fix:
private void ensureCapacity(int newSize) {
	//if (size < newSize) {	// FIX: performance hit - wlai
	if (newSize * 2 > points.length) {
		int old[] = points;
		newSize = Math.max(newSize, size * 3 / 2);
		points = new int[newSize * 2];
		System.arraycopy(old, 0, points, 0, size * 2);
	}
}

Problem # 2 - toIntArray
toIntArray() shrinks the points[] to its current size. Adding points to the 
PointList object loses the advantage of capacity pre-allocated in ctor. It 
should alloc a temp array, copy the content over and return the temp array if 
(points.length != size * 2).

Clinet test code:
PointList pl = new PointList(10000);
for (int j = 0; j < 20; j++) {
 for (int i = 0; i < 10000; i++) {
   pl.add(...)
   // if something happens, break this inner loop
 }
 graphics.drawPolyline(pl); // call toIntArray(), PointList.size may be < 10000
 pl.removeAllPoints();
}


Suggested fix:
public int[] toIntArray() {
	if (points.length != size * 2) {
		int[] old = points;
		// [ FIX: performance hit - wlai
		//points = new int[size * 2];
		//System.arraycopy(old, 0, points, 0, size * 2);
		int[] temp = new int[size * 2];
		System.arraycopy(old, 0, temp, 0, size * 2);
		return temp;
		// ]
	}
	return points;
}
Comment 1 Winnie Lai CLA 2005-05-02 19:05:50 EDT
This performance issue is tested against 3.1m6.
What is the work-around if it cannot be solved in 3.1m7?
Comment 2 Randy Hudson CLA 2005-05-02 20:31:14 EDT
The purpose of PointList is to make painting an array of ints convenient in 
SWT.  Since SWT requires the exact length int array, and since painting occurs 
orders of magnitude more often than modification of a given point list in 
general, we cache the exact-length int array.  So, problem #2 will not be fixed.

One workaround is to not use PointList.
Comment 3 Randy Hudson CLA 2005-05-02 20:33:15 EDT
How does this look.  It's the same as what you suggested I think, except I'm 
multiplying by 2 earlier to avoid the additional multiply+divide,

private void ensureCapacity(int newSize) {
	newSize *= 2;
	if (points.length < newSize) {
		int old[] = points;
		points = new int[Math.max(newSize, size * 3)];
		System.arraycopy(old, 0, points, 0, size * 2);
	}
}
Comment 4 Winnie Lai CLA 2005-05-03 16:47:28 EDT
(In reply to comment #3)
> How does this look.  It's the same as what you suggested I think, except I'm 
> multiplying by 2 earlier to avoid the additional multiply+divide,
> private void ensureCapacity(int newSize) {
> 	newSize *= 2;
> 	if (points.length < newSize) {
> 		int old[] = points;
> 		points = new int[Math.max(newSize, size * 3)];
> 		System.arraycopy(old, 0, points, 0, size * 2);
> 	}
> }

That's perfect and will be great if it will be delivered in 3.1M7
Comment 5 Winnie Lai CLA 2005-05-03 17:48:19 EDT
(In reply to comment #2)
> The purpose of PointList is to make painting an array of ints convenient in 
> SWT.  Since SWT requires the exact length int array, and since painting 
occurs 
> orders of magnitude more often than modification of a given point list in 
> general, we cache the exact-length int array.  So, problem #2 will not be 
fixed.
> One workaround is to not use PointList.

Will you consider to change ensureCapacity to public method and add new API to 
getCapacity() that returns points.length?

I was thinking to use setSize as a work-around to tackle my concern on 
toIntArray() as shown below
PointList pl = new PointList(10000);
for (int j = 0; j < 20; j++) {
 for (int i = 0; i < 10000; i++) {
   pl.add(...)
   // if something happens, break this inner loop
 }
 graphics.drawPolyline(pl); // call toIntArray(), PointList.size may be < 10000
 pl.removeAllPoints();
 pl.setSize(10000);
}
But then 
(i) If newSize * 2 == points.length, setSize still do "new int" and arrayCopy
(ii) I don't really intend to copy point's content (points.length) over to the 
new array because my desire is to clear content while ensuring capacity for 
next iteration.
That means I should not use setSize() for this purpose.
Comment 6 Randy Hudson CLA 2005-05-03 18:26:41 EDT
The (now corrected) behavior of addPoint() is that it will resize and copy the 
array at most O(Log(n)) times. So, adding 10,000 items will require something 
like 12 copies.
Comment 7 Winnie Lai CLA 2005-05-03 19:15:33 EDT
(In reply to comment #6)
> The (now corrected) behavior of addPoint() is that it will resize and copy 
the 
> array at most O(Log(n)) times. So, adding 10,000 items will require 
something 
> like 12 copies.

Are you saying you won't fix ensureCapacity as you suggested in Comment #3? I 
would suggest to fix ensureCapacity in addition the improvement of addPoint(), 
because it will be 0 resize and copy compared to 12 times. I don't see anyone 
would give up 0 copy and go for 12 times if the former case can be achieved.
Comment 8 Randy Hudson CLA 2005-05-04 09:43:15 EDT
Released to HEAD > 0504