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, <[email protected] <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, [email protected]
>> 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, [email protected]
>>> 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, [email protected]
>>>> 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 [email protected] <javascript:>.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
>
> --
> Michael T. Jones
> [email protected] <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 [email protected].
For more options, visit https://groups.google.com/d/optout.