On Wed, Oct 2, 2019 at 11:07 PM Travis Keep <kee...@gmail.com> wrote:
>
> I've been working on a math library github.com/keep94/gomath.  This library 
> has functions for generating prime numbers, ugly numbers, harshad numbers, 
> happy numbers etc.  Since there are infinitely many prime numbers, ugly 
> number, harshad numbers etc, these functions I wrote returned either a chan 
> int64 or a chan *big.Int. However I just recently rewrote all these functions 
> to return streams instead of channels.  By streams, I mean interfaces that 
> have a Next method that returns the next value in the stream. In this post I 
> explain why I made this change.
>
> First off there is overhead for callers with channels. When my functions 
> returned channels, they had to accept a Context since callers could never 
> exhaust the returned channels. Callers would have to remember to cancel the 
> context when they were done with the returned channel or else they would leak 
> a goroutine.
>
> Second using streams is about four times faster than using channels. I don't 
> know exactly why this is but I figure there is overhead involved in running a 
> goroutine to feed the returned channel as well as implicit locking that has 
> to be done when the channel is read from or written to.

It can be argued that you were misusing channels. Channels are
synchronization mechanisms between goroutines. They are not generic
data pipes. I think the operations you describe can be implemented as
a struct:

type PrimeGenerator struct {
   // internal state for generator
}

func (p *PrimeGenerator) Next() *big.Int {...}

If you want, you can still add:

type Generator interface {
  Next() *big.Int
}

>
> Third, streams are less taxing on the GC than channels.  A chan *big.Int has 
> to emit a newly allocated big.Int off the heap each time.  The Next method of 
> a *big.Int stream can accept a *big.Int from the caller and write the next 
> value to this caller supplied *big.Int instead of having to allocate a new 
> *big.Int each time.

Note that this can be dangerous. If the returned pointer is saved
somewhere else in the program as part of the internal state,
overwriting that instance of big.Int will also modify that state. You
get into issues about who owns data, which is never easy to solve.

>
> So at least for handling data structures handling an infinite number of 
> integers or *big.Int, using streams with a Next method is better than using 
> channels.
>
>
> --
> 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.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/golang-nuts/538038ca-d6b9-4f9c-a69f-510284fc3ab8%40googlegroups.com.

-- 
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.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAMV2Rqr57j9p%3DqBi3QHQmOLPq8zLTCCsjJyQYs3DzR__%2Bn_edA%40mail.gmail.com.

Reply via email to