Re: Parallelism and Concurrency was Re: Ideas for a (nntp: message (nntp: message 18 of 20) 14 of 20) "Object-Belongs-to-Thread" threading model

2010-05-18 Thread Daniel Ruoso
Em Ter, 2010-05-18 às 15:15 +0100, nigelsande...@btconnect.com escreveu:
> > 1) the interpreter doesn't need to detect the closed over variables, so
> > even string eval'ed access to such variables would work (which is, imho,
> > a good thing)
> You'd have to explain further for me to understand why it is necessary to  
> keep whole scopes around:
> - in order to make closures accessible from string-eval;
> - and why that is desirable?

I have no strong opinion on that, actually... Pointing to the outer
scope was simply an easy way to have it working... 

But this is currently, iirc, a requirement for the language, and
supported by rakudo and pugs...

 perl6: my $a; { my $b = 1; $a = sub { return eval '$b++' } };
say $a.(); say $a.(); say $a.();
 ..pugs, rakudo 689429: OUTPUT«1␤2␤3␤»


> > 2) all the values in that lexical scope are also preserved with the
> > closure, even if they won't be used (which is a bad thing).
> Please no! :)
> This is essentially the biggest problem with the Perl 5 iThreads  
> implementation. It is the *need* (though I have serious doubts that it is  
> actually a need even for Perl 5), to CLONE entire scope stacks every time  
> you spawn a thread that makes them costly to use.

hmmm... I wasn't expecting to clone the entire scope stack, but rather
to ask the owner of the outer scope for a value...

But I have to admit that importing the symbols used from outer scopes to
the current scope and making them shared (or a RemoteValue in my
original idea) is probably prettier. Accessing the OUTER scope in
run-time (via string eval, OUTER::{$var} or CALLER::{$var}) could be
subject to additional restrictions.

daniel



Re: Parallelism and Concurrency was Re: Ideas for a (nntp: message (nntp: message 18 of 20) 14 of 20) "Object-Belongs-to-Thread" threading model

2010-05-18 Thread nigelsandever

On Tue, 18 May 2010 11:41:08 +0100, Daniel Ruoso  wrote:


Em Dom, 2010-05-16 às 19:34 +0100, nigelsande...@btconnect.com escreveu:

Interoperability with Perl 5 and
is reference counting should not be a high priority in the decision  
making

process for defining the Perl 6 concurrency model.


If we drop that requirement then we can simply go to the
we-can-spawn-as-many-os-threads-as-we-want model..



I do not see that as a requirement. But, I am painfully aware that I am  
playing catchup with all the various versions, flavours and colors of  
Perl6 interpreter. And more importantly, the significance of each of tehm.  
When I recently started following #perl6 I was blown away (and totally  
confused) by all the various flavours that the eval bot responded to.


The funny thing is that I have a serious soft spot for the timelyness of  
reference couting "GC". And I recently came across a paper on a new RCGC  
that claimed to address the circular reference problem without resorting  
to weak references or other labour intensive mechanisms; nor a  
stop-the-world GC cycle. I scanned the paper and it was essentially a  
multi-pass "coloring" scheme, but achived better performaince than most by


a) running locally (to scopes I think) so that it had far fewer arenas to  
scan.
b) Using a invative coloring scheme that meant it was O(N) rather than the  
usual O(N * M)


Most of it went over my head, (as is often the case with aademic papers),  
but it seems real. But I think that is a boat that has long sailed for  
Perl 6?




daniel



Re: Parallelism and Concurrency was Re: Ideas for a (nntp: message (nntp: message 18 of 20) 14 of 20) "Object-Belongs-to-Thread" threading model

2010-05-18 Thread nigelsandever

On Tue, 18 May 2010 11:39:04 +0100, Daniel Ruoso  wrote:


This is the point I was trying to address, actually. Having *only*
explicitly shared variables makes it very cumbersome to write threaded
code, specially because explicitly shared variables have a lot of
restrictions on what they can be (this is from my experience in Perl 5
and SDL, which was what brought me to the message-passing idea).



