Preallocation behavior

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

Preallocation behavior

jannik laval
Hi all,

I am playing with MessageTally, and I have a strange result with prealocation.
Here is my example. I am working on a PharoCore1.3, with a VM4.2.5

First of all, I spy this source code:
====
MessageTally spyOn: 
     [ 500 timesRepeat: [
                     | str |  
                     str := ''
                     9000 timesRepeat: [ str := str, 'A' ]]].
====

The result appears after 24 961 ms.
An optimization is to use a Stream. Here is my source code:
===
MessageTally spyOn: 
     [ 500 timesRepeat: [
                     | str |  
                     str := WriteStream on: (String new)
                     9000 timesRepeat: [ str nextPut: $A ]]].
===

The result appears after 812 ms, which is a large improvement.
Now, we could optimize again using the preallocation. Here is my source code:

====
MessageTally spyOn: 
    [ 500 timesRepeat: [
                    | str |  
                    str := WriteStream on: (String new: 10000)
                    9000 timesRepeat: [ str nextPutAll: 'A' ]]].
====

And the result is strange: it makes 2 times slower to display the result.
The result appears after 1656 ms.

Here is the spy result:
===
 - 1656 tallies, 1656 msec.

**Tree**
--------------------------------
Process: (40s)  464519168: nil
--------------------------------
**Leaves**
22.9% {380ms} UndefinedObject>>DoIt
22.5% {373ms} SmallInteger(Integer)>>timesRepeat:
22.2% {368ms} WriteStream>>nextPutAll:
===

There is the call of UndefinedObject>>DoIt which is added and takes time.
Does anyone know what is done during the preallocation ?
Why is it slower than non-preallocation ?

Thanks for your answers.
Jannik
Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Andres Valloud-4
Maybe in the last case you also need to send nextPut: instead of
nextPutAll:...

On 4/27/11 23:35 , jannik.laval wrote:

> Hi all,
>
> I am playing with MessageTally, and I have a strange result with
> prealocation.
> Here is my example. I am working on a PharoCore1.3, with a VM4.2.5
>
> First of all, I spy this source code:
> ====
> MessageTally spyOn:
> [ 500 timesRepeat: [
> | str |
> *str := ''*.
> 9000 timesRepeat: [ str := str, 'A' ]]].
> ====
>
> The result appears after *24 961 ms*.
> An optimization is to use a Stream. Here is my source code:
> ===
> MessageTally spyOn:
> [ 500 timesRepeat: [
> | str |
> *str := WriteStream on: (String new)*.
> 9000 timesRepeat: [ str nextPut: $A ]]].
> ===
>
> The result appears after *812 ms*, which is a large improvement.
> Now, we could optimize again using the preallocation. Here is my source
> code:
>
> ====
> MessageTally spyOn:
> [ 500 timesRepeat: [
> | str |
> *str := WriteStream on: (String new: 10000)*.
> 9000 timesRepeat: [ str nextPutAll: 'A' ]]].
> ====
>
> And the result is strange: it makes 2 times slower to display the result.
> The result appears after 1656 ms.
>
> Here is the spy result:
> ===
> - 1656 tallies, 1656 msec.
>
> **Tree**
> --------------------------------
> Process: (40s) 464519168: nil
> --------------------------------
>
> **Leaves**
> *22.9% {380ms} UndefinedObject>>DoIt*
> 22.5% {373ms} SmallInteger(Integer)>>timesRepeat:
> 22.2% {368ms} WriteStream>>nextPutAll:
> ===
>
> There is the call of UndefinedObject>>DoIt which is added and takes time.
> Does anyone know what is done during the preallocation ?
> Why is it slower than non-preallocation ?
>
> Thanks for your answers.
> Jannik

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Andres Valloud-4
In reply to this post by jannik laval
As a side comment, I do not know if an atAllPut: method I wrote back in
about 2000 or so is still in the image... but if it is not, keep in mind
that you can use something like replaceFrom:to:with:startingAt: using
the receiver as the source of data, duplicating the amount of data
copied each time.  Back then, if you had to write the same object more
than 26 or so times, it was faster to use the duplicating "block copy"
method.  The key is to avoid multiple primitive calls (which have
expensive overhead compared to what is actually done), and replace them
with a single primitive call that does a bunch of writes.  With the
duplication method, you could get away with doing 9000 writes in no more
than 15 primitive calls.  I wrote about this in much more detail in the
Fundamentals volume 2 book.

