EndOfStream unused

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

EndOfStream unused

Nicolas Cellier-3
EndOfStream Exception seems to be unused in 3.10

My understanding was that:

   [[aStream next doSomething] repeat]
    on: EndOfStream do: [:exc | exc return: nil].

would be more efficient than testing atEnd at each loop:
(would only the EndOfStream be signalled!)

   [aStream atEnd]
    whileFalse: [aStream next doSomething].

Especially, I have stream wrappers (to select:) with costly atEnd test!
Maybe isNil test is efficient but I want to be able to stream on nil!

   | nxt |
   [nxt := aStream next.
   nxt == nil]
    whileFalse: [aStream next doSomething].

Am I all wrong?
What do you think?

Nicolas


Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Nicolas Cellier-3
Hmm, some clues:

http://lists.squeakfoundation.org/pipermail/squeak-dev/2000-May/014403.html
http://lists.squeakfoundation.org/pipermail/squeak-dev/2000-May/020882.html

http://aspn.activestate.com/ASPN/Mail/Message/squeak-list/1924925

http://lists.squeakfoundation.org/pipermail/squeak-harvest/2004-January/001114.html
http://lists.squeakfoundation.org/pipermail/squeak-harvest/2004-October/006974.html
http://lists.squeakfoundation.org/pipermail/squeak-harvest/2004-October/007009.html

So it has been proposed many times but never connected...
According to last ref, it would break some code.
I guess because such code is catching wide
     [...] on: Error do: [...]

So a simple solution would be to change EndOfStream superclass from
Error to Notification.

Any echo to this proposition?




Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Nicolas Cellier-3
Prop following on http://bugs.squeak.org/view.php?id=6755


nicolas cellier a écrit :

> Hmm, some clues:
>
> http://lists.squeakfoundation.org/pipermail/squeak-dev/2000-May/014403.html
> http://lists.squeakfoundation.org/pipermail/squeak-dev/2000-May/020882.html
>
> http://aspn.activestate.com/ASPN/Mail/Message/squeak-list/1924925
>
> http://lists.squeakfoundation.org/pipermail/squeak-harvest/2004-January/001114.html 
>
> http://lists.squeakfoundation.org/pipermail/squeak-harvest/2004-October/006974.html 
>
> http://lists.squeakfoundation.org/pipermail/squeak-harvest/2004-October/007009.html 
>
>
> So it has been proposed many times but never connected...
> According to last ref, it would break some code.
> I guess because such code is catching wide
>     [...] on: Error do: [...]
>
> So a simple solution would be to change EndOfStream superclass from
> Error to Notification.
>
> Any echo to this proposition?
>



Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Andreas.Raab
In reply to this post by Nicolas Cellier-3
nicolas cellier wrote:
> Am I all wrong?
> What do you think?

I think you're crazy ;-) A change like this is going to cause a huge
amount of pain and will have fallout for *years* to come. That's because
you're dramatically changing the rules of the game: Where previously
#next at the end of stream would immediately return it will now start
searching the stack, evaluating handler blocks, and *eventually* return.
  You're switching from one bytecode to literally hundreds of messages
with exception handling in the middle in this very common case. This
will dramatically impact all areas of the system that use streams with
completely unknown behavior if, for example, an application has an
exception handler around that bit of stream code (every realistic
application has; often more than a single one). So code that needs to
run fast will have to be explicitly rewritten from the "next == nil"
test to use "atEnd" instead to avoid that stack-searching,
handler-evaluating overhead.

 From my perspective, this is a no-go area. If you really need streams
to signal EndOfStream then the right way to do this is to write a new
stream framework (or use one that supports it; perhaps Nile or Flow do)
and gradually migrate code to that framework (in which case it can be
tested and fixed as necessary). Everything else is basically suicide.

Cheers,
  - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Nicolas Cellier-3
Wrong.
VW does support it, and concerning efficiency, they are not that crazy.

True, there is a stack scan, but ONLY ONCE at the end of the stream.
If the stream is long enough, it will save A LOT of atEnd tests.
This is called optimistic programming.
Imagine that I ask you at each step "Are we arrived?"; you would not
bear a very long walk, would you? That's what the atEnd test is doing.

In following mail and at http://bugs.squeak.org/view.php?id=6755 I
already noticed possible exception handling problem that caused Marcus
to retract this change. This is because EndOfStream were declared an
Error instead of a Notification.

And testing next == nil is a ugly hack i reject because i have some
collections with some nil.

I want for example to use
        aCollection lazily
                collect: [:e | e color];
                select: [:e | e notNil]
using LazyStreams iterating only once.
Absence of EndOfStream notification is just spoiling the game.
But nevermind, I will just publish on VW public store where I will find
crazy guys interested.

Nicolas

Andreas Raab a écrit :

