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 |
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? |
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? > |
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 |
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 > > |
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 |
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 |
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 |
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. > |
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 |
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 |
> 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 |
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 |
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 |
> 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 |
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 |
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 " ... " |
> 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 |
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 > > |
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. |
Free forum by Nabble | Edit this page |