On 4/27/11 23:35 , jannik.laval wrote:

> Hi all,
>
> I am playing with MessageTally, and I have a strange result with
> prealocation.
> Here is my example. I am working on a PharoCore1.3, with a VM4.2.5
>
> First of all, I spy this source code:
> ====
> MessageTally spyOn:
> [ 500 timesRepeat: [
> | str |
> *str := ''*.
> 9000 timesRepeat: [ str := str, 'A' ]]].
> ====
>
> The result appears after *24 961 ms*.
> An optimization is to use a Stream. Here is my source code:
> ===
> MessageTally spyOn:
> [ 500 timesRepeat: [
> | str |
> *str := WriteStream on: (String new)*.
> 9000 timesRepeat: [ str nextPut: $A ]]].
> ===
>
> The result appears after *812 ms*, which is a large improvement.
> Now, we could optimize again using the preallocation. Here is my source
> code:
>
> ====
> MessageTally spyOn:
> [ 500 timesRepeat: [
> | str |
> *str := WriteStream on: (String new: 10000)*.
> 9000 timesRepeat: [ str nextPutAll: 'A' ]]].
> ====
>
> And the result is strange: it makes 2 times slower to display the result.
> The result appears after 1656 ms.
>
> Here is the spy result:
> ===
> - 1656 tallies, 1656 msec.
>
> **Tree**
> --------------------------------
> Process: (40s) 464519168: nil
> --------------------------------
>
> **Leaves**
> *22.9% {380ms} UndefinedObject>>DoIt*
> 22.5% {373ms} SmallInteger(Integer)>>timesRepeat:
> 22.2% {368ms} WriteStream>>nextPutAll:
> ===
>
> There is the call of UndefinedObject>>DoIt which is added and takes time.
> Does anyone know what is done during the preallocation ?
> Why is it slower than non-preallocation ?
>
> Thanks for your answers.
> Jannik

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Henrik Sperre Johansen
On 28.04.2011 09:30, Andres Valloud wrote:

> As a side comment, I do not know if an atAllPut: method I wrote back
> in about 2000 or so is still in the image... but if it is not, keep in
> mind that you can use something like replaceFrom:to:with:startingAt:
> using the receiver as the source of data, duplicating the amount of
> data copied each time.  Back then, if you had to write the same object
> more than 26 or so times, it was faster to use the duplicating "block
> copy" method.  The key is to avoid multiple primitive calls (which
> have expensive overhead compared to what is actually done), and
> replace them with a single primitive call that does a bunch of
> writes.  With the duplication method, you could get away with doing
> 9000 writes in no more than 15 primitive calls.  I wrote about this in
> much more detail in the Fundamentals volume 2 book.
It is. It's still beautiful :D

>
> On 4/27/11 23:35 , jannik.laval wrote:
>> Hi all,
>>
>> I am playing with MessageTally, and I have a strange result with
>> prealocation.
>> Here is my example. I am working on a PharoCore1.3, with a VM4.2.5
>>
>> An optimization is to use a Stream. Here is my source code:
>> ===
>> MessageTally spyOn:
>> [ 500 timesRepeat: [
>> | str |
>> *str := WriteStream on: (String new)*.
>> 9000 timesRepeat: [ str nextPut: $A ]]].
>> ===
>>
>> The result appears after *812 ms*, which is a large improvement.
>> Now, we could optimize again using the preallocation. Here is my source
>> code:
>>
>> ====
>> MessageTally spyOn:
>> [ 500 timesRepeat: [
>> | str |
>> *str := WriteStream on: (String new: 10000)*.
>> 9000 timesRepeat: [ str nextPutAll: 'A' ]]].
>> ====
>>
>> And the result is strange: it makes 2 times slower to display the
>> result.
>> The result appears after 1656 ms.

In the first example, you are making a single string with all A's of
size 9000 repeated 500 times.
In the second example, you are making 9000 strings with all A's of size
10000  repeated 500 times.

ie. you are not measuring equivalent operations.

That it only takes twice as long is due to Andres' excellent nextPutAll:
implementation :)