> nicolas cellier wrote:
>> Am I all wrong?
>> What do you think?
>
> I think you're crazy ;-) A change like this is going to cause a huge
> amount of pain and will have fallout for *years* to come. That's because
> you're dramatically changing the rules of the game: Where previously
> #next at the end of stream would immediately return it will now start
> searching the stack, evaluating handler blocks, and *eventually* return.
>  You're switching from one bytecode to literally hundreds of messages
> with exception handling in the middle in this very common case. This
> will dramatically impact all areas of the system that use streams with
> completely unknown behavior if, for example, an application has an
> exception handler around that bit of stream code (every realistic
> application has; often more than a single one). So code that needs to
> run fast will have to be explicitly rewritten from the "next == nil"
> test to use "atEnd" instead to avoid that stack-searching,
> handler-evaluating overhead.
>
>  From my perspective, this is a no-go area. If you really need streams
> to signal EndOfStream then the right way to do this is to write a new
> stream framework (or use one that supports it; perhaps Nile or Flow do)
> and gradually migrate code to that framework (in which case it can be
> tested and fixed as necessary). Everything else is basically suicide.
>
> Cheers,
>  - Andreas
>
>


Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Andreas.Raab
nicolas cellier wrote:
> Wrong.

What exactly is wrong? That it does stack searches? That it evaluates
handler blocks? That it will cause pain due to unforeseen interactions?
That it is slow? Let's start there. How about a little benchmark:

ReadStream subclass: #ReadStreamWithNil

ReadStreamWithNil>>next
        <primitive: 65>
        position >= readLimit
                ifTrue: [^EndOfStream signal]
                ifFalse: [^collection at: (position := position + 1)]


And now:

streamClass := ReadStream. "vs. ReadStreamWithNil"
data := (1 to: 5) asArray.
[1 to: 100000 do:[:i|
        stream := streamClass on: data.
        [stream next == nil] whileFalse.
]] timeToRun.

If you run this trivial little benchmark, the results should be
enlightening: With ReadStream it completes (on my box) within 280 msecs.
With ReadStreamWithNil it completes in 1617 msecs. That is a factor of
6x in speed. Even if you change it to 100 elements in data you are
*still* at half of the speed (1645 vs. 3172 ms).

> VW does support it, and concerning efficiency, they are not that crazy.

VW isn't Squeak. If you think that VWs and Squeaks exception handling
have comparable performance, allow me to laugh heartily. Besides which
VW is a *lot* faster to begin with so the overhead is less noticable in
real applications (though I'm sure the overhead is measurable).

> True, there is a stack scan, but ONLY ONCE at the end of the stream.
> If the stream is long enough, it will save A LOT of atEnd tests.

Err, only if the stream *has* atEnd tests. Most code that is concerned
with efficient stream reads today goes like this:

   [(value := stream next) == nil] whileFalse:[].

No atEnd tests are saved in that situation. But even if we change our
benchmark to, e.g.,

data := (1 to: 5) asArray.
[1 to: 100000 do:[:i|
        stream := ReadStream on: data.
        [stream atEnd] whileFalse:[stream next].
]] timeToRun.

It comes in at 325 msecs on my box with is only 30% worse than the
"naked" stream next == nil test and about 4x *faster* than using
EndOfStream. And if you extend data to 100 elements it still comes in
right in the middle of the other variants (2500 msecs).

> This is called optimistic programming.

And what I do is called "measuring" ;-)

> Imagine that I ask you at each step "Are we arrived?"; you would not
> bear a very long walk, would you? That's what the atEnd test is doing.

True, when it's there. But unless you change exception handling it's
often (in particular for internal streams) still a *lot* faster since EH
is expensive in tight loops.

> In following mail and at http://bugs.squeak.org/view.php?id=6755 I
> already noticed possible exception handling problem that caused Marcus
> to retract this change. This is because EndOfStream were declared an
> Error instead of a Notification.

Have you actually *tried* it? Because you may be in for a nasty little
surprise. I'm not sure if this problem is going to bite you or not but
from the code it looks like it should so try it - it is just about
*exactly* the kind of thing that goes wrong for "no good reason" when
you change something as fundamental as this.

> And testing next == nil is a ugly hack i reject because i have some
> collections with some nil.

Then use atEnd. That's what it's for.

> I want for example to use
>     aCollection lazily
>         collect: [:e | e color];
>         select: [:e | e notNil]
> using LazyStreams iterating only once.
> Absence of EndOfStream notification is just spoiling the game.

I don't know. I find it hard to imagine an atEnd test that could
possibly be as costly as running the EH machinery. It's certainly worth
measuring before conjecturing about it.

> But nevermind, I will just publish on VW public store where I will find
> crazy guys interested.

As you'd like. If you are ever interested in having a serious discussion
about the topic I'll be waiting here.

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Nicolas Cellier-3
Andreas Raab a écrit :

