"Joshua Scholar" jscholar@access4less.net wrote: Changes Set>>do: from do: aBlock tally = 0 ifTrue: [^ self]. 1 to: array size do: [:index | | each | (each _ array at: index) ifNotNil: [aBlock value: each]] to do: aBlock tally = 0 ifTrue: [^ self]. array do: [:each | each ifNotNil: [aBlock value: each]]. There's one fairly major problem with Set>>do: which this change does nothing about: the fact that iterating over a Set of n elements is not O(n). This seems rather like polishing the brass on the Titanic.
The difference is that adding to a Set or Dictionary can put a new rehashed hashed hash table into "array" Yes, but it has always been strictly forbidden to make any kind of change to a Set or Dictionary while iterating over it. There are a number of ways to deal with this:
- Java actually maintains a change count and Set/Dictionary iterators watch out for changes, so If You Do Bad Things You Get Told
- AWK (at least the 'mawk' implementation with which I'm familiar) actually goes to the trouble of making a copy of the key set of an array when you do 'for (key in array) ...' so If You Do Bad Things It Works Like You Should Have Expected'.
- Smalltalk has traditionally gone a little bit crazy; If You Do Bad Things You Get What's Coming To You And Why Didn't You Read The Sources?
- Verification tools like ESC/M3, ESC/Java, SPARK, and so on verify at 'compile' time that You Didn't Do Anything Bad You Good Little Programmer You (it's just not what the customer wanted).
In the original version, if any block inside of a "do:" or any of the many loops that depend on "do:" called code that added to that Set then there was a chance the set would grow and rehash - and some element would be visited more than once and some not at all. Indeed. Break the contract and you lose your rights.
The way it is with this change, adding to a Set in a loop adds an element that might not be visited, and deleting an element doesn't necessarily mean the element won't be visited but the meltdown scenario can't happen. Add an element to a Set, and the following things can happen: (1) The element is already present; nothing bad happens. (2) The element is not already present and there is room in the array; (2a) the new element is inserted after the current iteration point=> it will be seen (2b) the new element is inserted before the current iteration point=> it will not be seen. We are already in Very Bad News territory. It's darned near impossible to give this a coherent abstract semantics. A "live view" should ensure that when you finish the loop you have seen _everything_ that is still in the set. The proposed change does not ensure that. A "snapshot" should ensure that when you finish the loop you have seen all and only the things that _were_ in the set at the start; the proposed change does not ensure that either. (3) The element is not already present and there is not enough room left (as defined by #fullCheck) The old code will switch over to the new array, but not scan all of it. The new code will continue scanning the old array, so will not see the addition or any other changes. This is at least as much of a "meltdown" as what the old code did, and is arguably worse, in that it will hold onto a dead data structure that ought to be GC-meat.
Remove an element from a Set, and the following things can happen: (1) The element is already absent; nothing bad happens. (2) The element is present. It's replaced by nil, then #fixCollisionsFrom: is called. This can make fairly large scale rearrangements of the array. As far as I can see, this can results in elements being missed or seen twice, WHICHEVER of the #do: versions is used.
In short, the only effective change is that the new Set>>do: will, in some rare circumstances, visit more elements because it is scanning the wrong array. I don't really understand why this is useful, when we STILL have the problem that additions may or may not be seen and removals can STILL scramble a #do:.
And of course the new version is at least as fast as the old one. I benchmarked it, and the new code is slower.
s := (1 to: 60000) asSet. [s do: [:each |]] timeToRun => 42 msec (old version) [s do2: [:each |]] timeToRun => 63 msec (new version)
These times with Squeak 3.6-5429-full, MacOS 10.3.3 on a 1GHz G4 PPC.
And of course the new version is at least as fast as the old one.
I benchmarked it, and the new code is slower.
s := (1 to: 60000) asSet. [s do: [:each |]] timeToRun => 42 msec (old version) [s do2: [:each |]] timeToRun => 63 msec (new version)
You're right, I made a mistake.
I had originally writen the method: do: aBlock | arrayHolder | tally = 0 ifTrue: [^ self]. arrayHolder_array. 1 to: arrayHolder size do: [:index | | each | (each _ arrayHolder at: index) ifNotNil: [aBlock value: each]].
Which shouldn't be a slow down - it just adds a local variable to the original - but then I noticed that the code in SequenceableCollection do: looked identical and thought using SquenceableCollection do: looked neater... But forgot that it has to jump though an extra block to get to the test.
Anyway since I already included a change set that some might consider a malicious slowdown, I've included a change set this time that has my arguably dubious improvement without slowing things down.
You know, it occures to me to wonder why common looping constructs near the bottom of hierachies haven't been turned into primitives. Faster looping constructs (a SmallInteger to: do:, Float to: by: do:, Interval to: by: do:, Array do:, ByteArray do: etc.) would significantly speed up some code.
Remove an element from a Set, and the following things can happen: (1) The element is already absent; nothing bad happens. (2) The element is present. It's replaced by nil, then
#fixCollisionsFrom:
is called. This can make fairly large scale rearrangements of the array. As far as I can see, this can results in elements being missed or seen twice, WHICHEVER of the #do: versions is used.
You're right. I forgot about fixCollisionFrom:. The new code is not much saner than the last code was, on deletes.
I've always prefered hash tables that use overflow list instead of scanning forward, because it's easier to tell if an object isn't in the table. And that looping code would be saner on deletes if we used overflow lists. Nevermind.
There's one fairly major problem with Set>>do: which this change does nothing about: the fact that iterating over a Set of n elements is not O(n). This seems rather like polishing the brass on the Titanic.
Yes. Talking about the memory leak caused by the fact that Sets grow but never shrink was a way of broaching a related subject.
Clearly if there was a limit to the ratio between unused elements and used ones then iterating would also have guaranteed time behavior... Not that I was thinking about this. I was worried about behavior, not time. You wouldn't use Smalltalk if you were generally more focused on execution speed than on having elegant programming tools.
Since Sets and Dictionaries and the like are usually added to and rarely removed from, it's hard to be sure that allowing these hash tables to shrink is really a win on average.
And the question I was wondering about mostely was whether putting a shrink in could break any of the many classes derived from Set. There ARE methods that depend on the internal structure of the Set and grow can be overridden etc. so other classes would probably have to be changed if Sets were allowed to shrink as well as grow.
Sigh.
(3) The element is not already present and there is not enough room left (as defined by #fullCheck) The old code will switch over to the new array, but not scan all of
it.
The new code will continue scanning the old array, so will not see the addition or any other changes. This is at least as much of a
"meltdown"
as what the old code did, and is arguably worse, in that it will hold onto a dead data structure that ought to be GC-meat.
I disagree that holding on to the the old version is a meltdown at all. The only non-messy semantics for do: (though too expensive) would be to make
aCollection do: [ :each|...
act like
aCollection copy do: [ :each |...
There's nothing messy about iterating over a snapshot of a table from a database. From the standpoint of functional programnming the second is much cleaner as well.
If you've worked on database engines (I have) , you know that there are lots of database modes each with different compromises about what data you'll see in a transaction and about exactly when "now" is. Databases are often run in less than theoretically perfect modes because perfectly atomic modes are too expensive.
So, for instance, a promise that you can add to a collection while adding to it, and see each of it's original members exactly once, but you might not see all of your new additions is one that's useable if only bearly. While the, it blows up and shows you some elements twice or more times and some elements never is completely unworkable.
As for whether it's wasting memory by holding on to the GC-meat until the end of the loop (I know you didn't say that), I would point out that Set collect: works exactly the way this code did, by invoking "array do:" As I said I don't think holding on to an object while it's being looped over is onerous.
Ultimately while this new code is saner for adding inside of a loop it's not easy to work with... So yes, it's not great.
But since (the updated) fix doesn't slow things down, and it does act much saner in a certain situation, I think it's a win.
Joshua Scholar
On Tue, 30 Nov 2004 08:30:52 -0800, "Joshua Scholar" jscholar@access4less.net wrote:
You know, it occures to me to wonder why common looping constructs near the bottom of hierachies haven't been turned into primitives. Faster looping constructs (a SmallInteger to: do:, Float to: by: do:, Interval to: by: do:, Array do:, ByteArray do: etc.) would significantly speed up some code.
VisualAge has done this. They have a generic collection iterator primitive, with which all the built-in collection enumeration methods are implemented.
Later, Jon
-------------------------------------------------------------- Jon Hylands Jon@huv.com http://www.huv.com/jon
Project: Micro Seeker (Micro Autonomous Underwater Vehicle) http://www.huv.com
jscholar@access4less.net wrote:
You know, it occures to me to wonder why common looping constructs near
the
bottom of hierachies haven't been turned into primitives. Faster looping constructs (a SmallInteger to: do:, Float to: by: do:, Interval to: by:
do:,
Array do:, ByteArray do: etc.) would significantly speed up some code.
Note that Number>>to:do: and Number>>to:by:do: are inlined by the Compiler. BlockContext>>whileTrue, BlockContext>>whileTrue: BlockContext>>whileFalse and BlockContext>>whileFalse: are also inlined. All these are used to define the collection iterators.
Inlining gives us a significant speedup, so the situation is not that bad.
Greetings, Boris
From: "Jon Hylands" jon@huv.com To: "Joshua Scholar" jscholar@access4less.net; "The general-purpose
Squeak developers list" squeak-dev@lists.squeakfoundation.org
Sent: Tuesday, November 30, 2004 9:03 AM Subject: Re: FIX: Making Set/Dictionary etc. loops more robust
On Tue, 30 Nov 2004 08:30:52 -0800, "Joshua Scholar" jscholar@access4less.net wrote:
You know, it occures to me to wonder why common looping constructs near
the
bottom of hierachies haven't been turned into primitives. Faster looping constructs (a SmallInteger to: do:, Float to: by: do:, Interval to: by:
do:,
Array do:, ByteArray do: etc.) would significantly speed up some code.
VisualAge has done this. They have a generic collection iterator
primitive,
with which all the built-in collection enumeration methods are
implemented.
Later, Jon
It would be fun to add primitives to speed up basic loops, but I'm confused about if, when and where the new closure compiler is going to come on line.
This might seem weird coming from someone like me who (if you look at my code) is obviously still learning the Smalltalk programming idioms... That atRandom: fix had an extra variable in it and an extra test because I knew about "whileTrue:" but not about "doWhileTrue:".
Anyway, is the new compiler going to be part of Squeak 4? (the VI4 (virtual image) page on Swiki hopes so, but the Squeak 4 pages don't say it will.
Is the rest of VI4 going to be in Squeak 4? Perhaps it's already in - the page is dated two years ago. Or was it abandoned?
How do I test the closure compiler? The page on Swiki says that the primitives went in to the VM back in 3.6.something, but someone complained that the new compiler was too slow on 3.7, and wondered if the primitives are in...
So, are the primitives in 3.7, 3.8 & 3.9?
So I'm not sure how to install the new compiler... Also, for the sake of speed testing, I'd like to be running the main classes recompiled with the new compiler - and I'm not sure what's the right way to rebuild the system if I did install the new compiler. I remember reading that EToys break if you recompile them with the new compiler - not that I'm using EToys.
It makes no sense to me to write looping primitive designed to work on a block structure that we are going to throw away. And I do prefer closures to what Squeak currently has.
Also I'm not conversant on exactly what problems primitives entail - my main question is what can't I do in a primitive for fear that a garbage collection will mess it up.
Another point is that some looping constructs might be better inlined than made into primitives. I have NO idea how to do that. Not that this should stop us from having an interim fix that's better than nothing...
Hey, that's a pretty good argument for making a loop primitive plug in for the current blocks. If the old system speeds up considerably because of a plug-in that will create pressure to make sure the new system has the same optimizations.
marcus made sure that the new compiler can produce old block style, so that we all do not lose 30% speed. And as soon as 3.8 is out the new compiler will be pushed into 3.9a
Stef
On 30 nov. 04, at 20:52, Joshua Scholar wrote:
From: "Jon Hylands" jon@huv.com To: "Joshua Scholar" jscholar@access4less.net; "The general-purpose
Squeak developers list" squeak-dev@lists.squeakfoundation.org
Sent: Tuesday, November 30, 2004 9:03 AM Subject: Re: FIX: Making Set/Dictionary etc. loops more robust
On Tue, 30 Nov 2004 08:30:52 -0800, "Joshua Scholar" jscholar@access4less.net wrote:
You know, it occures to me to wonder why common looping constructs near
the
bottom of hierachies haven't been turned into primitives. Faster looping constructs (a SmallInteger to: do:, Float to: by: do:, Interval to: by:
do:,
Array do:, ByteArray do: etc.) would significantly speed up some code.
VisualAge has done this. They have a generic collection iterator
primitive,
with which all the built-in collection enumeration methods are
implemented.
Later, Jon
It would be fun to add primitives to speed up basic loops, but I'm confused about if, when and where the new closure compiler is going to come on line.
This might seem weird coming from someone like me who (if you look at my code) is obviously still learning the Smalltalk programming idioms... That atRandom: fix had an extra variable in it and an extra test because I knew about "whileTrue:" but not about "doWhileTrue:".
Anyway, is the new compiler going to be part of Squeak 4? (the VI4 (virtual image) page on Swiki hopes so, but the Squeak 4 pages don't say it will.
Is the rest of VI4 going to be in Squeak 4? Perhaps it's already in - the page is dated two years ago. Or was it abandoned?
How do I test the closure compiler? The page on Swiki says that the primitives went in to the VM back in 3.6.something, but someone complained that the new compiler was too slow on 3.7, and wondered if the primitives are in...
So, are the primitives in 3.7, 3.8 & 3.9?
So I'm not sure how to install the new compiler... Also, for the sake of speed testing, I'd like to be running the main classes recompiled with the new compiler - and I'm not sure what's the right way to rebuild the system if I did install the new compiler. I remember reading that EToys break if you recompile them with the new compiler - not that I'm using EToys.
It makes no sense to me to write looping primitive designed to work on a block structure that we are going to throw away. And I do prefer closures to what Squeak currently has.
Also I'm not conversant on exactly what problems primitives entail - my main question is what can't I do in a primitive for fear that a garbage collection will mess it up.
Another point is that some looping constructs might be better inlined than made into primitives. I have NO idea how to do that. Not that this should stop us from having an interim fix that's better than nothing...
Hey, that's a pretty good argument for making a loop primitive plug in for the current blocks. If the old system speeds up considerably because of a plug-in that will create pressure to make sure the new system has the same optimizations.
I'm not exactly sure what we would expect primitives to speed up here.
General iteration loops are of the form from index =start to stop fetch a value from self according to index evaluate a block with that value return to top.
Evaluating the block will be done in the normal manner - it will still consist of bytecodes and so only the peripheral part can be 'optimized'. For really simple blocks it might possibly work out to be a small speed up since the indexing and value fetching will be a large part but for general blocks I'd expect the evaluation time to be the significant part.
Can anyone explain the VA primitive mentioned?
tim -- Tim Rowledge, tim@sumeru.stanford.edu, http://sumeru.stanford.edu/tim Useful random insult:- Went to the dentist to have his cranial cavity filled.
On Tue, 30 Nov 2004 12:32:36 -0800, Tim Rowledge tim@sumeru.stanford.edu wrote:
Can anyone explain the VA primitive mentioned?
Here's what the code looks like:
ArrayedCollection >> #do: aBlock "Iteratively evaluate the one argument block, aBlock using each element of the receiver, in order. Fail if aBlock is not a one argument block."
aBlock apply: self from: 1 to: self size.
--------------
BlockContext >> #apply: aCollection from: start to: end "Evaluate the receiver for each variable slot of aCollection from start to end. Answer aCollection."
<primitive: VMprBlockContextApplyFromTo> start to: end do: [:i | self value: (aCollection basicAt: i)]. ^aCollection
Later, Jon
-------------------------------------------------------------- Jon Hylands Jon@huv.com http://www.huv.com/jon
Project: Micro Seeker (Micro Autonomous Underwater Vehicle) http://www.huv.com
Jon Hylands jon@huv.com wrote:
On Tue, 30 Nov 2004 12:32:36 -0800, Tim Rowledge tim@sumeru.stanford.edu wrote:
Can anyone explain the VA primitive mentioned?
BlockContext >> #apply: aCollection from: start to: end "Evaluate the receiver for each variable slot of aCollection from start to end. Answer aCollection."
<primitive: VMprBlockContextApplyFromTo> start to: end do: [:i | self value: (aCollection basicAt: i)]. ^aCollection
Ah, I see. So I guess that OrderedCollection does it in two slices if the start/stop have wrapped around in stacklike usage. Fair enough but unless there is some hidden trickery it will only save the time in the to do: and basicAt: which isn't a lot. Hmm, I suppose one could use the prim to set some lock on the collection while one is iterating. Problematic if the block code modifies the collection.
tim -- Tim Rowledge, tim@sumeru.stanford.edu, http://sumeru.stanford.edu/tim Strange OpCodes: FSM: Fold, Spindle and Mutilate
----- Original Message ----- From: "Tim Rowledge" tim@sumeru.stanford.edu To: squeak-dev@lists.squeakfoundation.org Sent: Tuesday, November 30, 2004 4:28 PM Subject: Re: Adding loop primitives/optimizations (was Making Set/Dictionaryetc. loops more robust)
Jon Hylands jon@huv.com wrote:
On Tue, 30 Nov 2004 12:32:36 -0800, Tim Rowledge
wrote:
Can anyone explain the VA primitive mentioned?
BlockContext >> #apply: aCollection from: start to: end "Evaluate the receiver for each variable slot of aCollection from start to end. Answer aCollection."
<primitive: VMprBlockContextApplyFromTo> start to: end do: [:i | self value: (aCollection basicAt: i)]. ^aCollection
Ah, I see. So I guess that OrderedCollection does it in two slices if the start/stop have wrapped around in stacklike usage. Fair enough but unless there is some hidden trickery it will only save the time in the to do: and basicAt: which isn't a lot. Hmm, I suppose one could use the prim to set some lock on the collection while one is iterating. Problematic if the block code modifies the collection.
tim
Since I was only talking about iterating over primitive types like arrays, it's not possible to change the size of the collection while iterating over it. And changing the value of individual elements wouldn't be a problem.
Joshua Scholar
"Joshua Scholar" jscholar@access4less.net wrote:
Since I was only talking about iterating over primitive types like arrays, it's not possible to change the size of the collection while iterating over it.
Really? Consider what happens in this:-
|array newArray| array := Array new:10. 1 to: array size do:[:i| array at: i put: i squared]. "ok" array do:[:el| newArray := Array new: el +3. array become: newArray]
Warning - this code might cause brain damage. Never underestimate the excitment #become: can cause, nor how often some demented ninny will spring it upon you.
A more prosaic example might be a stream on an Array. You can add extra elements to the array which in the Squeak implementation replaces the array with a bigger one BUT IIRC older Smalltalks used a become to do that grow. Now add in multiple threads and life can get very complicated.
tim -- Tim Rowledge, tim@sumeru.stanford.edu, http://sumeru.stanford.edu/tim Useful random insult:- Talks to plants on their own level.
Your example would NOT see the new elements because SequenceableCollection>>do: is implemented as:
do: aBlock "Refer to the comment in Collection|do:." 1 to: self size do: [:index | aBlock value: (self at: index)]
See, end of the array was computed before the loop started.
If you shortened the array, you would have an error pop up.
"become:" affecting an active iteration is wrong. Iterations can only be clean if they're over a snapshot not over a live collection anyway. Ok, we can't afford pure cleanness but there's no point in pushing the language toward an idiom that can't even be defined without contradictions.
What if "become:" changes the type of array to a different type?
You could argue that an array of a different size IS a different type. It is in many languages.
No, THERE IS NOTHING WRONG locking down the array and iterating over it. If the code calls "become:" then continuting the iteration over the old array is no worse than trying to continue a loop over the new (I would say "wrong") object.
In fact, finishing the iteration with the object you started with _is_ _cleaner_.
If you don't agree with me on that, then maybe you'll decide that _it_ _doesn't_ matter_ _because_ _code_ _shouldn't_ _call_ _become_ _inside_ _of_ _and_ _iterator_ _on_ _that_ _object_.
Whew!
Joshua Scholar ----- Original Message ----- From: "Tim Rowledge" tim@sumeru.stanford.edu To: jscholar@access4less.net; squeak-dev@lists.squeakfoundation.org Sent: Wednesday, December 01, 2004 11:46 AM Subject: Re: Adding loop primitives/optimizations (was Making Set/Dictionaryetc. loops more robust)
"Joshua Scholar" jscholar@access4less.net wrote:
Since I was only talking about iterating over primitive types like
arrays,
it's not possible to change the size of the collection while iterating
over
it.
Really? Consider what happens in this:-
|array newArray| array := Array new:10. 1 to: array size do:[:i| array at: i put: i squared]. "ok" array do:[:el| newArray := Array new: el +3. array become: newArray]
Warning - this code might cause brain damage. Never underestimate the excitment #become: can cause, nor how often some demented ninny will spring it upon you.
A more prosaic example might be a stream on an Array. You can add extra elements to the array which in the Squeak implementation replaces the array with a bigger one BUT IIRC older Smalltalks used a become to do that grow. Now add in multiple threads and life can get very complicated.
tim
Tim Rowledge, tim@sumeru.stanford.edu, http://sumeru.stanford.edu/tim Useful random insult:- Talks to plants on their own level.
It's more interesting to look at the bytecodes, not that the compiler/vm might do something clever and ignore the flow. Still some languages might send size to the array on each check of the do loop just in case things did change. However here as you point out size is only sent once.
5 <70> self 6 <C2> send: size 7 <6A> popIntoTemp: 2 8 <76> pushConstant: 1 9 <69> popIntoTemp: 1 10 <11> pushTemp: 1 11 <12> pushTemp: 2 12 <B4> send: <= 13 <AC 0C> jumpFalse: 27 15 <10> pushTemp: 0 16 <70> self 17 <11> pushTemp: 1 18 <C0> send: at: 19 <CA> send: value: 20 <87> pop 21 <11> pushTemp: 1 22 <76> pushConstant: 1 23 <B0> send: + 24 <69> popIntoTemp: 1 25 <A3 EF> jumpTo: 10 27 <78> returnSelf
Something Andreas told me years ago, you need to do lots of clever things per second, and each one needs to have significant gains in order to see any real world improvement. Even if you improved array iteration speed by 50%, would anything show in the macro benchmarks? This becomes more of an issue with the highly threaded CPUs of today, for example you could remove an entire smalltalk bytecode from the loop above and actually see no improvement in performance.
On Dec 1, 2004, at 12:10 PM, Joshua Scholar wrote:
Your example would NOT see the new elements because SequenceableCollection>>do: is implemented as:
do: aBlock "Refer to the comment in Collection|do:." 1 to: self size do: [:index | aBlock value: (self at: index)]
See, end of the array was computed before the loop started.
If you shortened the array, you would have an error pop up.
"become:" affecting an active iteration is wrong. Iterations can only be clean if they're over a snapshot not over a live collection anyway. Ok, we can't afford pure cleanness but there's no point in pushing the language toward an idiom that can't even be defined without contradictions.
What if "become:" changes the type of array to a different type?
You could argue that an array of a different size IS a different type. It is in many languages.
No, THERE IS NOTHING WRONG locking down the array and iterating over it. If the code calls "become:" then continuting the iteration over the old array is no worse than trying to continue a loop over the new (I would say "wrong") object.
In fact, finishing the iteration with the object you started with _is_ _cleaner_.
If you don't agree with me on that, then maybe you'll decide that _it_ _doesn't_ matter_ _because_ _code_ _shouldn't_ _call_ _become_ _inside_ _of_ _and_ _iterator_ _on_ _that_ _object_.
Whew!
Joshua Scholar ----- Original Message ----- From: "Tim Rowledge" tim@sumeru.stanford.edu To: jscholar@access4less.net; squeak-dev@lists.squeakfoundation.org Sent: Wednesday, December 01, 2004 11:46 AM Subject: Re: Adding loop primitives/optimizations (was Making Set/Dictionaryetc. loops more robust)
"Joshua Scholar" jscholar@access4less.net wrote:
Since I was only talking about iterating over primitive types like
arrays,
it's not possible to change the size of the collection while iterating
over
it.
Really? Consider what happens in this:-
|array newArray| array := Array new:10. 1 to: array size do:[:i| array at: i put: i squared]. "ok" array do:[:el| newArray := Array new: el +3. array become: newArray]
Warning - this code might cause brain damage. Never underestimate the excitment #become: can cause, nor how often some demented ninny will spring it upon you.
A more prosaic example might be a stream on an Array. You can add extra elements to the array which in the Squeak implementation replaces the array with a bigger one BUT IIRC older Smalltalks used a become to do that grow. Now add in multiple threads and life can get very complicated.
tim
Tim Rowledge, tim@sumeru.stanford.edu, http://sumeru.stanford.edu/tim Useful random insult:- Talks to plants on their own level.
-- ======================================================================== === John M. McIntosh johnmci@smalltalkconsulting.com 1-800-477-2659 Corporate Smalltalk Consulting Ltd. http://www.smalltalkconsulting.com ======================================================================== ===
----- Original Message ----- From: "John M McIntosh" johnmci@smalltalkconsulting.com To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org; "Joshua Scholar" jscholar@access4less.net Sent: Wednesday, December 01, 2004 12:36 PM Subject: Re: Adding loop primitives/optimizations (was Making Set/Dictionaryetc. loops more robust)
It's more interesting to look at the bytecodes, not that the compiler/vm might do something clever and ignore the flow. Still some languages might send size to the array on each check of the do loop just in case things did change. However here as you point out size is only sent once.
5 <70> self 6 <C2> send: size 7 <6A> popIntoTemp: 2 8 <76> pushConstant: 1 9 <69> popIntoTemp: 1 10 <11> pushTemp: 1 11 <12> pushTemp: 2 12 <B4> send: <= 13 <AC 0C> jumpFalse: 27 15 <10> pushTemp: 0 16 <70> self 17 <11> pushTemp: 1 18 <C0> send: at: 19 <CA> send: value: 20 <87> pop 21 <11> pushTemp: 1 22 <76> pushConstant: 1 23 <B0> send: + 24 <69> popIntoTemp: 1 25 <A3 EF> jumpTo: 10 27 <78> returnSelf
Something Andreas told me years ago, you need to do lots of clever things per second, and each one needs to have significant gains in order to see any real world improvement. Even if you improved array iteration speed by 50%, would anything show in the macro benchmarks? This becomes more of an issue with the highly threaded CPUs of today, for example you could remove an entire smalltalk bytecode from the loop above and actually see no improvement in performance.
Well, I'm used to a sort of programming where the speed of loops is paramount. In DSP programming, sound and graphics processing, an extra instruction or two in a loop can mean a massive speed difference to a program.
Ok, maybe Smalltalk isn't so fast at number crunching anyway, but still the speed of simple loops can make a massive difference in my experience. Whenever you have a lot of data to loop over and simple processing to do on that data, the speed of the loop itself is very important.
I was thinking about iteration and closures the other day. Smalltalk blocks are becoming the equivalent of lambdas, but in Scheme, the block inside of a loop does not have to be a lambda, and can use the outer environment I think... So when we slow our loops down in order to create a new environment for each iteration, we're doing something that Scheme and Lisp didn't I think. I'll have to write some Scheme programs to be sure.
Anyway there may be some optimization for iteration there, once we've gone to block closures. Perhaps a block environment can have a flag that mentions whether it has ever become visible, and the loop code will create new environment only if the old one was visible.
On a similar note to the original topic (loops that crunch), one of my long term goals is to learn Squeak well enough to implement a sublanguge or tools that makes Squeak a suitable environment for Sound processing / DSP development. I want to compile to Slang - though there may be some problem with garbage collecting and calls out of Slang code into byte interpreted code... Is it possible to unlink/relink Slang generated C code at run time? In other words can I make Slang compilation transparent? Basically I want some tools to convert a more abstract language into Slang and to hide all of that plug-in linking stuff.
Joshua Scholar
On Dec 1, 2004, at 1:22 PM, Joshua Scholar wrote:
Well, I'm used to a sort of programming where the speed of loops is paramount. In DSP programming, sound and graphics processing, an extra instruction or two in a loop can mean a massive speed difference to a program.
When you are talking this type of stuff, then really you are talking about doing a plugin. A few years back I and others looked at Altivec stuff for Squeak. What I found was that well we do have a set of primtives to do vector math already. If the data is in Arrays we can make the prim call and say add two vectors together. The problem was that adding a million elements takes just a few milliseconds, but the overhead to setup the prim call and get at the data took many ms. So changing the cost from 2 milliseconds to less than 1 millisecond didn't make any difference to the bottom line since it took 10 ms to get us to where we could do the math.
Ok, maybe Smalltalk isn't so fast at number crunching anyway, but still the speed of simple loops can make a massive difference in my experience. Whenever you have a lot of data to loop over and simple processing to do on that data, the speed of the loop itself is very important.
I was thinking about iteration and closures the other day. Smalltalk blocks are becoming the equivalent of lambdas, but in Scheme, the block inside of a loop does not have to be a lambda, and can use the outer environment I think... So when we slow our loops down in order to create a new environment for each iteration, we're doing something that Scheme and Lisp didn't I think. I'll have to write some Scheme programs to be sure.
Anyway there may be some optimization for iteration there, once we've gone to block closures. Perhaps a block environment can have a flag that mentions whether it has ever become visible, and the loop code will create new environment only if the old one was visible.
Well the question really is do we have fully closed, partial closed or full context blocks where accessing information outside the block is expensive, and or as you notice sending an "value:" is expensive. VW for example can make fully closed blocks and drag temps making them local into it, versus having to refer to them outside the current context.
On a similar note to the original topic (loops that crunch), one of my long term goals is to learn Squeak well enough to implement a sublanguge or tools that makes Squeak a suitable environment for Sound processing / DSP development. I want to compile to Slang - though there may be some problem with garbage collecting and calls out of Slang code into byte interpreted code... Is it possible to unlink/relink Slang generated C code at run time? In other words can I make Slang compilation transparent? Basically I want some tools to convert a more abstract language into Slang and to hide all of that plug-in linking stuff.
Technically you could make a shell call to compile SLANG generated code, unload an existing primitive and reload the primitive. There is VM support for doing the unload/load part, but I'm not sure if it really works correctly? Somehow the fun of creating a bad chunk of slang code that you compile and attemp to run(crash) as you are fiddle with your development environment might be interesting. Don't forget it's easy to corrupt Oops memory... Still tho if you are assembling components to produce a plugin call, that could be quite safe & feasible.
Joshua Scholar
-- ======================================================================== === John M. McIntosh johnmci@smalltalkconsulting.com 1-800-477-2659 Corporate Smalltalk Consulting Ltd. http://www.smalltalkconsulting.com ======================================================================== ===
----- Original Message ----- From: "John M McIntosh" johnmci@smalltalkconsulting.com To: "The general-purpose Squeak developers list" squeak-dev@lists.squeakfoundation.org; "Joshua Scholar" jscholar@access4less.net Sent: Wednesday, December 01, 2004 1:47 PM Subject: Re: Adding loop primitives/optimizations (was Making Set/Dictionaryetc. loops more robust)
On Dec 1, 2004, at 1:22 PM, Joshua Scholar wrote:
Well, I'm used to a sort of programming where the speed of loops is paramount. In DSP programming, sound and graphics processing, an extra instruction or two in a loop can mean a massive speed difference to a program.
When you are talking this type of stuff, then really you are talking about doing a plugin. A few years back I and others looked at Altivec stuff for Squeak. What I found was that well we do have a set of primtives to do vector math already. If the data is in Arrays we can make the prim call and say add two vectors together. The problem was that adding a million elements takes just a few milliseconds, but the overhead to setup the prim call and get at the data took many ms. So changing the cost from 2 milliseconds to less than 1 millisecond didn't make any difference to the bottom line since it took 10 ms to get us to where we could do the math.
But now that we have FloatArray, IntegerArray, WordArray and ByteArray getting data into a useable state is free right? It will already be useable, right?
I'll respond to the rest later. Gotta go to lunch.
Bye, Joshua Scholar
John,
When you are talking this type of stuff, then really you are talking about doing a plugin. A few years back I and others looked at Altivec stuff for Squeak. What I found was that well we do have a set of primtives to do vector math already. If the data is in Arrays we can make the prim call and say add two vectors together. The problem was that adding a million elements takes just a few milliseconds, but the overhead to setup the prim call and get at the data took many ms. So changing the cost from 2 milliseconds to less than 1 millisecond didn't make any difference to the bottom line since it took 10 ms to get us to where we could do the math.
This "10ms" number doesn't agree with my experiences. On my computer,
f _ FloatArray new: 1. [1000000 timesRepeat: [f+=f]] timeToRun.
"=> 1094"
and
f _ FloatArray new: 1000. [1000000 timesRepeat: [f+=f]] timeToRun. "=> 3730"
or even
f _ FloatArray new: 10000. [1000000 timesRepeat: [f+=f]] timeToRun. "=> 31595"
So, basically, the primitive callout time isn't that terrible and it makes some sense to try to optimize the array arithmetic. In Kedama, the plugin supports a bit more array arithmetic primitives, and they definitely gives performance boost. In typical example, if we can cut the primitive execution time occupies 70% or so of total execution time and we can still imagine to cut the primitive execution time in half or so.
-- Yoshiki
By the way, this version:
f _ FloatArray new: 1. [10000 timesRepeat: [ f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. f+=f. ]] timeToRun "=> 830"
So, the loop overhead is somwhat in the same ballpark. (less than factor of 10.)
On Dec 1, 2004, at 9:47 PM, Yoshiki Ohshima wrote:
John,
the bottom line since it took 10 ms to get us to where we could do the math.
This "10ms" number doesn't agree with my experiences. On my computer,
One should never pull numbers from hats, unless one is doing magic tricks.
The machine I was testing on was way slower, and the algorithm in question had lots more bytecodes that the before getting to the vector math, thus that overhead outweighed the actual performance improvements in the array primitive. I believe we've also made the plugin call path more optimal, we'd wander off and look at the millisecond clock twice for each call.
That said, your usage should require looking back at the issue.
Have you tried using Apple's vector libraries? They resolve to Altivec or something depending on the platform they run on,. Over the weekend I'll take a peek and generate some uptodate numbers based on some of your simple examples here.
http://developer.apple.com/hardware/ve/vector_libraries.html
-- ======================================================================== === John M. McIntosh johnmci@smalltalkconsulting.com 1-800-477-2659 Corporate Smalltalk Consulting Ltd. http://www.smalltalkconsulting.com ======================================================================== ===
On Dec 1, 2004, at 10:44 PM, John M McIntosh wrote:
Over the weekend I'll take a peek and generate some uptodate numbers based on some of your simple examples here.
http://developer.apple.com/hardware/ve/vector_libraries.html
A quick check with f := FloatArray new: 1000000 withAll: 1.0 . g := FloatArray new: 1000000 withAll: 2.0 . [100 timesRepeat: [f+=g]] timeToRun.
using vadd(rcvrPtr,1,argPtr,1,rcvrPtr,1,length); //Is this legal?
takes about 1765ms, versus 3802ms
-- ======================================================================== === John M. McIntosh johnmci@smalltalkconsulting.com 1-800-477-2659 Corporate Smalltalk Consulting Ltd. http://www.smalltalkconsulting.com ======================================================================== ===
John,
A quick check with f := FloatArray new: 1000000 withAll: 1.0 . g := FloatArray new: 1000000 withAll: 2.0 . [100 timesRepeat: [f+=g]] timeToRun.
using vadd(rcvrPtr,1,argPtr,1,rcvrPtr,1,length); //Is this legal?
takes about 1765ms, versus 3802ms
Looks good. If you are willing to hack the KedamaPlugin, replace a few vector ops with altivec, and run some simple Kedama example, it should give you some noticable difference, (I hope.)
Thank you!
-- Yoshiki
Am 02.12.2004 um 08:57 schrieb John M McIntosh:
On Dec 1, 2004, at 10:44 PM, John M McIntosh wrote:
Over the weekend I'll take a peek and generate some uptodate numbers based on some of your simple examples here.
http://developer.apple.com/hardware/ve/vector_libraries.html
A quick check with f := FloatArray new: 1000000 withAll: 1.0 . g := FloatArray new: 1000000 withAll: 2.0 . [100 timesRepeat: [f+=g]] timeToRun.
using vadd(rcvrPtr,1,argPtr,1,rcvrPtr,1,length); //Is this legal?
takes about 1765ms, versus 3802ms
Has anybody looked into gcc's vector support? IIUC this is in 3.4, if not even earlier, and there will be auto-vecorization in 4.0 (the version following 3.4).
http://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
http://gcc.gnu.org/projects/tree-ssa/vectorization.html
- Bert -
In message 034b01c4d7e1$e00d7bd0$3fe22641@antssoftware.com "Joshua Scholar" jscholar@access4less.net wrote:
Your example would NOT see the new elements because SequenceableCollection>>do: is implemented as:
do: aBlock "Refer to the comment in Collection|do:." 1 to: self size do: [:index | aBlock value: (self at: index)]
See, end of the array was computed before the loop started.
If you shortened the array, you would have an error pop up.
Ah, run the code and see what fun it causes. I guarantee you'll see new elements, though they may not be ones you expect. Welcome to... The Become Zone!
"become:" affecting an active iteration is wrong. Iterations can only be clean if they're over a snapshot not over a live collection anyway. Ok, we can't afford pure cleanness but there's no point in pushing the language toward an idiom that can't even be defined without contradictions.
I'm not championing anything here except realism about how much mess one can trivially (and usually unexpectedly) get into when trying to think of optimisations like this. As you say, to be 'safe' one has to somehow lock the collection but I'm not convinced there is actually a real way to do that. And the performance hit could be appalling.
What if "become:" changes the type of array to a different type?
You could argue that an array of a different size IS a different type. It is in many languages.
No, THERE IS NOTHING WRONG locking down the array and iterating over it. If the code calls "become:" then continuting the iteration over the old array is no worse than trying to continue a loop over the new (I would say "wrong") object.
Err, using become: makes the new array into the original one - that's the whole point of become:, after all.
In fact, finishing the iteration with the object you started with _is_ _cleaner_.
If you don't agree with me on that, then maybe you'll decide that _it_ _doesn't_ matter_ _because_ _code_ _shouldn't_ _call_ _become_ _inside_ _of_ _and_ _iterator_ _on_ _that_ _object_.
Well of course I agree with that - but you cannot enforce it in any way that I can think of that would be practical. You cannot restrict the effects of code called during the iteration, nor code running in other threads that might run during the iteration.
I've only been doing this thinking about optimising Smalltalk for a few years so I could be wrong of course. I'm sure there are at least a couple of people alive that have been doing it for longer.
tim -- Tim Rowledge, tim@sumeru.stanford.edu, http://sumeru.stanford.edu/tim Strange OpCodes: FSRA: Forms Skip and Run-Away
Tim, you're apparently not reading my posts very carefully because everything you're responding to are things I specifically dealt with.
I didn't say that collections should be copied before iterating over them. I specifically said that such copies are too expensive.
I didn't say that collections should be locked while iterating over them, I only said that it doesn't make sense to expect iterators to change their behavior mid-stream if you try to mutate collection out from under them - that this was too much to ask, just like implicit copying was too much to ask.
I didn't say that I wanted arrays locked in a specific memory location while iterating over them, I specifically said that allowing the GC to move them was not onerous as long as there's some way for Slang code to keep track of where the array is.
As for being lectured about how I don't know enough about garbage collectors. Well that would be true if I had said any of what you assumed I was saying... Actually I know enough about GCs.
Is my writing that hard to understand?
I'd respond to what you said about become: inside of iterators, but I'm not getting the impression that you read what I said about become: any more carefully than you read what I said about iterator primitives. You certainly didn't respond to a single idea I stated...
Ah well, I try anyway: Here's a new idea about "become:" "become:" has such drastic effects that no method can run properly if it's owner is changed by "become:" unless it was designed or vetted for that specific change. No iterator in Squeak was designed to handle it's object being changed out from under it - so they will exhibit various bugs - bugs that would be different if trivial internals of those method were changed slightly - arbitrary bugs. Your example tells me nothing about become: becaue it isn't proper useage of become:.
As for multitasking examples - that's something else a program has to be specifically designed to handle in all of it's parts... Once again, the iterators weren't designed to have their objects changed out from under them by multithreaded programs - so they don't handle it in any reasonable way, and will exhibit bugs.
If you want to propose redesigns of every iterator to handle these things go ahead. BUT IT'S NOT A FLAW IN MY PROPOSAL THAT IT DOESN'T MAKE THE CODE HANDLE CASES THAT IT ALREADY CAN'T HANDLE.
Joshua Scholar
Sigh. This is getting very silly. One very last time and then I need to get on with work.
In message 049a01c4d821$13f7bde0$3fe22641@antssoftware.com "Joshua Scholar" jscholar@access4less.net wrote:
I didn't say that collections should be copied before iterating over them.
Nor I. I _did_ point out that it had been mentioned - I think perhaps by Richard O'Keefe as an example of some other langauge's way?
I specifically said that such copies are too expensive.
Me too. And, as it happens, incorrect as a way of solving the problem.
I didn't say that collections should be locked while iterating over them, I only said that it doesn't make sense to expect iterators to change their behavior mid-stream if you try to mutate collection out from under them - that this was too much to ask, just like implicit copying was too much to ask.
You said "become: affecting an active iteration is wrong. Iterations can only be clean if they're over a snapshot not over a live collection anyway." which seems like requiring a copy of some sort to me. It isn't so much that become: affecting things is wrong so much as it just plain _does_ affect them.
You also said "Since I was only talking about iterating over primitive types like arrays, it's not possible to change the size of the collection while iterating over it." which is clearly not correct in the face of capabilities like become:. We also don't have 'primitive types' in any sense that could correspond to anything much but perhaps this was just verbal sugar.
#become: _does_ affect the object in an active context, whether you like it or not. It can't avoid it - you send one object the message #become: with an argument of some other object and the identities are swapped including in the methodcontext in which you are running to send that message. Can it be confusing? You bet your sweet bippy. Can it be fatal? Damn right - amongst other things it can cause the very next bytecode to blow the VM away since and compiler generated offsets into the receiver it thouhgt it had can be wrong. Can it work 'correctly'? Sure - how do you think we mutate instances of classes that have their shape changed?
Is my writing that hard to understand?
It would appear so. I'm not generally considered particularly stupid but perhaps senility has suddenly crept up on me.
Ah well, I try anyway:
[snip] Thank you for a nice restating of exactly what I've been saying. Which is almost the same as most of what you have been saying. By the way, did you come here for the five minute argument or the full half-hour?
Your example tells me nothing about become: becaue it isn't proper useage of become:.
It doesn't matter whether that particular example is considered 'proper' by some standard, it simply matters that it is perfectly plausible Smalltalk that represents a class of things that can and do happen in real systems.
If you want to propose redesigns of every iterator to handle these things go ahead. BUT IT'S NOT A FLAW IN MY PROPOSAL THAT IT DOESN'T MAKE THE CODE HANDLE CASES THAT IT ALREADY CAN'T HANDLE.
Good grief man, get a grip and stop shouting. I haven't proposed changing a single thing about iterating, nor do I care to. I happen to quite like it as it is.
My sole original comments on the idea of some primitive relating to iterating were a) that it wouldn't provide much of a speedup, if any in actual practise, since the meat of the work is the code in the iterator block. A primitive will not affect said block. b) I further mused that to use such an iterator primitive for something like an OrderedCollection would require possibly two passes if the start/stop indices had wrapped at all. c) Finally I mused that maybe someone might think it smart to somehow lock the collection as part of the prim in order to (they think) prevent any of the problems inherent in code that modifies the collection and dismissed it as inadequate.
tim -- Tim Rowledge, tim@sumeru.stanford.edu, http://sumeru.stanford.edu/tim Strange OpCodes: GLV: Ground the Line Voltage
Hi Joshua!
I will try to answer a few questions.
"Joshua Scholar" jscholar@access4less.net wrote:
It would be fun to add primitives to speed up basic loops, but I'm confused about if, when and where the new closure compiler is going to come on line.
The new compiler is planned to enter 3.9alpha as soon as 3.8 goes final or something similar. Marcus Denker is holding that ball and can answer details.
This might seem weird coming from someone like me who (if you look at my code) is obviously still learning the Smalltalk programming idioms... That atRandom: fix had an extra variable in it and an extra test because I knew about "whileTrue:" but not about "doWhileTrue:".
Anyway, is the new compiler going to be part of Squeak 4? (the VI4 (virtual image) page on Swiki hopes so, but the Squeak 4 pages don't say it will.
I think that page is less updated
Is the rest of VI4 going to be in Squeak 4? Perhaps it's already in - the page is dated two years ago. Or was it abandoned?
How do I test the closure compiler? The page on Swiki says that the primitives went in to the VM back in 3.6.something, but someone complained that the new compiler was too slow on 3.7, and wondered if the primitives are in...
So, are the primitives in 3.7, 3.8 & 3.9?
Not sure, Marcus probably knows.
So I'm not sure how to install the new compiler... Also, for the sake of speed testing, I'd like to be running the main classes recompiled with the new compiler - and I'm not sure what's the right way to rebuild the system if I did install the new compiler. I remember reading that EToys break if you recompile them with the new compiler - not that I'm using EToys.
It makes no sense to me to write looping primitive designed to work on a block structure that we are going to throw away. And I do prefer closures to what Squeak currently has.
Also I'm not conversant on exactly what problems primitives entail - my main question is what can't I do in a primitive for fear that a garbage collection will mess it up.
Well, as people has explained - you can do whatever you like in a plugin/primitive but you need to be careful whenever you create/allocate Squeak-stuff. The push/pop remappableOop thingy is your friend but it can get tricksy. I have some code in my GtkPlugin experiment which looks... hideous, and it took some time to get it right. :)
Let me just finish by saying that IMHO if you want to go for SPEEEED then the most promising thing in Squeak country is Exupery by Bryce Kampjes. That is where I would put my money. Trying to speed up the regular VM is hard work and AFAICT it is hard to find cheap solutions.
Anthony claimed impressive speedups with his new stuff (Closures etc) - so hopefully all that will go in - but what has been seen earlier from the JIT-experiments by Ian is that only approximately half the time is spent in the interpreter when you run real world stuff. IIRC Jitter-3 was 4-5 times faster at interpreting but when running the benchmarks it went down to less than 2 times faster, because in the real world Squeak spends a lot of time in primitives. 2 times is a lot of course, but... for that amount of work (writing a full Jitter) it isn't much.
That is why I am more interested in Exupery which has quite impressive numbers to show and promises to become an alternative to C-plugins, and perhaps more.
regards, Göran
Am 02.12.2004 um 09:31 schrieb goran.krampe@bluefish.se:
Hi Joshua!
I will try to answer a few questions.
"Joshua Scholar" jscholar@access4less.net wrote:
It would be fun to add primitives to speed up basic loops, but I'm confused about if, when and where the new closure compiler is going to come on line.
The new compiler is planned to enter 3.9alpha as soon as 3.8 goes final or something similar. Marcus Denker is holding that ball and can answer details.
The plan is to add the new compiler to 3.9 as an option. I changed the closurecompiler to optionally emit old syle bytecode, so we can dumb the old compiler at some point even if we don't want to have closures by default.
Is the rest of VI4 going to be in Squeak 4? Perhaps it's already in
- the
page is dated two years ago. Or was it abandoned?
Squeak 4 is a chimera. We used to call it "Squeak 3" up to the point were the change file got to big and we made a V3. So my guess is that "Squeak4" will be renamed to "Squeak5" sometime next year. Don't hold your breath.
How do I test the closure compiler? The page on Swiki says that the primitives went in to the VM back in 3.6.something, but someone complained that the new compiler was too slow on 3.7, and wondered if the primitives are in...
Testing the closurecompiler: Just install (in 3.8image): Package Compiler, then package "closure-compiler". This comes with tests. There is a Preference to turn compiling with closures on and off. This will be slow untill the new 3.8 vm is released, and even after that full closure activation will be slower then the old blocks. Possibilties for some optimiziations are there e.g. I'd like to try to compile old style blocks in the cases where there are no captered vars...
So, are the primitives in 3.7, 3.8 & 3.9?
Not sure, Marcus probably knows
The primitives for an early version of the closureCompiler have been in 3.6/3.7. But the current version needs a new one, this will be in the 3.8 vm.
Anthony claimed impressive speedups with his new stuff (Closures etc) - so hopefully all that will go in -
No. The stack layout/new bytecode stuff is not part of the closurecompiler and has been discontinued, as far as I know.
Marcus
Eeek, I think I made a real hash :) out of a sentence near the end there.
"So, for instance, a promise that you can add to a collection while adding to it.."
Should read
"So, for instance, a promise that you can add to a collection while iterating over it..."
squeak-dev@lists.squeakfoundation.org