Well, do not base anything upon the restrictions and limitations of the  
Perl 5 threads/shared modules. They are broken-by-design in so many ways  
that they are not a good reference point. That particular  
restriction--what a :shared var can and cannot hold--is in some cases just  
an arbitrary restriction for no good reason that I can see.


For example: file handles cannot be assigned to :shared vars is totally  
arbitrary. This can be demonstrated in two ways:


1) If you pass the fileno of the filehandle to a thread and have it dup(2)  
a copy, then it can use it concurrently with the originating thread  
without problems--subject to the obvious locking requirements.


2) I've previously hacked the sources to bypass this restrict by adding  
SVt_PVGV to the switch in the following function:



SV *
Perl_sharedsv_find(pTHX_ SV *sv)
{
MAGIC *mg;
if (SvTYPE(sv) >= SVt_PVMG) {
switch(SvTYPE(sv)) {
case SVt_PVAV:
case SVt_PVHV:
case SVt_PVGV: // !!!
if ((mg = mg_find(sv, PERL_MAGIC_tied))
&& mg->mg_virtual == &sharedsv_array_vtbl) {
return ((SV *)mg->mg_ptr);
}
break;
default:
/* This should work for elements as well as they
 * have scalar magic as well as their element magic
 */
if ((mg = mg_find(sv, PERL_MAGIC_shared_scalar))
&& mg->mg_virtual == &sharedsv_scalar_vtbl) {
return ((SV *)mg->mg_ptr);
}
break;
}
}
/* Just for tidyness of API also handle tie objects */
if (SvROK(sv) && sv_derived_from(sv, "threads::shared::tie")) {
return (S_sharedsv_from_obj(aTHX_ sv));
}
return (NULL);
}

And with that one change, sharing file/directory handles in Perl 5 became  
possible and worked.


The problem is, GVs can hold far more than just those handles. And many of  
the glob-modules utilise the other slots in a GV (array/hahs scalaer etc.)  
for storing state and bless them as objects. At that point--when I tried  
the change--the was a conflict between the blessing that Shared.XS uses to  
make sharing working and any other type of blessing. The net result was  
that whilst the change lifted the restriction upon simple globs, it still  
didn't work with many of the most useful glob-based module--IO::Socket::*;  
HTTP::Deamon; etc. I guess that now the sharing of blessed objects has  
been mage possible, I shoudl try the hack again a see if it would allow  
those blessed globs to work.


Anyway, the point is that the limitations and restrictions of the Perl5  
implementation of the iThreads model, should not be considered as  
fundamental problems with with the iThreads model itself. They aren't.


However, interpreters already have to detect closed over variables  
in

order to 'lift' them and extend their lifetimes beyond their natural
scope.


Actually, the interpreter might choose to to implement the closed-up
variables by keeping that entire associated scope when it is still
referenced by another value, i.e.:

 { my $a;
   { my $b = 1;
 { $a = sub { $b++ } } }

this would happen by the having every lexical scope holding a reference
to its outer scope, so when a scope in the middle exits, but some
coderef was returned keeping it as its lexical outer, the entire scope
would be kept.

This means two things:

1) the interpreter doesn't need to detect the closed over variables, so
even string eval'ed access to such variables would work (which is, imho,
a good thing)


You'd have to explain further for me to understand why it is necessary to  
keep whole scopes around:

- in order to make closures accessible from string-eval;
- and why that is desirable?



2) all the values in that lexical scope are also preserved with the
closure, even if they won't be used (which is a bad thing).



Please no! :)

This is essentially the biggest problem with the Perl 5 iThreads  
implementation. It is the *need* (though I have serious doubts that it is  
actually a need even for Perl 5), to CLONE entire scope stacks every time  
you spawn a thread that makes them costly to use. Both because of the time  
it takes to perform the clone at spawn time; and the memory used to keep  
copies of all that stuff that simply isn't wanted; and in many cases isn't  
even accessible. AFAIK going by what I can find about the history of  
iThreads development, this was only done in Perl 5 in order to provide the  
Windows fork emulation.