> nicolas cellier wrote:
>> Wrong.
>
> What exactly is wrong? That it does stack searches? That it evaluates
> handler blocks? That it will cause pain due to unforeseen interactions?
> That it is slow? Let's start there. How about a little benchmark:
>
> ReadStream subclass: #ReadStreamWithNil
>
> ReadStreamWithNil>>next
>     <primitive: 65>
>     position >= readLimit
>         ifTrue: [^EndOfStream signal]
>         ifFalse: [^collection at: (position := position + 1)]
>
>
> And now:
>
> streamClass := ReadStream. "vs. ReadStreamWithNil"
> data := (1 to: 5) asArray.
> [1 to: 100000 do:[:i|
>     stream := streamClass on: data.
>     [stream next == nil] whileFalse.
> ]] timeToRun.
>
> If you run this trivial little benchmark, the results should be
> enlightening: With ReadStream it completes (on my box) within 280 msecs.
> With ReadStreamWithNil it completes in 1617 msecs. That is a factor of
> 6x in speed. Even if you change it to 100 elements in data you are
> *still* at half of the speed (1645 vs. 3172 ms).
>

Common, I could expect better from you.
This test is totally biased.
You could as well write

streamClass := ReadStream. "vs. ReadStreamWithNil"
data := (1 to: 100000) asArray.
[1 to: 5 do:[:i|
     stream := streamClass on: data.
     [stream next == nil] whileFalse.
]] timeToRun.

And obtain different results (260 original, 278 with EndOfStream)
Now, I'am not spoiling so much.

And then:

streamClass := ReadStreamWithNil.
data := (1 to: 100000) asArray.
[1 to: 5 do:[:i|
     [| aStream |
        aStream := streamClass on: data.
     [aStream next. true] whileTrue] on: EndOfStream do: [:exc | exc
return].
]] timeToRun.

255 fine, now I am as fast if not faster than == nil.

And far better than:

streamClass := ReadStream. "vs. ReadStreamWithNil"
data := (1 to: 100000) asArray.
[1 to: 5 do:[:i|
     stream := streamClass on: data.
     [stream atEnd] whileFalse: [stream next].
]] timeToRun.

433 too bad if ever you have to handle collections with nils...


OK, I loose with nicer code:

streamClass := ReadStreamWithNil.
data := (1 to: 100000) asArray.
[1 to: 5 do:[:i|
     stream := streamClass on: data.
     [stream next] repeatUntil: EndOfStream.
]] timeToRun.

  477 simply because repeat not inlined by compiler... nothing to do
with exception handling...

Enough with these bench...


>> VW does support it, and concerning efficiency, they are not that crazy.
>
> VW isn't Squeak. If you think that VWs and Squeaks exception handling
> have comparable performance, allow me to laugh heartily. Besides which
> VW is a *lot* faster to begin with so the overhead is less noticable in
> real applications (though I'm sure the overhead is measurable).
>
>> True, there is a stack scan, but ONLY ONCE at the end of the stream.
>> If the stream is long enough, it will save A LOT of atEnd tests.
>
> Err, only if the stream *has* atEnd tests. Most code that is concerned
> with efficient stream reads today goes like this:
>
>   [(value := stream next) == nil] whileFalse:[].
>

There are simply 267 senders of atEnd in my 3.10, most in while loops.

And once again, == nil is not general enough but for some character
streams and the like.


> No atEnd tests are saved in that situation. But even if we change our
> benchmark to, e.g.,
>
> data := (1 to: 5) asArray.
> [1 to: 100000 do:[:i|
>     stream := ReadStream on: data.
>     [stream atEnd] whileFalse:[stream next].
> ]] timeToRun.
>
> It comes in at 325 msecs on my box with is only 30% worse than the
> "naked" stream next == nil test and about 4x *faster* than using
> EndOfStream. And if you extend data to 100 elements it still comes in
> right in the middle of the other variants (2500 msecs).
>
>> This is called optimistic programming.
>
> And what I do is called "measuring" ;-)
>

Sure it's not called unfair biased argumentation?

Of course there is a trade-off in optimistic programming. It relies on
Exception being rare. Your example put the cursor at opposite.

Now, I admit that i can degrade some short stream created in tight loops
and optimized with == nil tests. But you have to show some real example,
something more serious than above tricks.


>> Imagine that I ask you at each step "Are we arrived?"; you would not
>> bear a very long walk, would you? That's what the atEnd test is doing.
>
> True, when it's there. But unless you change exception handling it's
> often (in particular for internal streams) still a *lot* faster since EH
> is expensive in tight loops.
>

I know that.
My aim is not to use it in tight loops!
Only once at end of stream.

