Explicitly avoiding process switch

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

Explicitly avoiding process switch

Mariano Martinez Peck
 
Hi guys. We were discussing yesterday with Marcus about the following....

 I remember a problem we have in Pharo because of changing "foo == nil" to "foo isNil"  in some important places like Process/Delay/Semaphore. Such change introduces a suspension point, where if I understand correctly, the scheduler can switch to another process only during method sends. #== is optimized with a special bytecode and there is no message send. Sending #isNil is a message sent, and hence we introduce a suspension point. In 99% of the cases this is not a problem.  But I can imagine that there are places (mostly those related to Process/Delay/Semaphore) where this changes can be very very bad.

 
To do some experiments, I want to remove the bytecode for #== but of course, I think I will have all these problems. And there are more. Marcus told me that some optimizations done by the compiler end up sending #==. And I also remember some code he told me (I think it was #allObjectsDo:) that would never finish because of the block closure objects creation.

So...what about making the necessity of NOT sending a message or NOT introducing a suspension point more explicit?  first we should have the support for that and second know each place where we need them.

The most basic thing I can think of is doing something like:

(ParseNode classVarNamed:  'StdSelectors') at: #== put: #bytecodedEqualEqual.
Compiler recompileAll.    

And put a nice comment why one may want to use #optimizedEqualEqual. But again, in which places should I change #== for #bytecodedEqualEqual


Any thought or idea about them is really appreciated.

Thanks

 
--
Mariano
http://marianopeck.wordpress.com

Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

Nicolas Cellier

I would properly comment the places where this "ATOMIC" operation is
really used.

Nicolas

2011/8/11 Mariano Martinez Peck <[hidden email]>:

>
> Hi guys. We were discussing yesterday with Marcus about the following....
>
>  I remember a problem we have in Pharo because of changing "foo == nil" to "foo isNil"  in some important places like Process/Delay/Semaphore. Such change introduces a suspension point, where if I understand correctly, the scheduler can switch to another process only during method sends. #== is optimized with a special bytecode and there is no message send. Sending #isNil is a message sent, and hence we introduce a suspension point. In 99% of the cases this is not a problem.  But I can imagine that there are places (mostly those related to Process/Delay/Semaphore) where this changes can be very very bad.
>
>
> To do some experiments, I want to remove the bytecode for #== but of course, I think I will have all these problems. And there are more. Marcus told me that some optimizations done by the compiler end up sending #==. And I also remember some code he told me (I think it was #allObjectsDo:) that would never finish because of the block closure objects creation.
>
> So...what about making the necessity of NOT sending a message or NOT introducing a suspension point more explicit?  first we should have the support for that and second know each place where we need them.
>
> The most basic thing I can think of is doing something like:
>
> (ParseNode classVarNamed:  'StdSelectors') at: #== put: #bytecodedEqualEqual.
> Compiler recompileAll.
>
> And put a nice comment why one may want to use #optimizedEqualEqual. But again, in which places should I change #== for #bytecodedEqualEqual
>
>
> Any thought or idea about them is really appreciated.
>
> Thanks
>
>
> --
> Mariano
> http://marianopeck.wordpress.com
>
>
>
Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

Igor Stasenko

On 11 August 2011 13:17, Nicolas Cellier
<[hidden email]> wrote:
>
> I would properly comment the places where this "ATOMIC" operation is
> really used.

Like in
AtomicQueueItem>>makeCircular? :)


I also remember discussion that some precautions in code to not insert
suspension point(s) in ProcessorScheduler/Process/or Semaphore..
but i forgot which one exactly.
Is it LinkedList>>removeLink:?