Cheers,
Henry

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Andres Valloud-4
:).  It can be improved in a number of ways.  Instead of atAllPut:, it
should be from:to:put: (so atAllPut: x should be self from: 1 to: self
size put: x), it should do 8 or so at:put:s by hand before starting the
loop, and it should keep a block of 2048 or so for the iteration (less
memory traffic on the CPU that way, read *the same* 8kb or so all the
time, write *different* 8kb each time).  All of this is thoroughly
explained and measured in the book.  I forget the minute details right
now, but tinkering with those details does make a difference.

On 4/28/11 1:00 , Henrik Sperre Johansen wrote:

> On 28.04.2011 09:30, Andres Valloud wrote:
>> As a side comment, I do not know if an atAllPut: method I wrote back
>> in about 2000 or so is still in the image... but if it is not, keep in
>> mind that you can use something like replaceFrom:to:with:startingAt:
>> using the receiver as the source of data, duplicating the amount of
>> data copied each time.  Back then, if you had to write the same object
>> more than 26 or so times, it was faster to use the duplicating "block
>> copy" method.  The key is to avoid multiple primitive calls (which
>> have expensive overhead compared to what is actually done), and
>> replace them with a single primitive call that does a bunch of
>> writes.  With the duplication method, you could get away with doing
>> 9000 writes in no more than 15 primitive calls.  I wrote about this in
>> much more detail in the Fundamentals volume 2 book.
> It is. It's still beautiful :D
>
>>
>> On 4/27/11 23:35 , jannik.laval wrote:
>>> Hi all,
>>>
>>> I am playing with MessageTally, and I have a strange result with
>>> prealocation.
>>> Here is my example. I am working on a PharoCore1.3, with a VM4.2.5
>>>
>>> An optimization is to use a Stream. Here is my source code:
>>> ===
>>> MessageTally spyOn:
>>> [ 500 timesRepeat: [
>>> | str |
>>> *str := WriteStream on: (String new)*.
>>> 9000 timesRepeat: [ str nextPut: $A ]]].
>>> ===
>>>
>>> The result appears after *812 ms*, which is a large improvement.
>>> Now, we could optimize again using the preallocation. Here is my source
>>> code:
>>>
>>> ====
>>> MessageTally spyOn:
>>> [ 500 timesRepeat: [
>>> | str |
>>> *str := WriteStream on: (String new: 10000)*.
>>> 9000 timesRepeat: [ str nextPutAll: 'A' ]]].
>>> ====
>>>
>>> And the result is strange: it makes 2 times slower to display the
>>> result.
>>> The result appears after 1656 ms.
>
> In the first example, you are making a single string with all A's of
> size 9000 repeated 500 times.
> In the second example, you are making 9000 strings with all A's of size
> 10000  repeated 500 times.
>
> ie. you are not measuring equivalent operations.
>
> That it only takes twice as long is due to Andres' excellent nextPutAll:
> implementation :)
>
> Cheers,
> Henry
> .
>

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Stéphane Ducasse
In reply to this post by Henrik Sperre Johansen
Henrik

you lost me

> In the first example, you are making a single string with all A's of size 9000 repeated 500 times.
> In the second example, you are making 9000 strings with all A's of size 10000  repeated 500 times.

Why?

>>> An optimization is to use a Stream. Here is my source code:
>>> ===
>>> MessageTally spyOn:
>>> [ 500 timesRepeat: [
>>> | str |
>>> *str := WriteStream on: (String new)*.
>>> 9000 timesRepeat: [ str nextPut: $A ]]].
>>> ===
>>>
>>> The result appears after *812 ms*, which is a large improvement.
>>> Now, we could optimize again using the preallocation. Here is my source
>>> code:
>>>
>>> ====
>>> MessageTally spyOn:
>>> [ 500 timesRepeat: [
>>> | str |
>>> *str := WriteStream on: (String new: 10000)*.
>>> 9000 timesRepeat: [ str nextPutAll: 'A' ]]].
>>> ====
>>>
>>> And the result is strange: it makes 2 times slower to display the result.
>>> The result appears after 1656 ms.
>
> In the first example, you are making a single string with all A's of size 9000 repeated 500 times.
> In the second example, you are making 9000 strings with all A's of size 10000  repeated 500 times.


Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Toon Verwaest-2
In reply to this post by jannik laval
On 04/28/2011 08:35 AM, jannik.laval wrote:
First of all, I spy this source code:
====
MessageTally spyOn: 
     [ 500 timesRepeat: [
                     | str |  
                     str := ''
                     9000 timesRepeat: [ str := str, 'A' ]]].
