Defending my reputation, I’m here for people making things, not for being an educator. Thinking quickly and making it work even with mistakes can be a valid approach sometimes.
Matt On Saturday, March 17, 2018 at 2:05:55 PM UTC-5, Michael Jones wrote: > > these are excellent answers. > > i offer a harsher one:* the wrong answer faster is not optimization. *the > law of programming has correctness at its core--imagine reasoning about a > program where "if 5 < 7{stuff}" executed 50% of the time, or even 99.9999% > of the time. if it was faster, that simply would not matter. this is what > you cause then deny when you embrace race conditions of any kind, anywhere, > ever. it means that your work only kind of works, in some cases, at some > time, at the very best. > > it is the software equivalent of saying that cracked bridges have "not all > that many cracks." don't go there. not just for your reputation, but for > users and that of careful programmers. > > On Sat, Mar 17, 2018 at 9:40 AM, <thepud...@gmail.com <javascript:>> > wrote: > >> Hi all, >> >> In this particular case, this is a toy example of course, but for this >> toy example, it is absolutely a case where the performance of the >> synchronization primitive literally does not matter at all. (If I followed >> here, the intent is seemingly to watch a long running task, and the example >> has a 500ms sleep, etc.). >> >> That said, sometimes performance does matter. >> >> If instead this was a DIFFERENT example that was instead in some tight >> performance critical inner loop, the performance of the synchronization >> primitive for a stop flag can start to be meaningful (which of course >> should first be demonstrated by your benchmarking and/or your profiling of >> your particular program -- "premature optimization is the root of all evil" >> and all that). >> >> Empirically speaking, that is then a situation where I've seen people >> start to get into discussion around "well on amd64 you can rely on X and Y >> so we can get away with a stop flag that doesn't use a mutex or an atomic", >> and then people start talking about benign data races, and then other >> people start talking about "there are no benign data races", and then >> there's the reference to Dmitry Vyukov's article on benign data races, etc.: >> >> >> https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong >> >> To be honest I've seen it take up a fair amount of energy just to >> discuss, and again empirically speaking I've seen very smart people make >> mistakes here. >> >> One question I have regarding using an atomic for a stop flag is whether >> or not there is material performance overhead compared to a simple >> unprotected/non-atomic stop flag. >> >> I looked at that question a bit circa go ~1.7, so possibly stale info >> here, and I haven't gone back to look at my more detailed notes, but if I >> recall correctly I think my conclusion at the time was: >> >> 1. If your use case is a stop flag that will be checked many times (say, >> in a tight inner loop), and the stop flag only gets set rarely, then the >> performance of the set doesn't matter much, the assembly emitted for an >> atomic load (such as atomic.LoadUint64) seems to be identical for the >> assembly emitted for a simple load (say, *myUnint64Ptr) based on some spot >> checking a while ago (with a sample of assembly from 1.9 pasted in at the >> bottom of this post for comparison purposes). >> >> 2. Basic micro benchmarks didn't show a difference between an atomic load >> and an unprotected load for a stop flag. >> >> 3. There might be some theoretical possible overhead due to the go >> compiler will do instruction reordering in general, but won't do >> instruction reordering across function calls, so that could be one >> difference that might or might not make a difference depending on your >> exact code (though likely modest impact)? Regarding this last point, I'm >> just an interested spectator when it comes to the go compiler, so just to >> be clear I don't really know this definitively. >> >> At least for us at the time, the quick test program & comparison of >> assembly was enough to move us past the whole "Well, on amd64 you can get >> away with X" discussion, so I didn't delve too much deeper than that at the >> time. >> >> In any event, sharing this sample assembly with the community in case >> anyone is interested and/or has additional commentary on the particulars >> here in terms of performance impact in go of using an atomic load vs an >> unprotected stop flag for the (admittedly somewhat rare) cases when the >> nanoseconds do indeed matter. (And for me, a clean report from the race >> detector trumps the performance arguments, but that doesn't mean I'm not >> curious about the performance...). >> >> Here is a simple test program: >> >> https://play.golang.org/p/PaCQwb5m9ag >> >> func simpleLoadUint64(inputPtr *uint64) uint64 { >> // normal load of *inputPtr >> return *inputPtr >> } >> >> func atomicLoadUint64(inputPtr *uint64) uint64 { >> // atomic.LoadUint64 atomically loads *inputPtr >> return atomic.LoadUint64(inputPtr) >> } >> >> And here is the corresponding assembly snippets (from go 1.9): >> >> go build -gcflags -S atomic_vs_normal_load.go >> >> // trivial function with an unprotected load >> >> "".simpleLoadUint64 STEXT nosplit size=14 args=0x10 locals=0x0 >> 0x0000 00000 (atomic_vs_normal_load.go:8) TEXT >> "".simpleLoadUint64(SB), NOSPLIT, $0-16 >> 0x0000 00000 (atomic_vs_normal_load.go:8) FUNCDATA $0, >> gclocals·aef1f7ba6e2630c93a51843d99f5a28a(SB) >> 0x0000 00000 (atomic_vs_normal_load.go:8) FUNCDATA $1, >> gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) >> 0x0000 00000 (atomic_vs_normal_load.go:8) MOVQ >> "".inputPtr+8(SP), AX >> 0x0005 00005 (atomic_vs_normal_load.go:10) MOVQ (AX), AX >> 0x0008 00008 (atomic_vs_normal_load.go:10) MOVQ AX, >> "".~r1+16(SP) >> 0x000d 00013 (atomic_vs_normal_load.go:10) RET >> 0x0000 48 8b 44 24 08 48 8b 00 48 89 44 24 10 c3 >> H.D$.H..H.D$.. >> >> // trivial function with a sync/atomic LoadUint64: >> >> "".atomicLoadUint64 STEXT nosplit size=14 args=0x10 locals=0x0 >> 0x0000 00000 (atomic_vs_normal_load.go:13) TEXT >> "".atomicLoadUint64(SB), NOSPLIT, $0-16 >> 0x0000 00000 (atomic_vs_normal_load.go:13) FUNCDATA $0, >> gclocals·aef1f7ba6e2630c93a51843d99f5a28a(SB) >> 0x0000 00000 (atomic_vs_normal_load.go:13) FUNCDATA $1, >> gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) >> 0x0000 00000 (atomic_vs_normal_load.go:13) MOVQ >> "".inputPtr+8(SP), AX >> 0x0005 00005 (atomic_vs_normal_load.go:15) MOVQ (AX), AX >> 0x0008 00008 (atomic_vs_normal_load.go:15) MOVQ AX, >> "".~r1+16(SP) >> 0x000d 00013 (atomic_vs_normal_load.go:15) RET >> 0x0000 48 8b 44 24 08 48 8b 00 48 89 44 24 10 c3 >> H.D$.H..H.D$.. >> --thepudds >> >> On Saturday, March 17, 2018 at 10:37:48 AM UTC-4, matthe...@gmail.com >> wrote: >> >>> I think the second example alternative given (playground link above) has >>>> a data race? >>> >>> >>> I’m not surprised that the race detector sees something (a read can >>> happen during a write of the checked bool) but I don’t think this could >>> actually cause problems because the var’s memory value will always be 0 or >>> 1. >>> >>> There may be implementation details or future implementation details >>> that cause a problem though, so one option could be to protect the bool as >>> a shared resource with a mutex or equivalent, but I think rog’s solution is >>> better anyway (the first one). >>> >>> They all involve either repeatedly checking on a timer or checking the >>>> value of another field (like polling) to see whether the long running task >>>> should be stopped. >>> >>> >>> Right, now every iteration of the loop has more work added. >>> >>> Using os.Cmd may be an option, where you can call Kill on the separate >>> process. >>> >>> Matt >>> >>> On Saturday, March 17, 2018 at 8:01:33 AM UTC-5, thepud...@gmail.com >>> wrote: >>>> >>>> *> "Here's another way: https://play.golang.org/p/gEDef3LolAZ >>>>> <https://play.golang.org/p/gEDef3LolAZ> "* >>>> >>>> >>>> Hi all, >>>> >>>> I think the second example alternative given (playground link above) >>>> has a data race? >>>> >>>> Sample race detector run just now. (The two reports are inverses of >>>> each other: read then write vs. write then read). >>>> >>>> ----------------------------------------------------------------------- >>>> go run -race stop_flag_from_gonuts.go >>>> >>>> . . . . . quit sending ... >>>> after quit sent================== >>>> . >>>> WARNING: DATA RACE >>>> Write at 0x00c042072000 by goroutine 8: >>>> main.f.func1() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:13 +0x59 >>>> >>>> Previous read at 0x00c042072000 by goroutine 6: >>>> main.f() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:17 +0x102 >>>> >>>> Goroutine 8 (running) created at: >>>> main.f() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:11 +0x8a >>>> >>>> Goroutine 6 (running) created at: >>>> main.main() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:28 +0x70 >>>> ================== >>>> ================== >>>> WARNING: DATA RACE >>>> Read at 0x00c042072000 by goroutine 6: >>>> main.f() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:17 +0x102 >>>> >>>> Previous write at 0x00c042072000 by goroutine 8: >>>> main.f.func1() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:13 +0x59 >>>> >>>> Goroutine 6 (running) created at: >>>> main.main() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:28 +0x70 >>>> >>>> Goroutine 8 (finished) created at: >>>> main.f() >>>> C:/cygwin64/bin/gonuts/stop_flag_from_gonuts.go:11 +0x8a >>>> ================== >>>> quit called >>>> Found 2 data race(s) >>>> exit status 66 >>>> ----------------------------------------------------------------------- >>>> >>>> --thepudds >>>> >>>> On Friday, March 16, 2018 at 11:04:38 AM UTC-4, matthe...@gmail.com >>>> wrote: >>>>> >>>>> While this is running, your select won't be receiving on the quit >>>>>> channel, even if it is non-nil. >>>>>> If you want to be able to cancel it, you'll need to make the code in >>>>>> the loop responsive to the quit channel >>>>>> (for example, by using a select like you're using in f already). >>>>> >>>>> >>>>> The default select case does it: https://play.golang.org/p/jlfaXu6TZ8L >>>>> >>>>> Here's another way: https://play.golang.org/p/gEDef3LolAZ >>>>> >>>>> Matt >>>>> >>>>> On Friday, March 16, 2018 at 9:45:00 AM UTC-5, Sathish VJ wrote: >>>>>> >>>>>> All the examples I've seen use some kind of ticker to run various >>>>>> cases of a select statement. But how does one run a long running task >>>>>> that >>>>>> is still cancelable? >>>>>> >>>>>> >>>>>> In the example below the quit part is never reached. >>>>>> >>>>>> https://play.golang.org/p/PLGwrUvKaqn (it does not run properly on >>>>>> play.golang.org). >>>>>> >>>>>> package main >>>>>> >>>>>> >>>>>> import ( >>>>>> "fmt" >>>>>> "os" >>>>>> "time" >>>>>> ) >>>>>> >>>>>> >>>>>> func f(quit chan bool) { >>>>>> for { >>>>>> select { >>>>>> case <-time.After(0 * time.Second): >>>>>> // start long running task immediately. >>>>>> for { >>>>>> time.Sleep(500 * time.Millisecond) >>>>>> fmt.Printf(". ") >>>>>> } >>>>>> case <-quit: >>>>>> fmt.Println("quit called") >>>>>> //deallocate resources in other long running task and then >>>>>> return from function. >>>>>> os.Exit(0) // or return >>>>>> } >>>>>> } >>>>>> } >>>>>> >>>>>> >>>>>> func main() { >>>>>> var quit chan bool >>>>>> go f(quit) >>>>>> >>>>>> >>>>>> println("quit sending ... ") >>>>>> quit <- true >>>>>> println("after quit sent") >>>>>> >>>>>> >>>>>> var i chan int >>>>>> <-i >>>>>> } >>>>>> >>>>>> >>>>>> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com <javascript:>. >> For more options, visit https://groups.google.com/d/optout. >> > > > > -- > Michael T. Jones > michae...@gmail.com <javascript:> > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.