> Nicolas
>
> 2011/8/11 Mariano Martinez Peck <[hidden email]>:
>>
>> Hi guys. We were discussing yesterday with Marcus about the following....
>>
>>  I remember a problem we have in Pharo because of changing "foo == nil" to "foo isNil"  in some important places like Process/Delay/Semaphore. Such change introduces a suspension point, where if I understand correctly, the scheduler can switch to another process only during method sends. #== is optimized with a special bytecode and there is no message send. Sending #isNil is a message sent, and hence we introduce a suspension point. In 99% of the cases this is not a problem.  But I can imagine that there are places (mostly those related to Process/Delay/Semaphore) where this changes can be very very bad.
>>
>>
>> To do some experiments, I want to remove the bytecode for #== but of course, I think I will have all these problems. And there are more. Marcus told me that some optimizations done by the compiler end up sending #==. And I also remember some code he told me (I think it was #allObjectsDo:) that would never finish because of the block closure objects creation.
>>
>> So...what about making the necessity of NOT sending a message or NOT introducing a suspension point more explicit?  first we should have the support for that and second know each place where we need them.
>>
>> The most basic thing I can think of is doing something like:
>>
>> (ParseNode classVarNamed:  'StdSelectors') at: #== put: #bytecodedEqualEqual.
>> Compiler recompileAll.
>>
>> And put a nice comment why one may want to use #optimizedEqualEqual. But again, in which places should I change #== for #bytecodedEqualEqual
>>
>>
>> Any thought or idea about them is really appreciated.
>>
>> Thanks
>>
>>
>> --
>> Mariano
>> http://marianopeck.wordpress.com
>>
>>
>>
>



--
Best regards,
Igor Stasenko AKA sig.
Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

Henrik Sperre Johansen
 

On Aug 11, 2011, at 2:11 37PM, Igor Stasenko wrote:


On 11 August 2011 13:17, Nicolas Cellier
<[hidden email]> wrote:

I would properly comment the places where this "ATOMIC" operation is
really used.

Like in
AtomicQueueItem>>makeCircular? :)


I also remember discussion that some precautions in code to not insert
suspension point(s) in ProcessorScheduler/Process/or Semaphore..
but i forgot which one exactly.
Is it LinkedList>>removeLink:?


So suspension points in remove:ifAbsent (as written now)  and removeLink: are ok.
Not in removeLink:ifAbsent: though.

Used to be:
link := self linkOf: aLinkOrObject ifAbsent: [^aBlock value].
self removeLink: link.
^aLinkOrObject

Because, well, I didn't think you'd ever get in a situation where :
1) two processes would try to remove the same link
2)  first of them suspends after link assignment
3) second carries through, and actually removes link
4) first calls removeLink: , and default block raising error is triggered when link is not found.

Cheers,
Henry
Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

Igor Stasenko

On 11 August 2011 14:28, Henrik Johansen <[hidden email]> wrote:

>
>
> On Aug 11, 2011, at 2:11 37PM, Igor Stasenko wrote:
>
> On 11 August 2011 13:17, Nicolas Cellier
> <[hidden email]> wrote:
>
> I would properly comment the places where this "ATOMIC" operation is
>
> really used.
>
> Like in
> AtomicQueueItem>>makeCircular? :)
>
>
> I also remember discussion that some precautions in code to not insert
> suspension point(s) in ProcessorScheduler/Process/or Semaphore..
> but i forgot which one exactly.
> Is it LinkedList>>removeLink:?
>
> http://code.google.com/p/pharo/issues/detail?id=3498
> So suspension points in remove:ifAbsent (as written now)  and removeLink: are ok.
> Not in removeLink:ifAbsent: though.
> Used to be:
> link := self linkOf: aLinkOrObject ifAbsent: [^aBlock value].
> self removeLink: link.
> ^aLinkOrObject

i don't like this code, because it iterating list twice, first to find a link,
and second time to remove it.

I don't think it is a big secret that removing an element from linked
list could be done using single iteration :)

> Because, well, I didn't think you'd ever get in a situation where :
> 1) two processes would try to remove the same link
> 2)  first of them suspends after link assignment
> 3) second carries through, and actually removes link
> 4) first calls removeLink: , and default block raising error is triggered when link is not found.
> Cheers,
> Henry
>



--
Best regards,
Igor Stasenko AKA sig.
Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

Henrik Sperre Johansen


On Aug 11, 2011, at 2:59 56PM, Igor Stasenko wrote:

>
> On 11 August 2011 14:28, Henrik Johansen <[hidden email]> wrote:
>>
>>
>> On Aug 11, 2011, at 2:11 37PM, Igor Stasenko wrote:
>>
>> On 11 August 2011 13:17, Nicolas Cellier
>> <[hidden email]> wrote:
>>
>> I would properly comment the places where this "ATOMIC" operation is
>>
>> really used.
>>
>> Like in
>> AtomicQueueItem>>makeCircular? :)
>>
>>
>> I also remember discussion that some precautions in code to not insert
>> suspension point(s) in ProcessorScheduler/Process/or Semaphore..
>> but i forgot which one exactly.
>> Is it LinkedList>>removeLink:?
>>
>> http://code.google.com/p/pharo/issues/detail?id=3498
>> So suspension points in remove:ifAbsent (as written now)  and removeLink: are ok.
>> Not in removeLink:ifAbsent: though.
>> Used to be:
>> link := self linkOf: aLinkOrObject ifAbsent: [^aBlock value].
>> self removeLink: link.
>> ^aLinkOrObject
>
> i don't like this code, because it iterating list twice, first to find a link,
> and second time to remove it.
>
> I don't think it is a big secret that removing an element from linked
> list could be done using single iteration :)

You really only need to iterate to find the correct one if passed a non-link.
So the following is probably better:

linkOf: anObject ifAbsent: errorBlock
        anObject asLink == anObject ifTrue: [^anObject].
        self
                linksDo: [:el | el value = anObject
                                ifTrue: [^ el]].
        ^ errorBlock value



Then

ll := LinkedList new.
link := ll addFirst: 5.
ll remove: link ifAbsent: [self halt]

works without halting as well :)

Cheers,
Henry
Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

stephane ducasse-2
In reply to this post by Nicolas Cellier

One idea marcus mentioned was to have an explicit bytecode for suspension point.
I think that explicit semantics and behavior is always better.

Marcus is also thinking about expression or method tagging that the compiler could take into account
to generate this specific bytecode.

Stef


>
> I would properly comment the places where this "ATOMIC" operation is
> really used.
>
> Nicolas
>
> 2011/8/11 Mariano Martinez Peck <[hidden email]>:
>>
>> Hi guys. We were discussing yesterday with Marcus about the following....
>>
>>  I remember a problem we have in Pharo because of changing "foo == nil" to "foo isNil"  in some important places like Process/Delay/Semaphore. Such change introduces a suspension point, where if I understand correctly, the scheduler can switch to another process only during method sends. #== is optimized with a special bytecode and there is no message send. Sending #isNil is a message sent, and hence we introduce a suspension point. In 99% of the cases this is not a problem.  But I can imagine that there are places (mostly those related to Process/Delay/Semaphore) where this changes can be very very bad.
>>
>>
>> To do some experiments, I want to remove the bytecode for #== but of course, I think I will have all these problems. And there are more. Marcus told me that some optimizations done by the compiler end up sending #==. And I also remember some code he told me (I think it was #allObjectsDo:) that would never finish because of the block closure objects creation.
>>
>> So...what about making the necessity of NOT sending a message or NOT introducing a suspension point more explicit?  first we should have the support for that and second know each place where we need them.
>>
>> The most basic thing I can think of is doing something like:
>>
>> (ParseNode classVarNamed:  'StdSelectors') at: #== put: #bytecodedEqualEqual.
>> Compiler recompileAll.
>>
>> And put a nice comment why one may want to use #optimizedEqualEqual. But again, in which places should I change #== for #bytecodedEqualEqual
>>
>>
>> Any thought or idea about them is really appreciated.
>>
>> Thanks
>>
>>
>> --
>> Mariano
>> http://marianopeck.wordpress.com
>>
>>
>>

Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

ccrraaiigg
In reply to this post by Nicolas Cellier
 

> I would properly comment the places where this "ATOMIC" operation is
> really used.

     Yeah, whatever is done behind the scenes (an explicit instruction
for the suspension point, etc.), I think it's also important to document
what's going on at the source level.


-C

--
Craig Latta
www.netjam.org/resume
+31   6 2757 7177
+ 1 415  287 3547


Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

Igor Stasenko
In reply to this post by stephane ducasse-2

On 11 August 2011 16:28, stephane ducasse <[hidden email]> wrote:
>
> One idea marcus mentioned was to have an explicit bytecode for suspension point.
> I think that explicit semantics and behavior is always better.
>
> Marcus is also thinking about expression or method tagging that the compiler could take into account
> to generate this specific bytecode.
>