>> In following mail and at http://bugs.squeak.org/view.php?id=6755 I
>> already noticed possible exception handling problem that caused Marcus
>> to retract this change. This is because EndOfStream were declared an
>> Error instead of a Notification.
>
> Have you actually *tried* it? Because you may be in for a nasty little
> surprise. I'm not sure if this problem is going to bite you or not but
> from the code it looks like it should so try it - it is just about
> *exactly* the kind of thing that goes wrong for "no good reason" when
> you change something as fundamental as this.
>

That is wise. Of course it deserve testing! who is saying the contrary?
I would not like myself that some guy do impose such potentially
dangerous changes in my image. He has to prove first for sure.
I do not want to impose it now. I want it to be discussed.

I passed some of the tests in my image (not all, because they hang my
3.10 without the change).
No problem so far.

Why? because I'am not that crazy, I turned the Error into a
Notification. Period.

Now it's still dangerous if a fool is catching Notification or even
better Exception!
Or if some Exception mechanism use streaming themselves...
Who knows...

>> And testing next == nil is a ugly hack i reject because i have some
>> collections with some nil.
>
> Then use atEnd. That's what it's for.
>
>> I want for example to use
>>     aCollection lazily
>>         collect: [:e | e color];
>>         select: [:e | e notNil]
>> using LazyStreams iterating only once.
>> Absence of EndOfStream notification is just spoiling the game.
>
> I don't know. I find it hard to imagine an atEnd test that could
> possibly be as costly as running the EH machinery. It's certainly worth
> measuring before conjecturing about it.
>

It is, because a SelectStream doing a select: operation is duplicating
the job calling the block once in atEnd test and another in next, AND
because I cannot use == nil trick in above example.


>> But nevermind, I will just publish on VW public store where I will
>> find crazy guys interested.
>
> As you'd like. If you are ever interested in having a serious discussion
> about the topic I'll be waiting here.
>

This was an answer to crazy.
4 AM. For a really serious discussion, I now need to rest a little.

> Cheers,
>   - Andreas
>
>

Agree that your arguments are not all wrong. But the way you push them
is more than unfriendly.
These points deserve discussion. No use to turn it into bashing!

Cheers


Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Tapple Gao
In reply to this post by Nicolas Cellier-3
On Tue, Nov 06, 2007 at 11:05:58PM +0100, nicolas cellier wrote:

> EndOfStream Exception seems to be unused in 3.10
>
> My understanding was that:
>
>   [[aStream next doSomething] repeat]
>   on: EndOfStream do: [:exc | exc return: nil].
>
> would be more efficient than testing atEnd at each loop:
> (would only the EndOfStream be signalled!)
>
>   [aStream atEnd]
>   whileFalse: [aStream next doSomething].

as an aside, Exception handling isn't really squeaky, in that it
is hardly used at all in most code. More commonly used is stuff
like custom control structures:

Dictionary>>at:ifAbsent:
Dictionary>>at:ifPresent:
Object>>ifNil:

You could define this in ReadStream, ReadWriteStream, and maybe
RWTextOrBinaryStream to cover most cases:

nextIfAtEnd: aBlock
    "Primitive. Answer the next object 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. Optional. See Object documentation
    whatIsAPrimitive."

    <primitive: 65>
    position >= readLimit
        ifTrue: [^ aBlock value]
        ifFalse: [^collection at: (position := position + 1)]

> Am I all wrong?
> What do you think?

Just a guess, but blocks are pretty fast, probably faster than
exception handling, and, imho, a lot simpler. Exceptions are
more feature complete, but not commonly needed, imho. And, you
could do "nextIfAtEnd: [EndOfStream signal]" to do error
handling.

--
Matthew Fulmer -- http://mtfulmer.wordpress.com/
Help improve Squeak Documentation: http://wiki.squeak.org/squeak/808

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Nicolas Cellier-3
Yes, only 129 senders of signal, 95 of signal: in 3.10, some in tests.
and
   Exception allSubclasses size. 73, some unused.
There were no Exception in st-80...

Block is very convenient for my simple task. I like it.
But would introduce a rewrite of a lot more methods if applied to files
(see the *FileStream basicNext and all this mess).

And Exception is more general. It can be handled in various ways.

As for efficiency, passing some extra arguments at each loop might also
be less efficient than performing an Exception once at end if stream is
long enough...
...as illustrated by Andreas like bench:

data := (1 to: 100000) asArray.
[1 to: 5 do:[:i|
     stream := streamClass on: data.
        atEnd := false.
     [stream nextIfAtEnd: [atEnd := true]. atEnd] whileFalse.
]] timeToRun.

971 instead of 260 for stream next == nil, and 255 for EndOfStream
Exception...


Matthew Fulmer a écrit :

