Re: [go-nuts] [generics] Print[T Stringer](s []T) vs Print(s []Stringer)

2020-12-27 Thread 'Axel Wagner' via golang-nuts
@Arnaud: I tend to agree that it's a downside of having two ways to do
things. However, I don't know how we would get the actually additive
expressive power of generics without that downside. If you have any ideas,
that would be great.

@Robert: I think sweeping statements about the performance of generic code
vs. non-generic code are premature. In particular, in a situation where
*either* generics *or* interfaces can be used for the same effect, I think
the goal should be for both to perform equivalently. In those cases, there
is nothing preventing the compiler from doing exactly the same
optimizations - that is, nothing in the language prescribing that an
interface needs to be passed and used as a boxed value, instead of
devirtualizing the function to operate on each concrete type it is called
with. In fact, AIUI, go 1.16 will already be significantly more clever
about doing that optimization.
Of course there *are* cases where it's not possible to devirtualize a
function. But in those cases, generics will run into similar problems.

I think it remains to be seen, if and when there is a performance benefit
and how large it is. Personally, *because* I would prefer us to continue to
favor interface-code where it can be used and only reach for generics if
they are necessary, I would very much like them to perform similarly, so
people don't use generics just "because they're faster".

On Sun, Dec 27, 2020 at 6:54 PM K. Alex Mills 
wrote:

> Makes sense. I just attempted to quantify the overhead and it looks like
> it's at least 2x on my machine, which is not as trivial a performance bump
> as I had imagined, so thanks for the push back.
>
> There are clearly some uses for which this pattern is going to be
> desirable for the performance benefits of monomorphization.
>
> Benchmark I used is included below, for completeness. It only quantifies
> the overhead of interface dispatch, which I think is the salient point. It
> may be worth noting that I don't have a go2 compiler locally and the
> playground doesn't support benchmarks. OTOH, this performance may be closer
> to what we might expect from go2 in case the current prototype isn't (yet)
> as optimized as it could be.
>
> package main
>
> import (
>   "strconv"
>   "testing"
> )
>
> func BenchmarkNonGenericScalar(b *testing.B) {
>   x := myInt(5341)
>   for i := 0; i < b.N; i++ {
> nonGeneric(x)
>   }
> }
>
> func BenchmarkGenericScalar(b *testing.B) {
>   x := myInt(5341)
>   for i := 0; i < b.N; i++ {
> generic(x)
>   }
> }
>
> type Stringer interface {
>   String() string
> }
>
> func nonGeneric(x Stringer) string {
>   return x.String()
> }
>
> // my assumption here is this will output monomorphized binary code
> roughly equivalent to
> // what the generics implementation would output; that assumption may be
> wrong.
> func generic(x myInt) string {
>   return x.String()
> }
>
> type myInt int
>
> func (i myInt) String() string {
>   return strconv.Itoa(int(i))
> }
>
> BenchmarkNonGenericScalar
> BenchmarkNonGenericScalar-24
> 21053407  55.3 ns/op16 B/op2 allocs/op
> BenchmarkGenericScalar
> BenchmarkGenericScalar-24
> 52173685  23.7 ns/op 4 B/op1 allocs/op
> PASS
>
> On Sun, Dec 27, 2020 at 11:35 AM robert engels 
> wrote:
>
>> That is not true. The generic version can have significant performance
>> implications for low-level/tight-loop functions as it will avoid the
>> indirection required for interface method dispatching (at the expensive of
>> code explosion for common calls with lots of types).
>>
>> On Dec 27, 2020, at 11:31 AM, K. Alex Mills 
>> wrote:
>>
>> Since in this case the use of generics doesn't let you do anything new, I
>> would argue that the KISS principle applies and the non-generic version
>> should be preferred.
>>
>> I think a linter can be added to go vet to warn about instances like this
>> one (where the type parameter can be replaced by the type bound) to
>> encourage simplicity.
>>
>> But as Ian pointed out, in the version that takes a slice argument, using
>> generics does allow you to do something you couldn't do otherwise (without
>> reallocating the contents of the slice to effect a "cast" to []Stringer).
>> In this case using generics actually makes the caller's job simpler and
>> improves performance by avoiding the need for reallocation.
>>
>> On Sun, Dec 27, 2020 at 4:23 AM Arnaud Delobelle 
>> wrote:
>>
>>> In my opinion, the issue is that there are two ways to express (almost)
>>> the same thing and that in itself creates friction in the language.
>>>
>>> There may be a valid reason to choose one version over the other, but
>>> every time it will be necessary to review the pros and cons of each
>>> alternative.
>>>
>>> If we could have "generics" without having to make this choice it would
>>> unblock the whole thing as far as I am concerned.
>>>
>>> Cheers
>>>
>>> On Sun, 27 Dec 2020, 05:25 K. Alex Mills, 
>>> wrote:
>>>
 While it depends on the final gen