But as a predom

Re: Parallelism and Concurrency was Re: Ideas for a (nntp: message (nntp: message 18 of 20) 14 of 20) "Object-Belongs-to-Thread" threading model

2010-05-18 Thread Daniel Ruoso
Em Dom, 2010-05-16 às 19:34 +0100, nigelsande...@btconnect.com escreveu:
> Interoperability with Perl 5 and  
> is reference counting should not be a high priority in the decision making  
> process for defining the Perl 6 concurrency model.

If we drop that requirement then we can simply go to the
we-can-spawn-as-many-os-threads-as-we-want model..


daniel




Re: Parallelism and Concurrency was Re: Ideas for a (nntp: message (nntp: message 18 of 20) 14 of 20) "Object-Belongs-to-Thread" threading model

2010-05-18 Thread Daniel Ruoso
Em Dom, 2010-05-16 às 19:34 +0100, nigelsande...@btconnect.com escreveu:
> 3) The tough-y: Closed-over variables.
> These are tough because it exposes lexicals to sharing, but they are so  
> natural to use, it is hard to suggest banning their use in concurrent  
> routines.

This is the point I was trying to address, actually. Having *only*
explicitly shared variables makes it very cumbersome to write threaded
code, specially because explicitly shared variables have a lot of
restrictions on what they can be (this is from my experience in Perl 5
and SDL, which was what brought me to the message-passing idea).

> However, interpreters already have to detect closed over variables in  
> order to 'lift' them and extend their lifetimes beyond their natural  
> scope.

Actually, the interpreter might choose to to implement the closed-up
variables by keeping that entire associated scope when it is still
referenced by another value, i.e.:

 { my $a;
   { my $b = 1;
 { $a = sub { $b++ } } }

this would happen by the having every lexical scope holding a reference
to its outer scope, so when a scope in the middle exits, but some
coderef was returned keeping it as its lexical outer, the entire scope
would be kept.

This means two things:

1) the interpreter doesn't need to detect the closed over variables, so
even string eval'ed access to such variables would work (which is, imho,
a good thing)

2) all the values in that lexical scope are also preserved with the
closure, even if they won't be used (which is a bad thing).

> It doesn't seem it would be any harder to lift them to shared  
> variable status, moving them out of the thread-local lexical pads and into  
> the same data-space as process globals and explicitly shared data.

It is still possible to do the detection on the moment of the runtime
lookup, tho...

> My currently favoured mechanism for handling shared data, is via  
> message-passing, but passing references to the shared data, rather than  
> the data itself. This seems to give the reason-ability, compose-ability  
> and controlled access of message passing whilst retaining the efficiency  
> of direct, shared-state mutability.

That was part of my idea too, I wasn't trying to address remote
processes or anything like that, I was considering doing the queues in
shared memory for its efficiency.

> Only the code that declares the shared  
> data, plus any other thread it choses to send a handle to, has any  
> knowledge of, and therefore access to the shared state.

If we can overcome the limitations we have in Perl 5 shared values, I'm
entirely in agreement with the above statement (assuming closed-over
values become shared transparently)

> Effectively, allocating a shared entity returns a handle to the underlying  
> state, and only the holder of that handle can access it. Such handles  
> would be indirect references and only usable from the thread that creates  
> them. When a handle is passed as a message to another thread, it is  
> transformed into a handle usable by the recipient thread during the  
> transfer and the old handle becomes invalid. Attempt to use an old handle  
> after it has been sent result in a runtime exception.

This is exactly what I meant by RemoteValue, RemoteInvocation and
InvocationQueue in my original idea. 

daniel



Re: Parallelism and Concurrency was Re: Ideas for a (nntp: message (nntp: message 18 of 20) 14 of 20) "Object-Belongs-to-Thread" threading model

2010-05-16 Thread nigelsandever
On Fri, 14 May 2010 20:00:01 +0100, Daniel Ruoso - dan...@ruoso.com  
<+nntp+browseruk+d52dbf78bb.daniel#ruoso@spamgourmet.com> wrote:



Em Sex, 2010-05-14 às 18:13 +0100, nigelsande...@btconnect.com escreveu:

The point I(we)'ve been trying to make is that once you have a reentrant
interpreter, and the ability to spawn one in an OS thread,
all the other bits can be built on top. But unless you have that  
ability,

whilst the others can be constructed, the cannot make
use of SMP. Eg. They cannot scale.


Okay, this is an important point... Having a reentrant interpreter is a
given already, The form I tried to implement that in SMOP was by using
CPS.

The idea of using green threads is just to enforce serialized access to
some data in order to avoid locking (locking with the amount of
polymorphism required by Perl 6 is too expensive), and then the regular
threads wouldn't share data between them.

Of course we could try to have the data structures thread-safe, I didn't
try to go that path since I was trying to get Perl 5 interoperability
and therefore used refcount garbage collector.

The other possibility would be to use processor affinity to force
different threads to spawn in the same processor and that way ensure
serialized access to the non-thread-safe data.

daniel




Sorry, but restricting yourself to one processor in order to avoid locking  
makes no sense at all.


It's like making one of your 4x400m relay runners run the whole race, to  
save carrying and passing the baton.



There are essentially four cases to consider:

1) IO-bound coroutines running on a single CPU machine:
   Locking will have very little effect upon coroutines bound by IO.

2) CPU-bound coroutines running on a single CPU machine:
   Locking would have some effect on throughput.
   But why would you parallelise CPU-bound coroutines on a single core.  
Even without locking, the context switching would have a negative affect  
upon throughput. You'd be better to just run the routines serially.


3) IO-bound coroutines running on a multi-core machine:
   Again, locking has almost no effect due to IO latency.

4) CPU-bound coroutines running on a multi-core machine:
   There is no way that the absence of locking is going to compensate for  
only using 1/2 of the available processor power on a 2-core or  
hyper-threading processor; much less only 1/4 of the current crop quad-CPU  
commodity processors.



Also, basing the Perl 6 concurrency model upon what is convenient for the  
implementation of SMOP:


http://www.perlfoundation.org/perl6/index.cgi?smop

as clever as it is, and as important as that has been to the evolution of  
the Perl 6 development effort, is not a good idea.


Given it's dependency list, it seems unlikely that SMOP is going to become  
*the* perl interpreter in the long term. Interoperability with Perl 5 and  
is reference counting should not be a high priority in the decision making  
process for defining the Perl 6 concurrency model.


Ultimately, I think your apprehensions about the costs of locking are  
unfounded.


If your interpreter is reentrant, then you should be able to start two  
copies in two threads, without any need to add internal locking as far as  
lexicals are concerned, because the scoping will prevent any attempts at  
concurrent access. Put simply, neither will be able to see the variables  
within the other, so even though they share the same address space, the  
language scoping keeps them apart.


There are 3 exceptions to this:

1) Process global entities: file & directory handles, environment  
variables etc.


These are sufficiently rare that applying per entity locking is not a  
problem.


Per entity (1 bit in the status word for each scalar), user space  
locking (bit test & set) requires minimal development/maintenance effort  
and imposes minimal runtime performance impact, in the light of the  
overhead of IO anyway.


2) Explicitly shared data.

These require (internal) locks regardless of how you wrap them. Be it  
with STM--which is looking less and less viable; message passing which  
works but precludes (or renders grossly inefficient), many data-parallel  
algorithms. I've reached the conclusion that some provision of user  
specified locking must be made available for some applications. But it  
doesn't mean there isn't the scope for hiding the complexities of the  
underlying (POSIX) mechanisms.


3) The tough-y: Closed-over variables.

   These are tough because it exposes lexicals to sharing, but they are so  
natural to use, it is hard to suggest banning their use in concurrent  
routines.


   However, interpreters already have to detect closed over variables in  
order to 'lift' them and extend their lifetimes beyond their natural  
scope. It doesn't seem it would be any harder to lift them to shared  
variable status, moving them out of the thread-local lexical pads and into  
the same data-space as process globals and explicitly shared