On Wed, 2007-02-14 at 00:18 -0500, Chris King wrote:
> Hi,
> 
> I got curious about fthreads and decided to try implementing the
> Shootout's "cheap concurrency" test [1].  However I'm encountering
> very odd problems.

The 'bagley' directory already contains many of the (older)
shootout tests.

The concurrency test I implemented for the Shootout is one
reason Felix was banned from the Shootout by Isaac Gouy.
It's too fast. Gouy is a nazi and simply banned Felix
without trying to understand that the problem was the
test methodology. [Jon Harrop agreed with me and also
got kicked out]


> def var i, val o = mk_ioschannel_pair[int] ();
> 
> proc worker(i: ischannel[int], o: oschannel[int])() {
>     forever {
>         var &x: int <- read i;
>         write(o, x + 1);
>     };
> }
> 
> var t: int;
> forall t in 1 upto 500 do
>     val i' = i;
>     def i, val o' = mk_ioschannel_pair[int] ();
>     spawn_fthread$ worker(i', o');
> done;

I think the problem is a known bug. When a call which creates
a closure is inlined, it refers to the original variable
instead of a variable containing a copy.

The workaround is the adjective 'noinline'. I will check your
case but I guess the problem is here:

    spawn_fthread$ worker(i', o');

The problem is that worker is being inlined, and so refers to
the *current* value of the variables i' and o'. Note that
just because o' is a val does not mean it cannot be modified:
it can indeed be modified if it is initialised inside a loop.

There is a better solution than 'noinline' though: write
the code using tail recursion instead of a loop.

The tail rec optimiser is smart enough to not reuse the
stack frame.

So again note: this is a known semantic bug in Felix.
I have no idea how to fix it. It only occurs with dynamic
closure creation where the function creating the closure
is inlined, and the closure refers to an argument of the
inlined function, which is lifted out into the callers
stack frame -- thereby failing to capture the value
of the variable at the point the outer function is called.

The problem is roughly that when you have a curried function:

        fun f (x:int) (y:int)=> x + y;

then you really do want to inline calls to it. But if you write:

        g = f 1;
        r = g 2;

you see that f really returns a closure .. but this code is the
same as

        r = f 1 2;

except there is no explicit 'g' variable. To inline this thing
and get

        r = x + y;

which is mandatory for performance, always works. But in the
decoupled case with the explicit g, if the use of g is defered,
then it doesn't work .. because it refers to the current
values of 'x' and 'y' .. not the ones at the time the closure
was created.

I can certainly fix the bug by simply not inlining functions
that return closures .. but that would include the trivial
case of 'f' above: the performance hit isn't acceptable.

The analysis for a simple loop should be correct: but
by 'simple loop' i mean a tail recursive procedure call.

Arbitrary code contains gotos, and so requires much more
sophisticated data flow analysis to detect if it is safe
to inline a function returning a closure (it is safe
if the closure is used before any variables
it depends on which would be inlined are modified,
otherwise it isn't safe).




-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Felix-language mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to