Re: [go-nuts] [generics] Print[T Stringer](s []T) vs Print(s []Stringer)

2020-12-27 Thread K. Alex Mills
Makes sense. I just attempted to quantify the overhead and it looks like
it's at least 2x on my machine, which is not as trivial a performance bump
as I had imagined, so thanks for the push back.

There are clearly some uses for which this pattern is going to be desirable
for the performance benefits of monomorphization.

Benchmark I used is included below, for completeness. It only quantifies
the overhead of interface dispatch, which I think is the salient point. It
may be worth noting that I don't have a go2 compiler locally and the
playground doesn't support benchmarks. OTOH, this performance may be closer
to what we might expect from go2 in case the current prototype isn't (yet)
as optimized as it could be.

package main

import (
  "strconv"
  "testing"
)

func BenchmarkNonGenericScalar(b *testing.B) {
  x := myInt(5341)
  for i := 0; i < b.N; i++ {
nonGeneric(x)
  }
}

func BenchmarkGenericScalar(b *testing.B) {
  x := myInt(5341)
  for i := 0; i < b.N; i++ {
generic(x)
  }
}

type Stringer interface {
  String() string
}

func nonGeneric(x Stringer) string {
  return x.String()
}

// my assumption here is this will output monomorphized binary code roughly
equivalent to
// what the generics implementation would output; that assumption may be
wrong.
func generic(x myInt) string {
  return x.String()
}

type myInt int

func (i myInt) String() string {
  return strconv.Itoa(int(i))
}

BenchmarkNonGenericScalar
BenchmarkNonGenericScalar-24
21053407  55.3 ns/op16 B/op2 allocs/op
BenchmarkGenericScalar
BenchmarkGenericScalar-24
52173685  23.7 ns/op 4 B/op1 allocs/op
PASS

On Sun, Dec 27, 2020 at 11:35 AM robert engels 
wrote:

> That is not true. The generic version can have significant performance
> implications for low-level/tight-loop functions as it will avoid the
> indirection required for interface method dispatching (at the expensive of
> code explosion for common calls with lots of types).
>
> On Dec 27, 2020, at 11:31 AM, K. Alex Mills 
> wrote:
>
> Since in this case the use of generics doesn't let you do anything new, I
> would argue that the KISS principle applies and the non-generic version
> should be preferred.
>
> I think a linter can be added to go vet to warn about instances like this
> one (where the type parameter can be replaced by the type bound) to
> encourage simplicity.
>
> But as Ian pointed out, in the version that takes a slice argument, using
> generics does allow you to do something you couldn't do otherwise (without
> reallocating the contents of the slice to effect a "cast" to []Stringer).
> In this case using generics actually makes the caller's job simpler and
> improves performance by avoiding the need for reallocation.
>
> On Sun, Dec 27, 2020 at 4:23 AM Arnaud Delobelle 
> wrote:
>
>> In my opinion, the issue is that there are two ways to express (almost)
>> the same thing and that in itself creates friction in the language.
>>
>> There may be a valid reason to choose one version over the other, but
>> every time it will be necessary to review the pros and cons of each
>> alternative.
>>
>> If we could have "generics" without having to make this choice it would
>> unblock the whole thing as far as I am concerned.
>>
>> Cheers
>>
>> On Sun, 27 Dec 2020, 05:25 K. Alex Mills,  wrote:
>>
>>> While it depends on the final generics implementation, my understanding
>>> of how things stand now is that Print would compile down to a separate
>>> chunk of binary for each type T that is used. For instance, if you used
>>> Print[A] and Print[B] in your code, they would each refer to separate
>>> binary implementations in which T is replaced by A and B, respectively.
>>>
>>> Printi does not do this, so you should see a smaller binary.
>>>
>>> IIRC, Printi also has to do a bit of work to lookup the Stringer method
>>> on the type inhabiting the interface. I don't think that creates a
>>> significant performance hit, but I might be understating the overhead of
>>> interface dispatch. A benchmark would help here (alas, I am on my phone).
>>>
>>> With respect for the concerns mentioned above, I don't see an argument
>>> for preferring Print over Printi. However, there may other concerns which I
>>> am unaware of.
>>>
>>> On Sat, Dec 26, 2020, 9:58 PM Elliot  wrote:
>>>
 If we remove slice from OP's example:

 https://go2goplay.golang.org/p/KSJpRw1Lrmm

 func Print[T Stringer](s T) {
 fmt.Print(s.String())
 }

 func Printi(s Stringer) {
 fmt.Print(s.String())
 }

 Are these two equivalent? When should one be chosen over the other?

 On Thursday, 24 December 2020 at 04:41:16 UTC+8 Henrik Johansson wrote:

> Why will interfaces be more idiomatic once generics lands? It remains
> to be seen I guess but I could very well see the other way become the 
> idiom.
>
> On Wed, 23 Dec 2020, 21:20 wilk,  wrote:
>
>> On 23-12-2020, Ian Lance Taylor

Re: [go-nuts] [generics] Print[T Stringer](s []T) vs Print(s []Stringer)

2020-12-27 Thread robert engels
That is not true. The generic version can have significant performance 
implications for low-level/tight-loop functions as it will avoid the 
indirection required for interface method dispatching (at the expensive of code 
explosion for common calls with lots of types).

> On Dec 27, 2020, at 11:31 AM, K. Alex Mills  wrote:
> 
> Since in this case the use of generics doesn't let you do anything new, I 
> would argue that the KISS principle applies and the non-generic version 
> should be preferred.
> 
> I think a linter can be added to go vet to warn about instances like this one 
> (where the type parameter can be replaced by the type bound) to encourage 
> simplicity.
> 
> But as Ian pointed out, in the version that takes a slice argument, using 
> generics does allow you to do something you couldn't do otherwise (without 
> reallocating the contents of the slice to effect a "cast" to []Stringer). In 
> this case using generics actually makes the caller's job simpler and improves 
> performance by avoiding the need for reallocation.
> 
> On Sun, Dec 27, 2020 at 4:23 AM Arnaud Delobelle  > wrote:
> In my opinion, the issue is that there are two ways to express (almost) the 
> same thing and that in itself creates friction in the language.
> 
> There may be a valid reason to choose one version over the other, but every 
> time it will be necessary to review the pros and cons of each alternative.
> 
> If we could have "generics" without having to make this choice it would 
> unblock the whole thing as far as I am concerned.
> 
> Cheers
> 
> On Sun, 27 Dec 2020, 05:25 K. Alex Mills,  > wrote:
> While it depends on the final generics implementation, my understanding of 
> how things stand now is that Print would compile down to a separate chunk of 
> binary for each type T that is used. For instance, if you used Print[A] and 
> Print[B] in your code, they would each refer to separate binary 
> implementations in which T is replaced by A and B, respectively.
> 
> Printi does not do this, so you should see a smaller binary. 
> 
> IIRC, Printi also has to do a bit of work to lookup the Stringer method on 
> the type inhabiting the interface. I don't think that creates a significant 
> performance hit, but I might be understating the overhead of interface 
> dispatch. A benchmark would help here (alas, I am on my phone).
> 
> With respect for the concerns mentioned above, I don't see an argument for 
> preferring Print over Printi. However, there may other concerns which I am 
> unaware of.
> 
> On Sat, Dec 26, 2020, 9:58 PM Elliot  > wrote:
> If we remove slice from OP's example:
> 
> https://go2goplay.golang.org/p/KSJpRw1Lrmm 
> 
> 
> func Print[T Stringer](s T) {
> fmt.Print(s.String())
> }
> 
> func Printi(s Stringer) {
> fmt.Print(s.String())
> }
> 
> Are these two equivalent? When should one be chosen over the other?
> 
> On Thursday, 24 December 2020 at 04:41:16 UTC+8 Henrik Johansson wrote:
> Why will interfaces be more idiomatic once generics lands? It remains to be 
> seen I guess but I could very well see the other way become the idiom.
> 
> On Wed, 23 Dec 2020, 21:20 wilk,  > wrote:
> On 23-12-2020, Ian Lance Taylor wrote:
> > On Wed, Dec 23, 2020 at 9:54 AM wilk  > > wrote:
> >>
> >> https://go2goplay.golang.org/p/fTW3hJYNgfU 
> >> 
> >>
> >> type Stringer interface {
> >>String() string
> >> }
> >>
> >> Print[T Stringer](s []T)
> >>
> >> Print(s []Stringer)
> >>
> >> Both forms works.
> >> How to prevent double way to do the same things that can be confusing ?
> >
> > Both forms work but they mean two different things.
> >
> > Print(s []Stringer) takes a slice of the type Stringer.
> >
> > Print[T Stringer](s []T) takes a slice of some type T, where T
> > implements Stringer.
> >
> > For example, if MyInt implements Stringer, and I have a []MyInt, then
> > I can call Print[T Stringer](s []T) but I can't call Print(s
> > []Stringer), because a []Stringer is not a []MyInt.
> 
> I understand the differences. But i'm affraid someone who never used
> Go before will use type parameters instead of interface which is more
> idiomatic i think.
> I mean it will be difficult to say, you could use type parameters but
> you should use interface, or something like that...
> I'm speaking about ease of learn Go2.
> 
> -- 
> wilk
> 
> -- 
> 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/rs08pp%24p8m%241%40ciao.gmane.io
>  
> 