> On Tue, Nov 06, 2007 at 11:05:58PM +0100, nicolas cellier wrote:
>> EndOfStream Exception seems to be unused in 3.10
>>
>> My understanding was that:
>>
>>   [[aStream next doSomething] repeat]
>>   on: EndOfStream do: [:exc | exc return: nil].
>>
>> would be more efficient than testing atEnd at each loop:
>> (would only the EndOfStream be signalled!)
>>
>>   [aStream atEnd]
>>   whileFalse: [aStream next doSomething].
>
> as an aside, Exception handling isn't really squeaky, in that it
> is hardly used at all in most code. More commonly used is stuff
> like custom control structures:
>
> Dictionary>>at:ifAbsent:
> Dictionary>>at:ifPresent:
> Object>>ifNil:
>
> You could define this in ReadStream, ReadWriteStream, and maybe
> RWTextOrBinaryStream to cover most cases:
>
> nextIfAtEnd: aBlock
>     "Primitive. Answer the next object 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. Optional. See Object documentation
>     whatIsAPrimitive."
>
>     <primitive: 65>
>     position >= readLimit
>         ifTrue: [^ aBlock value]
>         ifFalse: [^collection at: (position := position + 1)]
>
>> Am I all wrong?
>> What do you think?
>
> Just a guess, but blocks are pretty fast, probably faster than
> exception handling, and, imho, a lot simpler. Exceptions are
> more feature complete, but not commonly needed, imho. And, you
> could do "nextIfAtEnd: [EndOfStream signal]" to do error
> handling.
>



Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

timrowledge

On 6-Nov-07, at 8:46 PM, nicolas cellier wrote:
>
>
> And Exception is more general. It can be handled in various ways.
I won't argue with that but the practical problem I seem to keep  
running into with exception handling is simply that nobody does it  
very well.  I don't mean the actual mechanism of raising and catching  
exceptions either - after all I did the VM and a fair bit of the image  
work for exception handling in several Smalltalk systems, so obviously  
that was Simply Magnificent :-)
No, the bit that bothers me is how poorly exceptions get used.  I've  
seen cases where every possible exception was being caught and ignored  
'to avoid presenting users with notifiers'.  I've seen people use them  
to pass a value up to some other bit of code.  I've seen people do all  
sorts of obscene things. Yuck.

Surely, somewhere there must be some documentation that explains how  
to do it well? I'd love to be able to point culprits at a useful  
learning example.


tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Did you hear about Christopher Robin Hood? He stole from the rich to  
give to the Pooh



Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Andreas.Raab
In reply to this post by Nicolas Cellier-3
nicolas cellier wrote:
> Common, I could expect better from you.
> This test is totally biased.

How so? I used collections of sizes between 5 and 100 elements. That
doesn't seem "totally biased" for internal collections to me. If you
have evidence that the average internal collection is 100000+ elements
large I would like to see that evidence. Data sizes of 5-100 elements
seem quite realistic for average situations to me.

> You could as well write
>
> streamClass := ReadStream. "vs. ReadStreamWithNil"
> data := (1 to: 100000) asArray.
> [1 to: 5 do:[:i|
>     stream := streamClass on: data.
>     [stream next == nil] whileFalse.
> ]] timeToRun.
>
> And obtain different results (260 original, 278 with EndOfStream)

Knowing your experience with Squeak, these results should strike you as
very odd. *Five* EndOfStream signals would take 18msecs? 3+ msecs each?
That on its own should tell you there is something wrong (even with my
measures EndOfStream signaling took closer to .015 msecs which is still
a very long time). I'm not sure what you measured but it cerainly got
nothing to do with EndOfStream signaling.

> Now, I'am not spoiling so much.
>
> And then:
>
> streamClass := ReadStreamWithNil.
> data := (1 to: 100000) asArray.
> [1 to: 5 do:[:i|
>     [| aStream |
>     aStream := streamClass on: data.
>     [aStream next. true] whileTrue] on: EndOfStream do: [:exc | exc
> return].
> ]] timeToRun.
>
> 255 fine, now I am as fast if not faster than == nil.

But this just making my point. Which was that if you want to use
EndOfStream efficiently you *will* have to rewrite code all over the
places. Which is something that I'm certain VW does in places where that
matters but no code that has ever written for Squeak does that today.
Instead it uses the "stream next == nil" pattern and that is what I
compared - a realistic test of what such a change does to the existing
code base. I was never claiming that you can't possibly write efficient
code with EndOfStream but that all code in existence today will have to
be reviewed which is why introducing that pattern today without
understanding its consequences is just a terribly bad idea.

>>> This is called optimistic programming.
>>
>> And what I do is called "measuring" ;-)
>
> Sure it's not called unfair biased argumentation?

I don't think so. In my experience most streams operate on collections
of a few dozen elements so testing data sizes between 5-100 seems
totally realistic to me (again, if you have evidence to the contrary I'd
like to see it). For example, if I just run a quick:

(SequenceableCollection allSubInstances collect:[:c| c size]) average

