Hi! Threads in Pharo have always been mysterious for me. If I doit the following: [ true ] whileTrue Can other thread interrupt this? Say in other words, can the following piece of code may suffer from a concurrent problem in Pharo anOrderedCollection add: 42 My current understanding about thread is that there is a scheduling that may occurs each time we enter the VM (e.g., primitive call, instantiating an object, throwing an exception). So, the code "anOrderedCollection add: 42â will _never_ suffer from concurrent call because adding 42 to a collection does not enter the VM. Does this make sense? What is the current status of this? Thanks to you all Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Hi, On 24/03/14 17:56, Alexandre Bergel wrote:
Hi!
Threads in Pharo have always been mysterious for me. If I doit the following: [ true ] whileTrue Can other thread interrupt this?
Say in other words, can the following piece of code may suffer from a concurrent problem in Pharo anOrderedCollection add: 42
My current understanding about thread is that there is a scheduling that may occurs each time we enter the VM (e.g., primitive call, instantiating an object, throwing an exception). So, the code "anOrderedCollection add: 42â will _never_ suffer from concurrent call because adding 42 to a collection does not enter the VM. Does this make sense?
I don't think so. AFAIK interrupts are checked on message sends and on backward jumps (because of inlined loops just like in your example). So context switch may happen on each send... Jan
What is the current status of this?
Thanks to you all Alexandre
My current understanding about thread is that there is a scheduling that may occurs each time we enter the VM (e.g., primitive call, instantiating an object, throwing an exception). So, the code "anOrderedCollection add: 42â will _never_ suffer from concurrent call because adding 42 to a collection does not enter the VM. Does this make sense?
I don't think so. AFAIK interrupts are checked on message sends and on backward jumps (because of inlined loops just like in your example).
So context switch may happen on each send...
Ah yes!! I missed this case. Any idea what is the cost of using a semaphore? Inserting the expression "anOrderedCollection add: 42â in a semaphore surely make the expression slower. Any idea how much slower? Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Hi Alexandre: On 24 Mar 2014, at 19:20, Alexandre Bergel <alexandre.bergel@me.com> wrote:
Any idea what is the cost of using a semaphore? Inserting the expression "anOrderedCollection add: 42â in a semaphore surely make the expression slower. Any idea how much slower?
Can you elaborate a little on the problem. Your granularity does not seem to be of the right level. Covering a single #add: operations is most probably rather fine grained, and might not give you the guarantees you would expect. What exactly do you want to achieve? How many Smalltalk processes are interacting with that collection? How many consumer/producer do you have? And well, on the standard priority, you normally got cooperative scheduling anyway. So, it depends on what you are doing whether there is a real issue. And if you donât want to use a semaphore, there are also other mechanism. I think, there should be something like âexecute uninterruptibleâ for a block. Think that raises the priority of the processes to the highest level for the execution of the block, if I remember correctly. Best regards Stefan -- Stefan Marr INRIA Lille - Nord Europe http://stefan-marr.de/research/
Any idea what is the cost of using a semaphore? Inserting the expression "anOrderedCollection add: 42â in a semaphore surely make the expression slower. Any idea how much slower?
Can you elaborate a little on the problem.
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one. At the end, #add: and #remove: are performed on these polls of arrays. I havenât been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection newâ can pick an element of my global collection. I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
Your granularity does not seem to be of the right level. Covering a single #add: operations is most probably rather fine grained, and might not give you the guarantees you would expect. What exactly do you want to achieve? How many Smalltalk processes are interacting with that collection? How many consumer/producer do you have?
Basically, all the processes may access my global polls by inserting or removing elements.
And well, on the standard priority, you normally got cooperative scheduling anyway. So, it depends on what you are doing whether there is a real issue. And if you donât want to use a semaphore, there are also other mechanism. I think, there should be something like âexecute uninterruptibleâ for a block. Think that raises the priority of the processes to the highest level for the execution of the block, if I remember correctly.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far. Alexandre
Best regards Stefan
-- Stefan Marr INRIA Lille - Nord Europe http://stefan-marr.de/research/
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On 24/03/14 18:57, Alexandre Bergel wrote:
Any idea what is the cost of using a semaphore? Inserting the expression "anOrderedCollection add: 42â in a semaphore surely make the expression slower. Any idea how much slower?
Can you elaborate a little on the problem.
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Just out of curiosity, why do you do that? I would say is better throw them away (in most cases) Jan
Just out of curiosity, why do you do that? I would say is better throw them away (in most cases)
Just for the sake of publishing original ideas :-) Joking :-) The VM is considering expandable collections as simple objects, and this has a cost in terms of memory and CPU consumption. I will be able to say more at esug hopefully. Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
Hi Alexandre: On 24 Mar 2014, at 19:57, Alexandre Bergel <alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
At the end, #add: and #remove: are performed on these polls of arrays. I havenât been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection newâ can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isnât any âmulti-threadingâ like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway. However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization? Best regards Stefan
Alexandre
Best regards Stefan
-- Stefan Marr INRIA Lille - Nord Europe http://stefan-marr.de/research/
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
-- Stefan Marr INRIA Lille - Nord Europe http://stefan-marr.de/research/
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I havenât been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection newâ can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isnât any âmulti-threadingâ like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes. Thanks for all your help guys! Youâre great! Cheers, Alexandre -- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel <alexandre.bergel@me.com>wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work. BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
Here is the better documentation I found online about it when I learnt it: http://wiki.squeak.org/squeak/382 Afterwards... reading the vm and playing was the hard way to learn it... Then it seems there is an ongoing chapter in the topic https://ci.inria.fr/pharo-contribution/job/PharoForTheEnterprise/lastSuccess... On Mon, Mar 24, 2014 at 10:54 PM, phil@highoctane.be <phil@highoctane.be>wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel <alexandre.bergel@me.com
wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
Chapters 17 and 18 are interesting too. http://stephane.ducasse.free.fr/FreeBooks/ByExample/SmalltalkByExampleNewRel... On Mon, Mar 24, 2014 at 11:29 PM, Guillermo Polito < guillermopolito@gmail.com> wrote:
Here is the better documentation I found online about it when I learnt it:
http://wiki.squeak.org/squeak/382
Afterwards... reading the vm and playing was the hard way to learn it...
Then it seems there is an ongoing chapter in the topic
https://ci.inria.fr/pharo-contribution/job/PharoForTheEnterprise/lastSuccess...
On Mon, Mar 24, 2014 at 10:54 PM, phil@highoctane.be <phil@highoctane.be>wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
Also Smalltalk-80: the Language and its Implementation (the Blue Book<http://stephane.ducasse.free.fr/FreeBooks/BlueBook/Bluebook.pdf>) has a good chapter, Chapter 15: 15 Multiple Independent Processes On Mon, Mar 24, 2014 at 3:29 PM, Guillermo Polito <guillermopolito@gmail.com
wrote:
Here is the better documentation I found online about it when I learnt it:
http://wiki.squeak.org/squeak/382
Afterwards... reading the vm and playing was the hard way to learn it...
Then it seems there is an ongoing chapter in the topic
https://ci.inria.fr/pharo-contribution/job/PharoForTheEnterprise/lastSuccess...
On Mon, Mar 24, 2014 at 10:54 PM, phil@highoctane.be <phil@highoctane.be>wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
-- best, Eliot
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel <alexandre.bergel@me.com
wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I havenât been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection newâ can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isnât any âmulti-threadingâ like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! Youâre great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something". -- Best regards, Igor Stasenko.
On Tue, Mar 25, 2014 at 1:05 PM, Igor Stasenko <siguctua@gmail.com> wrote:
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something".
If you reshape the world, it makes sense. I was looking at how classes were migrated, that's why I found it. And all of the new Pharo way of doing these things. Hey, it is becoming really cool down there. Martin and Camille have been hard at work. Kudos! migrateClasses: old to: new using: anInstanceModification instanceModification := anInstanceModification. old ifEmpty: [ ^ self ]. [ 1 to: old size do: [ :index | self updateClass: (old at: index) to: (new at: index)]. old elementsForwardIdentityTo: new. " Garbage collect away the zombie instances left behind in garbage memory in #updateInstancesFrom: " " If we don't clean up this garbage, a second update would revive them with a wrong layout! " " (newClass rather than oldClass, since they are now both newClass) " Smalltalk garbageCollect. ] valueUnpreemptively KR Phil
-- Best regards, Igor Stasenko.
Hi Igor, On Tue, Mar 25, 2014 at 5:05 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something".
Really? Surely any system as interactive as Smalltalk can benefit from a stop-the-rest-of-the-world scheduling facility, and surely packaging it as BlockClosure>>valueUnpreemptively would be a convenient way of doing so. Surely the right attitude for an implementor of a threading system for Smalltalk would be "Sure, I can implement that, even in a truly concurrent, multi-processor environment". It may take some doing but it's an important facility to have. It shouldn't be abused, but when you need it, you need it. -- best, Eliot
On 25 March 2014 17:31, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Igor,
On Tue, Mar 25, 2014 at 5:05 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I havenât been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection newâ can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isnât any âmulti-threadingâ like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! Youâre great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something".
Really? Surely any system as interactive as Smalltalk can benefit from a stop-the-rest-of-the-world scheduling facility, and surely packaging it as BlockClosure>>valueUnpreemptively would be a convenient way of doing so. Surely the right attitude for an implementor of a threading system for Smalltalk would be "Sure, I can implement that, even in a truly concurrent, multi-processor environment". It may take some doing but it's an important facility to have. It shouldn't be abused, but when you need it, you need it.
There should be hard guarantees from VM to do it. Right now there's none. That's my point. Like special primitive(s) for disabling interrupts/scheduling and enabling it back again. Let us be realistic: the above implementation is based on insider's knowledge how scheduling works, lacking any notion of contract between VM and image. Right now it just based on implementation detail rather than on guaranteed and well defined semantic. It is no doubt, sometimes you may need such hammer to stop the world. And it is no doubt (to me) that one should avoid using it unless it is impossible to do otherwise.
-- best, Eliot
-- Best regards, Igor Stasenko.
Hi Igor, you have a point but I disagree. The scheduler is defined in the implementation section of the blue book. It could be more explicit, but the blue book scheduler is a known and simple system. In my threading work I've made sure to preserve its semantics (cooperative within priorities, preemptive across priorities, thread switch at activation of non-primitive sends and backward branches). The only serious bug I know of (that preempting sends a process to the back of its run-queue) was addressed in Cog and the VW VM. We can and should write up the semantics, but they're not undefined, they're simple and they do work. Eliot (phone) On Mar 25, 2014, at 10:11 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 25 March 2014 17:31, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Igor,
On Tue, Mar 25, 2014 at 5:05 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel <alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I havenât been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection newâ can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isnât any âmulti-threadingâ like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! Youâre great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something".
Really? Surely any system as interactive as Smalltalk can benefit from a stop-the-rest-of-the-world scheduling facility, and surely packaging it as BlockClosure>>valueUnpreemptively would be a convenient way of doing so. Surely the right attitude for an implementor of a threading system for Smalltalk would be "Sure, I can implement that, even in a truly concurrent, multi-processor environment". It may take some doing but it's an important facility to have. It shouldn't be abused, but when you need it, you need it.
There should be hard guarantees from VM to do it. Right now there's none. That's my point. Like special primitive(s) for disabling interrupts/scheduling and enabling it back again. Let us be realistic: the above implementation is based on insider's knowledge how scheduling works, lacking any notion of contract between VM and image. Right now it just based on implementation detail rather than on guaranteed and well defined semantic.
It is no doubt, sometimes you may need such hammer to stop the world. And it is no doubt (to me) that one should avoid using it unless it is impossible to do otherwise.
-- best, Eliot
-- Best regards, Igor Stasenko.
On Tue, Mar 25, 2014 at 10:21 AM, Eliot Miranda <eliot.miranda@gmail.com>wrote:
Hi Igor,
you have a point but I disagree. The scheduler is defined in the implementation section of the blue book. It could be more explicit, but the blue book scheduler is a known and simple system. In my threading work I've made sure to preserve its semantics (cooperative within priorities, preemptive across priorities, thread switch at activation of non-primitive sends and backward branches). The only serious bug I know of (that preempting sends a process to the back of its run-queue) was addressed in Cog and the VW VM. We can and should write up the semantics, but they're not undefined, they're simple and they do work.
Oops. I lie. There is a serious bug with Semaphore>>critical: and the like. There's a suspension point after signal and before block evaluation that can result in deadlock if a higher priority process signals the semaphore. Since the higher priority process is running the lower priority process never makes progress to run the ensure block or cvlear the flag or whatever it needs to do, and hence the system deadlocks. This requires thought ;-) So I do know of one outstanding bug.
Eliot (phone)
On Mar 25, 2014, at 10:11 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 25 March 2014 17:31, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Igor,
On Tue, Mar 25, 2014 at 5:05 AM, Igor Stasenko <siguctua@gmail.com>wrote:
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something".
Really? Surely any system as interactive as Smalltalk can benefit from a stop-the-rest-of-the-world scheduling facility, and surely packaging it as BlockClosure>>valueUnpreemptively would be a convenient way of doing so. Surely the right attitude for an implementor of a threading system for Smalltalk would be "Sure, I can implement that, even in a truly concurrent, multi-processor environment". It may take some doing but it's an important facility to have. It shouldn't be abused, but when you need it, you need it.
There should be hard guarantees from VM to do it. Right now there's none. That's my point. Like special primitive(s) for disabling interrupts/scheduling and enabling it back again. Let us be realistic: the above implementation is based on insider's knowledge how scheduling works, lacking any notion of contract between VM and image. Right now it just based on implementation detail rather than on guaranteed and well defined semantic.
It is no doubt, sometimes you may need such hammer to stop the world. And it is no doubt (to me) that one should avoid using it unless it is impossible to do otherwise.
-- best, Eliot
-- Best regards, Igor Stasenko.
-- best, Eliot
On 26 March 2014 15:04, Ben Coman <btc@openinworld.com> wrote:
Eliot Miranda wrote:
On Tue, Mar 25, 2014 at 10:21 AM, Eliot Miranda <eliot.miranda@gmail.com>wrote:
Hi Igor,
you have a point but I disagree. The scheduler is defined in the implementation section of the blue book. It could be more explicit, but the blue book scheduler is a known and simple system. In my threading work I've made sure to preserve its semantics (cooperative within priorities, preemptive across priorities, thread switch at activation of non-primitive sends and backward branches). The only serious bug I know of (that preempting sends a process to the back of its run-queue) was addressed in Cog and the VW VM. We can and should write up the semantics, but they're not undefined, they're simple and they do work.
Oops. I lie. There is a serious bug with Semaphore>>critical: and the like. There's a suspension point after signal and before block evaluation that can result in deadlock if a higher priority process signals the semaphore. Since the higher priority process is running the lower priority process never makes progress to run the ensure block or cvlear the flag or whatever it needs to do, and hence the system deadlocks. This requires thought ;-) So I do know of one outstanding bug.
My grasp of concurrency controls hasn't been tested in 20 years, so naively I would say: If a lower priority process "L" holds** a semaphore when the higher priority process "H" signals the semaphore, temporarily raise the priority of the "L", or temporarily lower the priority of "H". Is there something simple that I missing?
It really depends what you put into "priority" thing. In preemptive scheduling policy it means: threads with higher priority always preempt ones with lower one. That means that any low-priority task has no chances to advance, if there even single higher priority task that never stops. In shared-time scheduling that means: threads with higher priority has higher % of computing resources allocated (the exact amount/ratio can depend on policy). But shared-time scheduling good in another aspect: all tasks has chance to advance, if observed over some finite time frame, regardless of their priority. The semaphores and other synchronizing primitives is not really related to scheduling. They are orthogonal, as they just giving a control over yielding execution of task (basically halting it) until some condition met.
cheers -ben
-- Best regards, Igor Stasenko.
Hi Ben, On Wed, Mar 26, 2014 at 7:04 AM, Ben Coman <btc@openinworld.com> wrote:
Eliot Miranda wrote:
On Tue, Mar 25, 2014 at 10:21 AM, Eliot Miranda <eliot.miranda@gmail.com>wrote:
Hi Igor,
you have a point but I disagree. The scheduler is defined in the implementation section of the blue book. It could be more explicit, but the blue book scheduler is a known and simple system. In my threading work I've made sure to preserve its semantics (cooperative within priorities, preemptive across priorities, thread switch at activation of non-primitive sends and backward branches). The only serious bug I know of (that preempting sends a process to the back of its run-queue) was addressed in Cog and the VW VM. We can and should write up the semantics, but they're not undefined, they're simple and they do work.
Oops. I lie. There is a serious bug with Semaphore>>critical: and the like. There's a suspension point after signal and before block evaluation that can result in deadlock if a higher priority process signals the semaphore. Since the higher priority process is running the lower priority process never makes progress to run the ensure block or cvlear the flag or whatever it needs to do, and hence the system deadlocks. This requires thought ;-) So I do know of one outstanding bug.
My grasp of concurrency controls hasn't been tested in 20 years, so naively I would say: If a lower priority process "L" holds** a semaphore when the higher priority process "H" signals the semaphore, temporarily raise the priority of the "L", or temporarily lower the priority of "H". Is there something simple that I missing?
There are a couple of obvious solutions. One is to avoid the suspension point somehow, another is adding priority inversion. Adding priority inversion to the scheduler is a significant change which would increase complexity. You yourself have glossed over detail by saying "temporarily". I've glossed over a lot with "somehow". How would these "temporarily"s and "somehow"s be implemented? Your design sketches are appreciated. cc'ing vm-dev and squeak dev lists. this is an important discussion.
cheers -ben
Eliot (phone)
On Mar 25, 2014, at 10:11 AM, Igor Stasenko <siguctua@gmail.com> wrote:
On 25 March 2014 17:31, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Igor,
On Tue, Mar 25, 2014 at 5:05 AM, Igor Stasenko <siguctua@gmail.com>wrote:
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I haven't been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection new" can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isn't any 'multi-threading' like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! You're great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something".
Really? Surely any system as interactive as Smalltalk can benefit from a stop-the-rest-of-the-world scheduling facility, and surely packaging it as BlockClosure>>valueUnpreemptively would be a convenient way of doing so. Surely the right attitude for an implementor of a threading system for Smalltalk would be "Sure, I can implement that, even in a truly concurrent, multi-processor environment". It may take some doing but it's an important facility to have. It shouldn't be abused, but when you need it, you need it.
There should be hard guarantees from VM to do it. Right now there's none. That's my point. Like special primitive(s) for disabling interrupts/scheduling and enabling it back again. Let us be realistic: the above implementation is based on insider's knowledge how scheduling works, lacking any notion of contract between VM and image. Right now it just based on implementation detail rather than on guaranteed and well defined semantic.
It is no doubt, sometimes you may need such hammer to stop the world. And it is no doubt (to me) that one should avoid using it unless it is impossible to do otherwise.
-- best, Eliot
-- Best regards, Igor Stasenko.
-- best, Eliot
-- best, Eliot
On 25 March 2014 18:21, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Igor,
you have a point but I disagree. The scheduler is defined in the implementation section of the blue book. It could be more explicit, but the blue book scheduler is a known and simple system. In my threading work I've made sure to preserve its semantics (cooperative within priorities, preemptive across priorities, thread switch at activation of non-primitive sends and backward branches). The only serious bug I know of (that preempting sends a process to the back of its run-queue) was addressed in Cog and the VW VM. We can and should write up the semantics, but they're not undefined, they're simple and they do work.
And you remember that i managed to move all scheduling logic out of VM to image. That gives you much more freedom in defining how scheduling shall work, without need of implementing complex, unsafe and errorneus sematic which involves mind-blowing exercises trying to fill the gap between VM and image without having right tools. I don't argue that current scheduling implementation is quite simple and more or less OK. What I am not pleased with, is that it is the only available semantic which we would ever have. And producing and using code based on it , only makes it carved in stone & live forever.
Eliot (phone)
-- Best regards, Igor Stasenko.
Just to show what i talking about.. Credits to Queen: | str | str := 'Don''t stop me now.'. [ 2 seconds asDelay wait. str := str , '(oops , i did)' ] forkAt: Processor highestPriority. [ 4 seconds asDelay wait. str := str, 'I''m having such a good time I''m having a ball'] valueUnpreemptively. str On 25 March 2014 18:11, Igor Stasenko <siguctua@gmail.com> wrote:
On 25 March 2014 17:31, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Igor,
On Tue, Mar 25, 2014 at 5:05 AM, Igor Stasenko <siguctua@gmail.com>wrote:
On 24 March 2014 22:54, phil@highoctane.be <phil@highoctane.be> wrote:
On Mon, Mar 24, 2014 at 8:23 PM, Alexandre Bergel < alexandre.bergel@me.com> wrote:
I am working on a memory model for expandable collection in Pharo. Currently, OrderedCollection, Dictionary and other expandable collections use a internal array to store their data. My new collection library recycle these array instead of letting the garbage collector dispose them. I simply insert the arrays in an ordered collection when an array is not necessary anymore. And I remove one when I need one.
Hm, is that really going to be worth the trouble?
This technique reduces the consumption of about 15% of memory.
At the end, #add: and #remove: are performed on these polls of arrays. I havenât been able to spot any problem regarding concurrency and I made no effort in preventing them. I have a simple global collection and each call site of "OrderedCollection newâ can pick an element of my global collection.
I have the impression that I simply need to guard the access to the global poll, which is basically guarding #add: #remove: and #includes:
One of the AtomicCollections might be the right things for you?
I will have a look at it.
What is funny, is that I did not care at all about multi-threading and concurrency, and I have not spotted any problem so far.
There isnât any âmulti-threadingâ like in Java, you got a much more control version: cooperative on the same priority, preemptive between priorities. So, I am not surprised. And well, these operations are likely not to be problematic when they are racy, except when the underling data structure could get into an inconsistent state itself. The overall operations (adding/removing/searing) are racy on the application level anyway.
However, much more interesting would be to know what kind of benefit do you see for such reuse? And especially, with Spur around the corner, will it still pay off then? Or is it an application-specific optimization?
I am exploring a new design of the collection library of Pharo. Not all the (academic) ideas will be worth porting into the mainstream of Pharo. But some of them yes.
Thanks for all your help guys! Youâre great!
Cheers, Alexandre
-- _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;: Alexandre Bergel http://www.bergel.eu ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
An interesting method I stumbled upon which may help in understanding how these things do work.
BlockClosure>>valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result semaphore | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority].
I would not recommend you to use this method for anything. This method heavily relies on how process scheduler works, and in case of any changes, it may break everything. For the sake of programming, one shall never assume there is a way to "stop the world while i busy doing something".
Really? Surely any system as interactive as Smalltalk can benefit from a stop-the-rest-of-the-world scheduling facility, and surely packaging it as BlockClosure>>valueUnpreemptively would be a convenient way of doing so. Surely the right attitude for an implementor of a threading system for Smalltalk would be "Sure, I can implement that, even in a truly concurrent, multi-processor environment". It may take some doing but it's an important facility to have. It shouldn't be abused, but when you need it, you need it.
There should be hard guarantees from VM to do it. Right now there's none. That's my point. Like special primitive(s) for disabling interrupts/scheduling and enabling it back again. Let us be realistic: the above implementation is based on insider's knowledge how scheduling works, lacking any notion of contract between VM and image. Right now it just based on implementation detail rather than on guaranteed and well defined semantic.
It is no doubt, sometimes you may need such hammer to stop the world. And it is no doubt (to me) that one should avoid using it unless it is impossible to do otherwise.
-- best, Eliot
-- Best regards, Igor Stasenko.
-- Best regards, Igor Stasenko.
On 24/03/14 18:20, Alexandre Bergel wrote:
My current understanding about thread is that there is a scheduling that may occurs each time we enter the VM (e.g., primitive call, instantiating an object, throwing an exception). So, the code "anOrderedCollection add: 42â will _never_ suffer from concurrent call because adding 42 to a collection does not enter the VM. Does this make sense?
I don't think so. AFAIK interrupts are checked on message sends and on backward jumps (because of inlined loops just like in your example).
So context switch may happen on each send...
Ah yes!! I missed this case. Any idea what is the cost of using a semaphore? Inserting the expression "anOrderedCollection add: 42â in a semaphore surely make the expression slower. Any idea how much slower?
Not really sure, since #signal and #wait are primitives, but simple benchmark should do it :-) However, you may want to use recursion lock (sometimes called monitor - in Pharo class Monitor) which allows for recursion. Otherwise you may get a nice deadlock if your code recurs. Cost of recursion lock could be reduced to couple machine instructions (if there's no contention), if done properly. The implementation of Monitor in Pharo seems to be way, way more costly than few instructions. Jan
participants (9)
-
Alexandre Bergel -
Ben Coman -
btc@openinworld.com -
Eliot Miranda -
Guillermo Polito -
Igor Stasenko -
Jan Vrany -
phil@highoctane.be -
Stefan Marr