Re: [go-nuts] [generics] Print[T Stringer](s []T) vs Print(s []Stringer)

2020-12-23 Thread Sebastien Binet
to illustrate what Ian wrote, with your example:

https://go2goplay.golang.org/p/YTqF-WS0m6O

func main() {
var ps = []*Person{
&Person{"Arthur Dent", 42},
&Person{"Zaphod Beeblebrox", 9001},
}
Print(ps)  // ok.
Printi(ps) // compilation error.
}

‐‐‐ Original Message ‐‐‐

On Wednesday, December 23rd, 2020 at 7:02 PM, Ian Lance Taylor 
 wrote:

> On Wed, Dec 23, 2020 at 9:54 AM wilk w...@flibuste.net wrote:
>
> > https://go2goplay.golang.org/p/fTW3hJYNgfU
> >
> > type Stringer interface {
> >
> > String() string
> >
> > }
> >
> > Print[T Stringer](s []T)
> >
> > Print(s []Stringer)
> >
> > Both forms works.
> >
> > How to prevent double way to do the same things that can be confusing ?
>
> Both forms work but they mean two different things.
>
> Print(s []Stringer) takes a slice of the type Stringer.
>
> Print[T Stringer](s []T) takes a slice of some type T, where T
>
> implements Stringer.
>
> For example, if MyInt implements Stringer, and I have a []MyInt, then
>
> I can call Print[T Stringer](s []T) but I can't call Print(s
>
> []Stringer), because a []Stringer is not a []MyInt.
>
> Ian
>
> 
>
> 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/CAOyqgcUALDVBWJwhHYxT6T5%3Dz2tvSKp7yMy%3DF4HSJc_uTZZKGQ%40mail.gmail.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/irhuBuJMF9HLWxoYcutqmZM8aZmKAhxUEX05kKplyqZ9N8wQtZNWtP41CfTJmXpmWGfo8qkEI-27xnYGykDbAcUlSyPrDskc2lB2Q5Pt-BA%3D%40sbinet.org.


Re: [go-nuts] [generics] Print[T Stringer](s []T) vs Print(s []Stringer)

2020-12-23 Thread Ian Lance Taylor
On Wed, Dec 23, 2020 at 9:54 AM wilk  wrote:
>
> https://go2goplay.golang.org/p/fTW3hJYNgfU
>
> type Stringer interface {
>String() string
> }
>
> Print[T Stringer](s []T)
>
> Print(s []Stringer)
>
> Both forms works.
> How to prevent double way to do the same things that can be confusing ?

Both forms work but they mean two different things.

Print(s []Stringer) takes a slice of the type Stringer.

Print[T Stringer](s []T) takes a slice of some type T, where T
implements Stringer.

For example, if MyInt implements Stringer, and I have a []MyInt, then
I can call Print[T Stringer](s []T) but I can't call Print(s
[]Stringer), because a []Stringer is not a []MyInt.

Ian

-- 
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/CAOyqgcUALDVBWJwhHYxT6T5%3Dz2tvSKp7yMy%3DF4HSJc_uTZZKGQ%40mail.gmail.com.


[go-nuts] [generics] Print[T Stringer](s []T) vs Print(s []Stringer)

2020-12-23 Thread wilk
Hi,

https://go2goplay.golang.org/p/fTW3hJYNgfU

type Stringer interface {
   String() string
}

Print[T Stringer](s []T) 

Print(s []Stringer)

Both forms works.
How to prevent double way to do the same things that can be confusing ?

-- 
wilk

-- 
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/rs0077%24vd9%241%40ciao.gmane.io.