[clean-list] Recursive array algorithms

Mike Rainey marainey@cs.indiana.edu
Sun, 18 Jul 2004 01:46:25 -0500


Hi, I'm David Wise's student, and am attempting to implement some recursive 
array libraries in Clean.  In particular, I'm adding support for quadtree 
matrices.  The timing results will (hopefully) generate comparison graphs 
against Haskell and C, which will later end up in a journal submission.  
Clean, with its uniqueness type system, seems an excellent environment for 
quadtree matrices.  However, there are some fundamental difficulties that 
preclude good performance.  To explain my problem I'll start with the 
canonical example, matrix multiplication.

matrMult::*{#Real} {#Real} {#Real} !Int -> *{#Real} 
matrMult c a b ord = mm c (MkM 0) (MkM 0) (MkM 0) 0
	where 
	dpth = lg2 ord   // matrix depth (in number of recursive steps to reach the 
base case) for an n x n size is lg base 2 of n
	mm::*{#Real} !MortonIx !MortonIx !MortonIx !Int -> *{#Real}
	mm c cin ain bin lvl | lvl >= dpth =  // base case (1x1 block)
							let (cv,cp) = c![coerceInt(cin)] in  
								{cp & [coerceInt(cin)]= cv + a.[coerceInt(ain)] * b.[coerceInt(bin)]}
   			              | otherwise #     c = mm c (nw cin) (nw ain) (nw bin) lvlp
	   				        	       c = mm c (nw cin) (ne ain) (sw bin) lvlp
							       c = mm c (ne cin) (nw ain) (ne bin) lvlp
							       c = mm c (ne cin) (ne ain) (se bin) lvlp
						               c = mm c (sw cin) (sw ain) (nw bin) lvlp
							       c = mm c (sw cin) (se ain) (sw bin) lvlp
							       c = mm c (se cin) (sw ain) (ne bin) lvlp
					                 =         mm c (se cin) (se ain) (se bin) lvlp
	   where lvlp = lvl + 1

matrMult takes matrices c, a, and b and stores the result of a*b into c.  The 
mm helper function multiplies the 1x1 base case in the first guard, and 
recursively computes the n x n case in the second guard.  To recurse down 
this matrix, Morton order indices are used.  They are selected by quadtant as 
nw (northwest), sw (south west), etc.  This test code has no bounds checking, 
as it is only intended for powers of 2.

The point is, however, that the mm function, with it's 8 recursive calls, ends 
up clobbering the stack.  The program segfaults for interesting sizes (those 
> 64x64).  Upping the stack size solves this, until size is greater than 
2048x2048.  But this is a poor solution, as a constant size stack will do.  
Should Clean recognize that mm is tail-recursive?  Is there some magic 
incantation that might force mm to be tail-recursive?  I'm very interested in 
how uniqueness types can increase performance, and would appreciate any 
insights.

Thanks!
Mike Rainey
Indiana University CS, soon to be University of Chicago CS