====
This is what Joel Spolsky called a "Shlemiel the Painter's algorithm"
http://www.joelonsoftware.com/articles/fog0000000319.html

It's obviously very slow and it's very different from the code below for obvious reasons.

Other /languages that won't be named/ just do hacks to allow you to think in terms of string concatenation while it's optimized internally using buffers. This works well to make programs by dummies faster, but it also dumbs down programmers since you forget that stuff actually needs to be done.

The result appears after 24 961 ms.
An optimization is to use a Stream. Here is my source code:
===
MessageTally spyOn: 
     [ 500 timesRepeat: [
                     | str |  
                     str := WriteStream on: (String new)
                     9000 timesRepeat: [ str nextPut: $A ]]].
===

The result appears after 812 ms, which is a large improvement.
Now, we could optimize again using the preallocation. Here is my source code:

====
MessageTally spyOn: 
    [ 500 timesRepeat: [
                    | str |  
                    str := WriteStream on: (String new: 10000)
                    9000 timesRepeat: [ str nextPutAll: 'A' ]]].
====

And the result is strange: it makes 2 times slower to display the result.
The result appears after 1656 ms.

Here is the spy result:
===
 - 1656 tallies, 1656 msec.

**Tree**
--------------------------------
Process: (40s)  464519168: nil
--------------------------------
**Leaves**
22.9% {380ms} UndefinedObject>>DoIt
22.5% {373ms} SmallInteger(Integer)>>timesRepeat:
22.2% {368ms} WriteStream>>nextPutAll:
===

There is the call of UndefinedObject>>DoIt which is added and takes time.
Does anyone know what is done during the preallocation ?
Why is it slower than non-preallocation ?

Thanks for your answers.
Jannik

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Toon Verwaest-2
In reply to this post by Stéphane Ducasse

>> In the first example, you are making a single string with all A's of size 9000 repeated 500 times.
>> In the second example, you are making 9000 strings with all A's of size 10000  repeated 500 times.
> Why?
Because it's not true :) The problem is rather that you are doing
nextPutAll: with a string, which is obviously slower than nextPut: with
a character. NextPutAll: has to loop over the input string to copy the
values and as an optimization (which works against you in this case) it
increases the size of the string to be as big as you want. Look at their
code:

nextPutAll: aCollection

      | newEnd |
      collection class == aCollection class ifFalse:
          [^ super nextPutAll: aCollection ].

      newEnd := position + aCollection size.
      newEnd > writeLimit ifTrue:
          [self growTo: newEnd + 10].

      collection replaceFrom: position+1 to: newEnd  with: aCollection
startingAt: 1.
      position := newEnd.

     ^ aCollection

vs

nextPut: anObject
     "Primitive. Insert the argument at the next position in the Stream
     represented by the receiver. Fail if the collection of this stream
is not an
     Array or a String. Fail if the stream is positioned at its end, or
if the
     position is out of bounds in the collection. Fail if the argument
is not
     of the right type for the collection. Optional. See Object
documentation
     whatIsAPrimitive."

<primitive: 66>
     position >= writeLimit
         ifTrue: [^ self pastEndPut: anObject]
         ifFalse:
             [position := position + 1.
             ^collection at: position put: anObject]

The primitive will only fail if you are trying to write past the end.
This check is very fast since it's primitive... and we'll only go in
Smalltalk code when it fails. The first version on the other hand is
always first some smalltalk code (quite a lot for your basic example)
before it goes into a slightly more heavy primitive
(replaceFrom:to:with:startingAt:).

Of course it's both the same big-O (unlike the first example), but the
constant factor is pretty different because of smalltalk vs primitive
behavior.