I end up with 45 elements. Now, granted this may not be the average size
of internal collections used for streams but since most streams are
transient it's hard to get an actual number for it. But it doesn't make
me feel like running numbers between 5 and 100 seems "totally biased".
To the contrary.

> Of course there is a trade-off in optimistic programming. It relies on
> Exception being rare. Your example put the cursor at opposite.

I don't know. A probability between .2 to .01 is the "opposite of rare"?

> Now, I admit that i can degrade some short stream created in tight loops
> and optimized with == nil tests. But you have to show some real example,
> something more serious than above tricks.

Give me an example that you're interested in. And the above is no
"trick"; it is a micro-benchmark. These are of limited use to compare
real-world usages but they give an understanding of the baseline of
behavior one is talking about, for example: what is the actual cost of
EndOfStream and what is the number of elements at which point the effect
of EndOfStream becomes negligible. That in itself is valuable information.

>>> In following mail and at http://bugs.squeak.org/view.php?id=6755 I
>>> already noticed possible exception handling problem that caused
>>> Marcus to retract this change. This is because EndOfStream were
>>> declared an Error instead of a Notification.
>>
>> Have you actually *tried* it? Because you may be in for a nasty little
>> surprise. I'm not sure if this problem is going to bite you or not but
>> from the code it looks like it should so try it - it is just about
>> *exactly* the kind of thing that goes wrong for "no good reason" when
>> you change something as fundamental as this.
>>
>
> That is wise. Of course it deserve testing! who is saying the contrary?

It's not supposed to be "wise", it is supposed to be illustrating a
point. The point is that a low-level change like this always comes with
a *ton* of unforeseen problems. It was very interesting for me to find
out that adding a progress bar for downloads in Croquet would break SM
for the very reason that SM handles Exception instead of Error. Yes,
that is a bug in SM but it's just the kind of thing that you have to
expect with such low-level changes and which makes them as risky as they
are.

And just in case that wasn't clear, my point here is that even if you
*think* you "fixed the problem" with making EndOfStream a notification
it turns out that in practice the dependencies (and sometimes bugs) are
much more complex than what you'd assume.

> I would not like myself that some guy do impose such potentially
> dangerous changes in my image. He has to prove first for sure.
> I do not want to impose it now. I want it to be discussed.

But I'm discussing it ;-) I just happen to question the value of that
change in particular considering the implications that this change has.

> I passed some of the tests in my image (not all, because they hang my
> 3.10 without the change).
> No problem so far.
>
> Why? because I'am not that crazy, I turned the Error into a
> Notification. Period.
>
> Now it's still dangerous if a fool is catching Notification or even
> better Exception!
> Or if some Exception mechanism use streaming themselves...
> Who knows...

Well, but again, that *is* my point. People *do* make mistakes and
changes like these will trigger a whole new world of pain of previously
hidden bugs and strangeness.

>> I don't know. I find it hard to imagine an atEnd test that could
>> possibly be as costly as running the EH machinery. It's certainly
>> worth measuring before conjecturing about it.
>
> It is, because a SelectStream doing a select: operation is duplicating
> the job calling the block once in atEnd test and another in next, AND
> because I cannot use == nil trick in above example.

Where is that SelectStream? I'd like to have a look at it to see whether
that complexity is really necessary in there or not.

>> As you'd like. If you are ever interested in having a serious
>> discussion about the topic I'll be waiting here.
>
> This was an answer to crazy.

I think you may have missed the smiley at the end of that sentence.

> 4 AM. For a really serious discussion, I now need to rest a little.

Yes, I think that's a good idea. Seriously, think about this a little
more. I really don't think that my values are *that* biased; I was
actually surprised about how terribly slow EndOfStream is myself. And
micro-benchmarks measure what micro-benchmarks measure; but I would
expect a 2x slowdown with 100 elements to show some real results in
applications (maybe "only" by 10-20% but Squeak is slow as it stands).

> Agree that your arguments are not all wrong. But the way you push them
> is more than unfriendly.

