Re: [Chicken-users] optimizing mutually recursive loops

2009-02-16 Thread Tobia Conforto

Alaric Snell-Pym wrote:
Some would say that the matrix multiply should just be written in C  
and FFIed.


In fact, most would just (use blas) or such.  But I understand it was  
just an example.


Concurrent Clean, a Haskell-like purely functional language that,  
unlike Haskell, implements mutating operations using uniqueness  
typing.


I'm wondering whether we could apply a similar idea to Chicken.  Do  
you think there is any space for improvement, where we could somehow  
reduce GC by declaring stuff as unique?  I know I'm just hand-waving  
at this point, I need to think more about it.


Any complex imperative algorithm involves a certain number of nested  
control structures, and before long, you need to add a single extra  
arc to that.


This is quite true and I've found Scheme's named let an excellent  
syntax for this kind of algorithm. You can just write the loops in the  
natural order of variable declaration, and then jump back and forth as  
you need: "Oh, I'll just restart with the 2nd outer loop here: (loop- 
row x (+ y 1))."


In fact, I've been writing "named loops" in other languages too, where  
the language lets me do it... which means, if it has a syntax for  
lambdas. But they never come out as nicely as in Scheme.


But written as a state machine, such extra control flow arcs are  
easy to add. I've been meaning for some time to experiment with  
writing Scheme macros that implement a state machine language  
designed for ease of use, and experimenting with using them.


Nice! Do you have any prototype we could play with, or ideas we could  
discuss?


-Tobia


___
Chicken-users mailing list
Chicken-users@nongnu.org
http://lists.nongnu.org/mailman/listinfo/chicken-users


Re: [Chicken-users] optimizing mutually recursive loops

2009-02-16 Thread Alaric Snell-Pym


[cunning optimisation stunts]

Some would say that the matrix multiply should just be written in C
and FFIed.

Some would say that Chicken should be improved until you can write
natural Scheme code without a thought for performance, and Chicken
should general optimal C as good as any human could handcode (although
they may get a bit vague and hand-wavey as to how this might be
accomplished).

But finding middle grounds is interesting; Alex has found one by
bending Chicken to his will cunningly. I wonder, though, how this can
be generalised a bit, since I have an instinctive distaste for
specific hacks: a let-machine is, in a way, a little subset of Scheme
that can be implemented particularly efficiently by Chicken.

Now I, in my own work, have sometimes had to implement low-level
operations involving binary data, and I've hankered for a way to do
them more efficiently in Scheme so I don't have to go into C so often.
What I've been thinking of is somewhat similar in execution, but first
a history lesson...

One of my inspirations in programming language technology (other than
Lisp) is Concurrent Clean, a Haskell-like purely functional language
that, unlike Haskell, implements mutating operations (non-copying
array ops, I/O, that sort of thing) using uniqueness typing.
Basically, if an object like an array passed into an array-set! style
function can be shown by the type system to be the only reference to
that object in the system, then array-set! can modify it in-place and
return the 'same' array with a new value, while still being a pure
function. If you pass it in a non-unique reference, then it has to
make a copy of the array to mutate and return.

As well as implementing efficient updates on large arrays, this is
also used for I/O; a top-level Clean program is a function of type
Unique World -> Unique World. And the system provides a number of
operations on type World, each of which require a unique reference to
the World and return a unique reference to a World. Eg, there's a
function that takes a World and a string, and returns a new World in
which that string has been displayed on a display device somewhere;
and a function that takes a World and returns a string and a World in
which some time has passed and somebody has typed that string in and
pressed enter... in effect, the entire universe is encapsulated in a
logical object so the program can 'mutate' it using the same trick.
But because the functions that accept Worlds all require them to be
unique, the type system forces the program to only ever have a single
World; if you try and keep a reference to a particular state of the
World then it stops being unique, so your program gives a compile-time
type error when you try and use that World or return it from 'main'.

Anyway, you can write imperative-style code in Clean, but it looks a
bit ugly. I can't remember the exact syntax, but in spirit, you end up
with:

SayHello :: !World -> !World
SayHello World =
  let World2 = printString (World, "Please enter your name") in
  let World3, Name = inputString (World2) in
  printString (World3, "Hello, " + Name)

This has benefits over monads - for a start, you can compose them in
parallel; your program might have regions that mutate some array, and
regions that mutate the world, and those two bits can be interleaved;
while in Haskell you can only do this by bunging everything into a
single IO monad, which ends up enforcing more sequentiality than is
needed (subject only to actual data-flow requirements between the two
"threads", the Clean compiler has more scope to rearrange or
parallelise stuff).

And it's really neat in that unique values don't need garbage
collection. A function that takes an object and doesn't return it (in
any form), if it's given a unique value, knows it never needs it
again. Functions that require unique arguments can just hardcode
deallocation; functions that might take a unique argument can, if they
destroy their reference to it, do an "if unique then deallocate"
operation reminiscent of a reference count decrement. Only shared
values are prone to garbage collection, and if the compiler can
correctly deduce the uniqueness of lots of data, that's lots of gc
overhead removed.

Anyway. Having noticed that the recursive-lets-with-new-names syntax
sucked, I followed a chain of thought as to how I'd improve it.

1) I don't know why the examples I saw used a new name for the World
each time - surely the nesting lexical scopes of the lets would let
you write:

let World = printString (World, "Please enter your name") in
 ...

and keep things neater?

2) Isn't a mutually-recursive set of functions passing unique values
between themselves just a state machine in register transfer language,
and thus really trivial to implement efficiently?

Now, I have a theory that state machines are a really good syntax for
imperative stuff. Structured programming has put goto out of fashion,
but used properly it was a good thing; the problem was that pe