Subject: Re: design approach for numerical data structures

From: Urs Schreiber <[email-masked]>
Date: Wed, 04 Aug 1999 03:39:58 +0200

Thanks for all the answers to my last posting. I understand that at
least the first part of my approach (passing result parameters) is
nothing new and widely used. My misjudgment of the coding practice of
numerical applications was based mainly on two packages that I had a
closer look at, namely  JNL and Jama (which I found by following Java
Grande links). Correct me if I am wrong, but these two packages do not
provide means to limit constructor calls (except for the += and *=
analogies Complex.add() and Complex.multiply() in JNL). 

	Let me note one thing, though, regarding the wrapping up of methods in
classes: I have downloaded and read "An Evaluation of Java for Numerical
Computing" by B. Blount and S.Chatterjee a few minutes ago (which I
found by following one of the links I was given in response to my
posting) and found an intriguing description of high performance Java
design as done in the JLAPACK package. It was interesting to read (in
paragraph 3.2 "Limiting constructor calls") that the second of the
design principles I was talking about (creating temporary objects when
initializing)  has, too, already been applied. There it says about
temporary objects used by methods: "However, we make them private static
class members." But the next sentence makes the restriction: "Note that
this approach works only because none of the methods in the library are
recursive and because we are ignoring issues of thread safety." Here I
would like to remark that by not making the temporaries static but, as I
had suggested, making them non static members of a class wrapping the
method, it becomes both thread safe and allowed to be recursive. Thread
safety is achieved because every thread constructs its own instance of
the wrapper class, thus also constructing an individual set of
temporaries (as demonstrated in the example in my last posting).
Recursion becomes (somewhat similarly) possible if the wrapper for the
method to be recursively called is constructed on demand, depending on
the depth of the recursion. To see this, consider the following
recursive method (not wrapped) which uses one temporary object:

Matrix recursiveCalculation(Matrix a, Matrix result) {
	if (a.needsFurtherTreatment())
		Matrix cache = new Matrix(3,3);
		a.treat(cache); //assign treated "a" to "cache"
		return recursiveCalculation(cache,result);
	}
	else
		return a.copyTo(result);
		
}

Now wrapping up the method and making its temporary object a member of
the wrapper gives:

class RecursiveCalculation {
	Matrix cache = new Matrix(3,3);
	RecursiveCalculation nextRecursion;
	Matrix do(Matrix a, Matrix result) {
		if (a.needsFurtherTreatment()) {
			if (nextRecursion == null)
				nextRecursion = new RecursiveCalculation();
			a.treat(cache);
			return nextRecursion.do(cache,result);
		}
		else
			return a.copyTo(result);
	}
}

Upon first construction of RecursiveCalculation by the calculating
thread a single temporary object is created. Each time the do() method
is called existing temporaries are used and more temporary objects are
constructed as soon as the recursion goes deeper than any of the
previous ones. (If you feel the line "if (nextRecursion == null)" is an
undesirable overhead, simply remove it and enclose the "if" statement
inside a try-catch block, which, when catching NullPointerExceptions,
creates the nextRecursion object and recalls the method.)
The above code does not construct its temporaries upon initialization.
This would be impossible if the limit of the recursion is not known in
advance. Minor additions and modifications would allow to take
precautions and construct a certain number of temporary objects in
advance or remove some when desired and necessary.

Removing temporaries reminds me of a point made by Wolfgang Hoschek in
his reply to my last posting. He wrote:
Concerning cached objects: Increased memory consumption can be a problem
when an object is still referenced but the operations using the cached
tmp's are nomore needed. trimToSize() methods and/or SoftReferences can
be useful in this context.
<<

I am not really sure about what SoftReferences are, but the problem
mentioned can also be easily solved when using method wrappers by simply
calling a dispose() method on the method's wrapper which does the same
to its wrapped members and nulls all its temporaries. (But the same is
impossible or at least much more involved when using static
temporaries.)

Considering all this I wonder if maybe the concept of wrapping methods
to provide temporary objects is useful for high perforormance numerical
Java software (and not an old hat)?

Urs Schreiber

--
 [email-masked] 
 http://www.uni-essen.de/~hnr00s

---

From: Wolfgang HOSCHEK <[email-masked]>
Date: Wed, 04 Aug 1999 10:28:50 +0200

> impossible or at least much more involved when using static

Trying to clarify the discussion:

trimToSize() methods are just the same as safe dispose() methods. They
simply null out all references to their temporaries, so that they may be
garbage collected. They object remains fully functional, though, because
it will later set up tmp's again when needed for its operations.

Soft and weak references are new since JDK1.2 and provide means to
implement lazy caches. They are applicable if some objects are for
performance reasons held in memory, but could be "reconstructed" at any
time, either by recomputing them or reading them from disk. See
http://java.sun.com\/products/jdk/1.2/docs/guide/refobs/index.html

An object can have a soft reference to another cached object. The cached
object may be reclaimed by the garbage collector at any time, but will
typically only be reclaimed when no "normal" objects can be garbage
collected anymore, just before totally running out of memory. 
When an object needs its owned cache object for some operation, it
checks whether it exists (has not been gc'd), constructs it if nomore
available, and proceeds as usual. Thus, soft and weak refs provide means
to relieve the user of needing to figure out when best to call
trimToSize (or dispose) methods. In a way trimToSize() methods are
called automatically just before running out of memory.

Wolfgang.

---

From: Urs Schreiber <[email-masked]>
Date: Wed, 04 Aug 1999 21:02:08 +0200

> Soft and weak references are new since JDK1.2 and provide means to

Thanks for the hint. To my shame I have to admit that I was not aware of
this feature of JDK1.2. Now I understand your remark about increased
memory consumption when caching objects. You are perfectly right when
saying that references to cached objects (and also to the method
wrappers I mentioned) should be made SoftReferences whenever possible
and practical, this way reducing the risk of memory overflow.  

Regards,
Urs Schreiber