We're discussed this today.
My own point that we should design our algorithms to never rely on
some specific scheduling semantics,
but always assume that an interrupt could happen at any moment, or
more in general:
two or more processes could run in parallel.

Because if you won't let baby to start walking and running (and of
course there are 90% risk of falling down at the beginning),
by tying it tightly in baby carriage, one have zero chances to learn
how to walk without falling down.


> Stef
>
>
>>
>> I would properly comment the places where this "ATOMIC" operation is
>> really used.
>>
>> Nicolas
>>
>> 2011/8/11 Mariano Martinez Peck <[hidden email]>:
>>>
>>> Hi guys. We were discussing yesterday with Marcus about the following....
>>>
>>>  I remember a problem we have in Pharo because of changing "foo == nil" to "foo isNil"  in some important places like Process/Delay/Semaphore. Such change introduces a suspension point, where if I understand correctly, the scheduler can switch to another process only during method sends. #== is optimized with a special bytecode and there is no message send. Sending #isNil is a message sent, and hence we introduce a suspension point. In 99% of the cases this is not a problem.  But I can imagine that there are places (mostly those related to Process/Delay/Semaphore) where this changes can be very very bad.
>>>
>>>
>>> To do some experiments, I want to remove the bytecode for #== but of course, I think I will have all these problems. And there are more. Marcus told me that some optimizations done by the compiler end up sending #==. And I also remember some code he told me (I think it was #allObjectsDo:) that would never finish because of the block closure objects creation.
>>>
>>> So...what about making the necessity of NOT sending a message or NOT introducing a suspension point more explicit?  first we should have the support for that and second know each place where we need them.
>>>
>>> The most basic thing I can think of is doing something like:
>>>
>>> (ParseNode classVarNamed:  'StdSelectors') at: #== put: #bytecodedEqualEqual.
>>> Compiler recompileAll.
>>>
>>> And put a nice comment why one may want to use #optimizedEqualEqual. But again, in which places should I change #== for #bytecodedEqualEqual
>>>
>>>
>>> Any thought or idea about them is really appreciated.
>>>
>>> Thanks
>>>
>>>
>>> --
>>> Mariano
>>> http://marianopeck.wordpress.com
>>>
>>>
>>>
>
>



--
Best regards,
Igor Stasenko AKA sig.
Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

stephane ducasse-2


On Aug 11, 2011, at 5:10 PM, Igor Stasenko wrote:

>
> On 11 August 2011 16:28, stephane ducasse <[hidden email]> wrote:
>>
>> One idea marcus mentioned was to have an explicit bytecode for suspension point.
>> I think that explicit semantics and behavior is always better.
>>
>> Marcus is also thinking about expression or method tagging that the compiler could take into account
>> to generate this specific bytecode.
>>
>
> We're discussed this today.
> My own point that we should design our algorithms to never rely on
> some specific scheduling semantics,
> but always assume that an interrupt could happen at any moment, or
> more in general:
> two or more processes could run in parallel.
>
> Because if you won't let baby to start walking and running (and of
> course there are 90% risk of falling down at the beginning),
> by tying it tightly in baby carriage, one have zero chances to learn
> how to walk without falling down.

yes this us my favorite solution :)

>
>
>> Stef
>>
>>
>>>
>>> I would properly comment the places where this "ATOMIC" operation is
>>> really used.
>>>
>>> Nicolas
>>>
>>> 2011/8/11 Mariano Martinez Peck <[hidden email]>:
>>>>
>>>> Hi guys. We were discussing yesterday with Marcus about the following....
>>>>
>>>>  I remember a problem we have in Pharo because of changing "foo == nil" to "foo isNil"  in some important places like Process/Delay/Semaphore. Such change introduces a suspension point, where if I understand correctly, the scheduler can switch to another process only during method sends. #== is optimized with a special bytecode and there is no message send. Sending #isNil is a message sent, and hence we introduce a suspension point. In 99% of the cases this is not a problem.  But I can imagine that there are places (mostly those related to Process/Delay/Semaphore) where this changes can be very very bad.
>>>>
>>>>
>>>> To do some experiments, I want to remove the bytecode for #== but of course, I think I will have all these problems. And there are more. Marcus told me that some optimizations done by the compiler end up sending #==. And I also remember some code he told me (I think it was #allObjectsDo:) that would never finish because of the block closure objects creation.
>>>>
>>>> So...what about making the necessity of NOT sending a message or NOT introducing a suspension point more explicit?  first we should have the support for that and second know each place where we need them.
>>>>
>>>> The most basic thing I can think of is doing something like:
>>>>
>>>> (ParseNode classVarNamed:  'StdSelectors') at: #== put: #bytecodedEqualEqual.
>>>> Compiler recompileAll.
>>>>
>>>> And put a nice comment why one may want to use #optimizedEqualEqual. But again, in which places should I change #== for #bytecodedEqualEqual
>>>>
>>>>
>>>> Any thought or idea about them is really appreciated.
>>>>
>>>> Thanks
>>>>
>>>>
>>>> --
>>>> Mariano
>>>> http://marianopeck.wordpress.com
>>>>
>>>>
>>>>
>>
>>
>
>
>
> --
> Best regards,
> Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

