[e-lang] Making 'with' efficient

Kevin Reid kpreid at mac.com
Tue Jun 12 14:12:14 EDT 2007


With a naive implementation of e.g. ConstList#with/1, if a list is  
constructed by accumulation, the list will be repeatedly copied, with  
the complete operation taking O(n^2) time in the number of elements.

I have now implemented a mechanism to avoid this in E-on-CL  
(revisions 823 and 824):


Each call to with/1 returns a vector-with-cell, which is a kind of  
lazy-ref (a newly introduced class). A lazy-ref does not compute its  
referent until it is examined (call, shorten, Ref.state, ...).

vector-with-cell recognizes the with/1 message specially and  
constructs another vector-with-cell; thus the accumulation is  
initially stored as a reverse linked list.

When turning a vector-with-cell into an actual vector (ConstList),  
the recipient is examined for being another vector-with-cell  
(recursively), and thus the resulting ConstList can be allocated and  
populated without having constructed the intermediate shorter  
ConstLists.

A similar optimization is performed for ConstMaps, and ConstSet  
inherits this by way of being a wrapper for ConstMap. (Note that this  
changes the GC behavior; if you retain [a => b].with(a, c) without  
examining it, b will be retained.)


With this optimization, 'with' accumulation takes O(n) time in my  
benchmarks, and is actually faster than using a FlexList (though this  
is probably due to the current FlexList implementation).


I chose to use the lazy-ref technique rather than something more  
'just objects' primarily because it would have been harder to ensure  
that the implementation differences were transparent (for consistent  
selflessness). In this implementation, with/1 is the only method  
implemented specially, and the transparency is handled the existing  
code for forwarding-refs.


The relevant ref implementation classes are:

   forwarding-ref
     resolved-ref
     lazy-ref
       with-cell
         vector-with-cell
         const-map-with-cell

A lazy-ref becomes a resolved-ref when its referent is computed.

Resolved-refs are also what resolved promises become, and were  
previously known as forwarding-refs.

-- 
Kevin Reid                            <http://homepage.mac.com/kpreid/>




More information about the e-lang mailing list