I apologize. I got bitten by changes like these (which got added without
any thought about the implications) in the past and do react a little
over the top when I see those proposals come up again (it's the "oh, no,
not *again*" knee-jerk kind of reaction). The proposals *always* get
made with the best of intentions are are never really thought through in
terms of what they do to Squeak as a *system*.

This, by the way, is where I miss Dan's leadership most. He is just the
master of assessing systems implications.

Cheers,
   - Andreas


Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Paolo Bonzini-2

> I don't think so. In my experience most streams operate on collections
> of a few dozen elements so testing data sizes between 5-100 seems
> totally realistic to me (again, if you have evidence to the contrary I'd
> like to see it). For example, if I just run a quick:
>
> (SequenceableCollection allSubInstances collect:[:c| c size]) average
>
> I end up with 45 elements. Now, granted this may not be the average size
> of internal collections used for streams but since most streams are
> transient it's hard to get an actual number for it.

In my experience (it might be a little different in Squeak) most
collection streams are WriteStreams.  Most ReadStreams are file-based,
and those are longer than 100 bytes.

Paolo

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Nicolas Cellier-3
In reply to this post by timrowledge
tim Rowledge <tim <at> rowledge.org> writes:

>
>
> On 6-Nov-07, at 8:46 PM, nicolas cellier wrote:
> >
> >
> > And Exception is more general. It can be handled in various ways.
> I won't argue with that but the practical problem I seem to keep  
> running into with exception handling is simply that nobody does it  
> very well.  I don't mean the actual mechanism of raising and catching  
> exceptions either - after all I did the VM and a fair bit of the image  
> work for exception handling in several Smalltalk systems, so obviously  
> that was Simply Magnificent
> No, the bit that bothers me is how poorly exceptions get used.  I've  
> seen cases where every possible exception was being caught and ignored  
> 'to avoid presenting users with notifiers'.  I've seen people use them  
> to pass a value up to some other bit of code.  I've seen people do all  
> sorts of obscene things. Yuck.
>
> Surely, somewhere there must be some documentation that explains how  
> to do it well? I'd love to be able to point culprits at a useful  
> learning example.
>
> tim
> --
> tim Rowledge; tim <at> rowledge.org; http://www.rowledge.org/tim
> Did you hear about Christopher Robin Hood? He stole from the rich to  
> give to the Pooh
>
>


Concerning Exceptions in general, my only rules are:
- Rule 1: Don't use them unless it really helps.
- Rule 2: Don't forbid them neither!

You know the advantages:
1) factor error handling across a complex code hierarchy (several methods in
several classes).
It often beat alternative consisting to pass extra error-handling-arguments to a
bunch of messages and/or objects, or testing return codes at a lot of levels.
2) avoid same precondition tests repeated at lot of levels (optimistic
programming), keep only lowest level protected (usually a primitive).

You know the defaults:
1) don't protect inside a tight loop
2) in simple cases use a simple block argument
3) if handling logic become complex, better create a ErrorHandler object (rather
than the horror show you describe)

Compiler-Decoder would provide a complex test case for Exception framework.
OldCompiler does not use Exception.
A handler is passed, but is not known at every level of internal logic,
resulting in some error not being handled
(you get a debugger open on compiler internal logic,
which is not a direct path to your source code problem)...
Would Exception Help
- simplifying the code?
- enhance capabilities?
Maybe a subject for summer...



Concerning EndOfStream, it would be used mainly in some private Stream logic.
Eventually at a few place on client side (like factoring premature end of file
alarm), or maybe not at all.

I experiment a specific Stream extensions that would greatly benefit from it.
Existing code would not benefit from, unless rewritten.

Whether rule 2 implies:
- Rule 3: provide an Exception mechanism where it can prove usefull
is the core of this discussion.

Warning from Andreas are legitimate for sure: would it hurt?
1) It would not prevent [stream atEnd] nor [stream next == nil] constructs from
being used.
2) It might introduce a performance drop (whether noticeable or not in real
applications has to be proved).
3) It might introduce bugs and need strong/long tests phases in some production
image (IMO, using a Notification solves that, up to me to prove it).

Maybe that's a second Nile-like subject for summer as Andreas suggested, though
i see it totally decoupled from traits (i did not make my opinion whether they
are good thing or not). But while at refactoring...


Nicolas


Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Nicolas Cellier-3
In reply to this post by Paolo Bonzini-2
Paolo Bonzini <bonzini <at> gnu.org> writes:

>
>
> > I don't think so. In my experience most streams operate on collections
> > of a few dozen elements so testing data sizes between 5-100 seems
> > totally realistic to me (again, if you have evidence to the contrary I'd
> > like to see it). For example, if I just run a quick:
> >
> > (SequenceableCollection allSubInstances collect:[:c| c size]) average
> >
> > I end up with 45 elements. Now, granted this may not be the average size
> > of internal collections used for streams but since most streams are
> > transient it's hard to get an actual number for it.
>
> In my experience (it might be a little different in Squeak) most
> collection streams are WriteStreams.  Most ReadStreams are file-based,
> and those are longer than 100 bytes.
>
> Paolo
>
>


Mostly agree with last Andreas mail.
And apologizes too for agressive over-reacting toward a master.
Answering me is yet trying to be constructive, thanks.

Paolo makes a good point too.
The optimized == nil trick is a Character or Byte Stream thing anyway.
- this let files, Files are long
- but ReadStream can be used in short String processing too
There I must inquire for performance drop!


Otherwise, I'm just playing with LazyStream ideas which would be more valuable
on large collections of course...
See
http://lists.squeakfoundation.org/pipermail/squeak-dev/2007-September/120222.html
up to Paolo's
http://lists.squeakfoundation.org/pipermail/squeak-dev/2007-September/120695.html
I thought i add some other interesting refs, but Pipe thread was so long...