Re: Explicitly avoiding process switch

Levente Uzonyi-2
In reply to this post by stephane ducasse-2
 
On Thu, 11 Aug 2011, stephane ducasse wrote:

>
> One idea marcus mentioned was to have an explicit bytecode for suspension point.
> I think that explicit semantics and behavior is always better.

What's the point of having a bytecode for suspension points?

Since the goal is to avoid suspension points, the best solution is to use
pragmas that the compiler takes into account. It can either be global for
the given method or it can specify some selectors which should be
optimized to avoid suspension points. Here's an example for the per
selector version:

critical: mutuallyExcludedBlock ifLocked: alternativeBlock
  "Evaluate mutuallyExcludedBlock only if the receiver is not currently in
  the process of running the critical: message. If the receiver is, then evaluate
  alternativeBlock and return."
  "See the comment of #critical: for the explanation how this pattern works
  before changing the code."

  | caught |
  <useByteCodesFor: #(== ifTrue:ifFalse: -)>
  caught := false.
  ^[
  "We're using #== here instead of #=, because it won't introduce a
  suspension point, while #= may do that."
  excessSignals == 0
  ifTrue: [ alternativeBlock value ]
  ifFalse: [
  excessSignals := excessSignals - 1.
  caught := true.
  mutuallyExcludedBlock value ] ]
  ensure: [ caught ifTrue: [ self signal ] ]


Levente

>
> Marcus is also thinking about expression or method tagging that the compiler could take into account
> to generate this specific bytecode.
>
> Stef
>
>
>>
>> I would properly comment the places where this "ATOMIC" operation is
>> really used.
>>
>> Nicolas
>>
>> 2011/8/11 Mariano Martinez Peck <[hidden email]>:
>>>
>>> Hi guys. We were discussing yesterday with Marcus about the following....
>>>
>>>  I remember a problem we have in Pharo because of changing "foo == nil" to "foo isNil"  in some important places like Process/Delay/Semaphore. Such change introduces a suspension point, where if I understand correctly, the scheduler can switch to another process only during method sends. #== is optimized with a special bytecode and there is no message send. Sending #isNil is a message sent, and hence we introduce a suspension point. In 99% of the cases this is not a problem.  But I can imagine that there are places (mostly those related to Process/Delay/Semaphore) where this changes can be very very bad.
>>>
>>>
>>> To do some experiments, I want to remove the bytecode for #== but of course, I think I will have all these problems. And there are more. Marcus told me that some optimizations done by the compiler end up sending #==. And I also remember some code he told me (I think it was #allObjectsDo:) that would never finish because of the block closure objects creation.
>>>
>>> So...what about making the necessity of NOT sending a message or NOT introducing a suspension point more explicit?  first we should have the support for that and second know each place where we need them.
>>>
>>> The most basic thing I can think of is doing something like:
>>>
>>> (ParseNode classVarNamed:  'StdSelectors') at: #== put: #bytecodedEqualEqual.
>>> Compiler recompileAll.
>>>
>>> And put a nice comment why one may want to use #optimizedEqualEqual. But again, in which places should I change #== for #bytecodedEqualEqual
>>>
>>>
>>> Any thought or idea about them is really appreciated.
>>>
>>> Thanks
>>>
>>>
>>> --
>>> Mariano
>>> http://marianopeck.wordpress.com
>>>
>>>
>>>
>
>