>>>> An optimization is to use a Stream. Here is my source code:
>>>> ===
>>>> MessageTally spyOn:
>>>> [ 500 timesRepeat: [
>>>> | str |
>>>> *str := WriteStream on: (String new)*.
>>>> 9000 timesRepeat: [ str nextPut: $A ]]].
>>>> ===
>>>>
>>>> The result appears after *812 ms*, which is a large improvement.
>>>> Now, we could optimize again using the preallocation. Here is my source
>>>> code:
>>>>
>>>> ====
>>>> MessageTally spyOn:
>>>> [ 500 timesRepeat: [
>>>> | str |
>>>> *str := WriteStream on: (String new: 10000)*.
>>>> 9000 timesRepeat: [ str nextPutAll: 'A' ]]].
>>>> ====
>>>>
>>>> And the result is strange: it makes 2 times slower to display the result.
>>>> The result appears after 1656 ms.
>> In the first example, you are making a single string with all A's of size 9000 repeated 500 times.
>> In the second example, you are making 9000 strings with all A's of size 10000  repeated 500 times.
>


Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Henrik Sperre Johansen
In reply to this post by Stéphane Ducasse
On 28.04.2011 10:49, Stéphane Ducasse wrote:
> Henrik
>
> you lost me
>
>> In the first example, you are making a single string with all A's of size 9000 repeated 500 times.
>> In the second example, you are making 9000 strings with all A's of size 10000  repeated 500 times.
> Why?
My bad, I misread and thought the code was using atAllPut: , like it
should :) (that is, if its intent is to initialize a presized collection
with default values)

MessageTally spyOn:

  [ 500 timesRepeat: [
  | str |
  str :=String new: 9000.
  str atAllPut: $A ]].

only takes a couple of milliseconds.


As for nextPut: vs nextPutAll:, that is to be expected.
nextPutAll: is optimized for sizes>  1, on my machine it "catches up" when you surpass 4 characters added in each loop.

Cheers,
Henry





Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Stéphane Ducasse
tx I prefer that.
On Apr 28, 2011, at 12:17 PM, Henrik Sperre Johansen wrote:

> On 28.04.2011 10:49, Stéphane Ducasse wrote:
>> Henrik
>>
>> you lost me
>>
>>> In the first example, you are making a single string with all A's of size 9000 repeated 500 times.
>>> In the second example, you are making 9000 strings with all A's of size 10000  repeated 500 times.
>> Why?
> My bad, I misread and thought the code was using atAllPut: , like it should :) (that is, if its intent is to initialize a presized collection with default values)
>
> MessageTally spyOn:
>
> [ 500 timesRepeat: [
> | str |
> str :=String new: 9000.
> str atAllPut: $A ]].
>
> only takes a couple of milliseconds.
>
>
> As for nextPut: vs nextPutAll:, that is to be expected.
> nextPutAll: is optimized for sizes>  1, on my machine it "catches up" when you surpass 4 characters added in each loop.
>
> Cheers,
> Henry
>
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Henrik Sperre Johansen
In reply to this post by Toon Verwaest-2
On 28.04.2011 12:12, Toon Verwaest wrote:

> nextPut: anObject
>     "Primitive. Insert the argument at the next position in the Stream
>     represented by the receiver. Fail if the collection of this stream
> is not an
>     Array or a String. Fail if the stream is positioned at its end, or
> if the
>     position is out of bounds in the collection. Fail if the argument
> is not
>     of the right type for the collection. Optional. See Object
> documentation
>     whatIsAPrimitive."
>
> <primitive: 66>
>     position >= writeLimit
>         ifTrue: [^ self pastEndPut: anObject]
>         ifFalse:
>             [position := position + 1.
>             ^collection at: position put: anObject]
>
> The primitive will only fail if you are trying to write past the end.
> This check is very fast since it's primitive... and we'll only go in
> Smalltalk code when it fails. The first version on the other hand is
> always first some smalltalk code (quite a lot for your basic example)
> before it goes into a slightly more heavy primitive
> (replaceFrom:to:with:startingAt:).
To be exact, the primitive always fails on Cog (not implemented, jitted
version is just as fast), and only works for non-ReadWriteable streams
on newish (last year or so) VM's :)
http://forum.world.st/Bug-in-Interpreter-gt-gt-primitiveNextPut-td788236.html

The constant overhead is still lower than nextPutAll: of course.

Cheers,
Henry





Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Stéphane Ducasse
may be you could add a bug entry on the cog tracker

stef

On Apr 28, 2011, at 12:25 PM, Henrik Sperre Johansen wrote:

> On 28.04.2011 12:12, Toon Verwaest wrote:
>> nextPut: anObject
>>    "Primitive. Insert the argument at the next position in the Stream
>>    represented by the receiver. Fail if the collection of this stream is not an
>>    Array or a String. Fail if the stream is positioned at its end, or if the
>>    position is out of bounds in the collection. Fail if the argument is not
>>    of the right type for the collection. Optional. See Object documentation
>>    whatIsAPrimitive."
>>
>> <primitive: 66>
>>    position >= writeLimit
>>        ifTrue: [^ self pastEndPut: anObject]
>>        ifFalse:
>>            [position := position + 1.
>>            ^collection at: position put: anObject]
>>
>> The primitive will only fail if you are trying to write past the end. This check is very fast since it's primitive... and we'll only go in Smalltalk code when it fails. The first version on the other hand is always first some smalltalk code (quite a lot for your basic example) before it goes into a slightly more heavy primitive (replaceFrom:to:with:startingAt:).
> To be exact, the primitive always fails on Cog (not implemented, jitted version is just as fast), and only works for non-ReadWriteable streams on newish (last year or so) VM's :)
> http://forum.world.st/Bug-in-Interpreter-gt-gt-primitiveNextPut-td788236.html
>
> The constant overhead is still lower than nextPutAll: of course.
>
> Cheers,
> Henry
>
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Henrik Sperre Johansen
On 28.04.2011 11:27, Stéphane Ducasse wrote:
> may be you could add a bug entry on the cog tracker
>
> stef
It's not a bug, it's a feature.

Cheers,
Henry

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Toon Verwaest-2
On 04/28/2011 12:34 PM, Henrik Sperre Johansen wrote:
> On 28.04.2011 11:27, Stéphane Ducasse wrote:
>> may be you could add a bug entry on the cog tracker
>>
>> stef
> It's not a bug, it's a feature.
>
> Cheers,
> Henry
>
That it only works on non-readwrite streams?

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Nicolas Cellier
In reply to this post by Toon Verwaest-2
IMHO, you would need a stream equivalent of atAll:put: maybe something like
   stream next: 900 put: $A.

It would write directly to destination and save a copy versus say
   stream nextPutAll: (String new: 900 withAll: $A)

2011/4/28 Toon Verwaest <[hidden email]>:

> On 04/28/2011 08:35 AM, jannik.laval wrote:
>
> First of all, I spy this source code:
> ====
> MessageTally spyOn:
>      [ 500 timesRepeat: [
>                      | str |
>                      str := ''.
>                      9000 timesRepeat: [ str := str, 'A' ]]].
> ====
>
> This is what Joel Spolsky called a "Shlemiel the Painter's algorithm"
> http://www.joelonsoftware.com/articles/fog0000000319.html
>
> It's obviously very slow and it's very different from the code below for
> obvious reasons.
>
> Other /languages that won't be named/ just do hacks to allow you to think in
> terms of string concatenation while it's optimized internally using buffers.
> This works well to make programs by dummies faster, but it also dumbs down
> programmers since you forget that stuff actually needs to be done.
>
> The result appears after 24 961 ms.
> An optimization is to use a Stream. Here is my source code:
> ===
> MessageTally spyOn:
>      [ 500 timesRepeat: [
>                      | str |
>                      str := WriteStream on: (String new).
>                      9000 timesRepeat: [ str nextPut: $A ]]].
> ===
> The result appears after 812 ms, which is a large improvement.
> Now, we could optimize again using the preallocation. Here is my source
> code:
> ====
> MessageTally spyOn:
>     [ 500 timesRepeat: [
>                     | str |
>                     str := WriteStream on: (String new: 10000).
>                     9000 timesRepeat: [ str nextPutAll: 'A' ]]].
> ====
> And the result is strange: it makes 2 times slower to display the result.
> The result appears after 1656 ms.
> Here is the spy result:
> ===
>  - 1656 tallies, 1656 msec.
> **Tree**
> --------------------------------
> Process: (40s)  464519168: nil
> --------------------------------
> **Leaves**
> 22.9% {380ms} UndefinedObject>>DoIt
> 22.5% {373ms} SmallInteger(Integer)>>timesRepeat:
> 22.2% {368ms} WriteStream>>nextPutAll:
> ===
> There is the call of UndefinedObject>>DoIt which is added and takes time.
> Does anyone know what is done during the preallocation ?
> Why is it slower than non-preallocation ?
> Thanks for your answers.
> Jannik
>

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

Henrik Sperre Johansen
In reply to this post by Toon Verwaest-2
On 28.04.2011 12:35, Toon Verwaest wrote:

> On 04/28/2011 12:34 PM, Henrik Sperre Johansen wrote:
>> On 28.04.2011 11:27, Stéphane Ducasse wrote:
>>> may be you could add a bug entry on the cog tracker
>>>
>>> stef
>> It's not a bug, it's a feature.
>>
>> Cheers,
>> Henry
>>
> That it only works on non-readwrite streams?
>
No, that it's not implemented at all in Cog. (which is what I assume
Steph was talking about since he mentioned the cog tracker)

The "only works on non-readwrite streams"-bug in "standard" vm's was
fixed in VMMaker-dtl.153 as mentioned in the thread I linked, so the
definition of "newish" would be any VM built with sources newer than that.

Cheers,
Henry

Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

abergel
In reply to this post by jannik laval
Hi Jannik,

> ===
> MessageTally spyOn:
>      [ 500 timesRepeat: [
>                      | str |  
>                      str := WriteStream on: (String new).
>                      9000 timesRepeat: [ str nextPut: $A ]]].
> ===
>
> The result appears after 812 ms, which is a large improvement.
> Now, we could optimize again using the preallocation. Here is my source code:
>
> ====
> MessageTally spyOn:
>     [ 500 timesRepeat: [
>                     | str |  
>                     str := WriteStream on: (String new: 10000).
>                     9000 timesRepeat: [ str nextPutAll: 'A' ]]].
> ====

In the first case you use nextPut:, in the second case you use nextPutAll:.
Replacing nextPutAll: by nextPut: gives me coherent result.

330 ms for the first, and 321 for the second (with the preallocation).

Alexandre


> And the result is strange: it makes 2 times slower to display the result.
> The result appears after 1656 ms.
>
> Here is the spy result:
> ===
>  - 1656 tallies, 1656 msec.
>
> **Tree**
> --------------------------------
> Process: (40s)  464519168: nil
> --------------------------------
> **Leaves**
> 22.9% {380ms} UndefinedObject>>DoIt
> 22.5% {373ms} SmallInteger(Integer)>>timesRepeat:
> 22.2% {368ms} WriteStream>>nextPutAll:
> ===
>
> There is the call of UndefinedObject>>DoIt which is added and takes time.
> Does anyone know what is done during the preallocation ?
> Why is it slower than non-preallocation ?
>
> Thanks for your answers.
> Jannik

--
_,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:
Alexandre Bergel  http://www.bergel.eu
^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.






Reply | Threaded
Open this post in threaded view
|

Re: Preallocation behavior

jannik laval
Hi all,

thank you for the information.
In fact, Alex you are right, I have a nextPutAll: that I should replace.

Cheers,
Jannik

On Apr 28, 2011, at 15:02 , Alexandre Bergel wrote:

> Hi Jannik,
>
>> ===
>> MessageTally spyOn:
>>     [ 500 timesRepeat: [
>>                     | str |  
>>                     str := WriteStream on: (String new).
>>                     9000 timesRepeat: [ str nextPut: $A ]]].
>> ===
>>
>> The result appears after 812 ms, which is a large improvement.
>> Now, we could optimize again using the preallocation. Here is my source code:
>>
>> ====
>> MessageTally spyOn:
>>    [ 500 timesRepeat: [
>>                    | str |  
>>                    str := WriteStream on: (String new: 10000).
>>                    9000 timesRepeat: [ str nextPutAll: 'A' ]]].
>> ====
>
> In the first case you use nextPut:, in the second case you use nextPutAll:.
> Replacing nextPutAll: by nextPut: gives me coherent result.
>
> 330 ms for the first, and 321 for the second (with the preallocation).
>
> Alexandre
>
>
>> And the result is strange: it makes 2 times slower to display the result.
>> The result appears after 1656 ms.
>>
>> Here is the spy result:
>> ===
>> - 1656 tallies, 1656 msec.
>>
>> **Tree**
>> --------------------------------
>> Process: (40s)  464519168: nil
>> --------------------------------
>> **Leaves**
>> 22.9% {380ms} UndefinedObject>>DoIt
>> 22.5% {373ms} SmallInteger(Integer)>>timesRepeat:
>> 22.2% {368ms} WriteStream>>nextPutAll:
>> ===
>>
>> There is the call of UndefinedObject>>DoIt which is added and takes time.
>> Does anyone know what is done during the preallocation ?
>> Why is it slower than non-preallocation ?
>>
>> Thanks for your answers.
>> Jannik
>
> --
> _,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:
> Alexandre Bergel  http://www.bergel.eu
> ^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;._,.;:~^~:;.
>
>
>
>
>
>