I would write entirely with stream
  someCollectionOrStream lazily
   select: [:e | e someTest];
   collect: [:e | e square];
   reject: [:e | e otherTest];
   contents

With a trick (in place modification of receiver) to handle pipe without
parenthesis. Much like a unix filter, processes are blocks, pipes are stream.

cheers

Nicolas


Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Paolo Bonzini-2

> Paolo makes a good point too.
> The optimized == nil trick is a Character or Byte Stream thing anyway.
> - this let files, Files are long
> - but ReadStream can be used in short String processing too
> There I must inquire for performance drop!

You should also make a list of places where next == nil is used.  Are
there so many really?  Can they do something like

p := self position.
size := (stream setToEnd; position) - p.
self position: p.
size timesRepeat: [ ... ]

It might be faster or slower than next == nil, who knows...

Paolo

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Damien Cassou-3
In reply to this post by Nicolas Cellier-3
Hi Nicolas,

as Andreas suggested, you can use Nile
(http://www.squeaksource.com/Nile/) and adapt it to your needs (please
commit any change directly to the repository). Nile is way more
simpler than it was on previous announcements... a new announcement
will arrive soon :-).

Please ask any question you may have.

Bye

--
Damien Cassou

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Jason Johnson-5
In reply to this post by Tapple Gao
On Nov 7, 2007 4:59 AM, Matthew Fulmer <[hidden email]> wrote:
>
> as an aside, Exception handling isn't really squeaky, in that it
> is hardly used at all in most code. More commonly used is stuff
> like custom control structures:
>
> Dictionary>>at:ifAbsent:
> Dictionary>>at:ifPresent:
> Object>>ifNil:

+1.  I have traditionally been a big user of exceptions but so far in
my Squeaking I have not used them once.  I find the #ifError: type
construct very nice, as I'm usually just interested in return from a
location a few stack frames up in the case of error anyway.  It's much
more code to do:

someMethod

  [ codeThatCallsThreeMethodsThenCrashes ] on: Error do: [ ^ nil ]

then just:

codeThatCallThreeMethodsThen: [ ^ nil ]

I realize that means this handler has to be passed to several methods,
but usually these methods are themselves an external protocol, so it
makes sense for them to have the handler anyway.

I think it's potentially a good idea to have stream do this common
behavior, i.e.:

Stream>>next
  ^ self nextIfAtEnd: [ ^ nil ]

Stream>>nextIfAtEnd: anEndBlock
  " ... "

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Paolo Bonzini-2

> Stream>>next
>   ^ self nextIfAtEnd: [ ^ nil ]
>
> Stream>>nextIfAtEnd: anEndBlock

If you don't care about the cost of creating a block (and on "real"
closure implementations blocks that return are the most expensive ones),
you will just use #do:.  This will kill performance 100 times more than
an EndOfStream notification.

Smalltalk people sometimes seem to be allergic to thinking out of the
box.  #at:ifAbsent:-like things have a place, exception handling has a
similar but different place.  Nobody is proposing to have millions of
exceptions as in Java.

Paolo

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

Jason Johnson-5
Why would it kill performance?  The block is created at the first
call, and thereafter a reference to it is past, no?  That sounds
pretty fast to me.

I don't think Smalltalk people have a hard time thinking outside the
box, I think rather they like flexibility and don't always concern
themselves by the dead ends other systems pursue. :)  Certainly not to
say exceptions are a dead end, just that having a choice when possible
is nice.

On Nov 8, 2007 6:22 PM, Paolo Bonzini <[hidden email]> wrote:

>
> > Stream>>next
> >   ^ self nextIfAtEnd: [ ^ nil ]
> >
> > Stream>>nextIfAtEnd: anEndBlock
>
> If you don't care about the cost of creating a block (and on "real"
> closure implementations blocks that return are the most expensive ones),
> you will just use #do:.  This will kill performance 100 times more than
> an EndOfStream notification.
>
> Smalltalk people sometimes seem to be allergic to thinking out of the
> box.  #at:ifAbsent:-like things have a place, exception handling has a
> similar but different place.  Nobody is proposing to have millions of
> exceptions as in Java.
>
> Paolo
>
>

Reply | Threaded
Open this post in threaded view
|

Re: EndOfStream unused

stephane ducasse
In reply to this post by Andreas.Raab
Hi andreas and nicolas

I like this discussion :)
Please discuss :)
Andreas did you push the bug to goran?

Stef

On 7 nov. 07, at 06:29, Andreas Raab wrote:

>  It was very interesting for me to find out that adding a progress  
> bar for downloads in Croquet would break SM for the very reason  
> that SM handles Exception instead of Error. Yes, that is a bug in  
> SM but it's just the kind of thing that you have to expect with  
> such low-level changes and which makes them as risky as they are.


12