Overhead of Type Assertions - High or Low?

瀏覽次數:734 次
跳到第一則未讀訊息

Will Walthall

未讀,
2010年3月30日 清晨7:13:562010/3/30
收件者:golan...@googlegroups.com
I'm just wondering how cheap or expensive Type Assertions are. I find
myself using this code structure quite often.

type Food interface {
CanEatWith(interface {}) interface {}
}

type Tacos struct {
percentBeans int
percentHotSauce int
}

type Water struct {
volume int
quality int
}

type Meal struct {
iterms []interface {}
}

func (w *Water) CanEatWith(food interface {}) (interface {}) {
if food == nil {
return "Nil isn't edible"
}
switch x := food.(type) {
case Tacos:
// do some processing
return Meal{items:{x, w}}
case Meal:
// do some processing
return Meal{items:{x, w}}
default:
return "Unknown Arg"
}
}

Repeating that sort of structure for Tacos and also for processing the
return val of CanEatWIth().

It is simulating an overloaded function. I know it isn't going to
perform like an overloaded function, but is the overhead high enough
that I should avoid it during time critical code? I can't use the
if/else form because, x := food, this statement sets x's type to
interface since food was passed as and empty interface.

I am using this sequence incredibly often in my own packages, usually
combined with (chan interfaces {})'s as the input/output medium.

-Will

Will

未讀,
2010年3月30日 上午8:00:532010/3/30
收件者:golang-nuts
Well I realized that after I had posted that I have been writing my
package code all night and was only checking for compiling errors. It
dawned on me literally secs after posting that x := food.(type) would
evaluate as an interface in the type switch as well so I have to use a
if/else structure to discover the type that food is. And I was
getting so attached to the beautiful code that I was producing.

I guess I'll enum all my types as I would in other languages,
resulting in a code structure that switch's on enum values accessed
through a generic interface.

type TypeId interface {
Type() uint
}

type TypeId_ struct {
Id uint
}

func(t *TypeId_) Type() uint {
return t.Id
}

const (
IdTacos = iota;
IdWater;
)

type Tacos struct {
TypeId_
}

func generic(arg interface {}) interface {} {
x := arg.(TypeId)
switch x.Type() {
case IdTacos:
tacos := x.(Tacos)
//processing
return tacos
...etc

2 assertions, I'm happy with that.

roger peppe

未讀,
2010年3月30日 上午8:26:412010/3/30
收件者:Will Walthall、golan...@googlegroups.com
On 30 March 2010 11:13, Will Walthall <ght...@gmail.com> wrote:
> I'm just wondering how cheap or expensive Type Assertions are.  I find
> myself using this code structure quite often.

a type assertion against a non-interface type is essentially one
pointer comparison; currently it's implemented in C and uses
three function calls. it'll probably be faster in the future, but
the cost is pretty small now.
see src/pkg/runtime/^·ifaceE2T for details.

against an interface, the first time between two types will be slower,
but subsequent tests won't be that many instructions.
see src/pkg/runtime/iface.c:/^itab

> func (w *Water) CanEatWith(food interface {}) (interface {}) {
>  if food == nil {
>  return "Nil isn't edible"
>  }

this can go inside the switch, as below

>  switch x := food.(type) {

case nil:


return "Nil isn't edible"

>  case Tacos:


>  // do some processing
>   return Meal{items:{x, w}}
>  case Meal:
>   // do some processing
>   return Meal{items:{x, w}}
>  default:
>   return "Unknown Arg"
>  }
> }

[...]


> I can't use the
> if/else form because, x := food, this statement sets x's type to
> interface since food was passed as and empty interface.

i don't understand this.

Will Walthall

未讀,
2010年3月30日 上午9:55:072010/3/30
收件者:roger peppe、golan...@googlegroups.com
>
> i don't understand this.
>

Here is my working code example, using if statements isn't as pretty.
Still its quite nice and I almost perfer it to using overloaded
functions. The code is compact and concise.

package comm

import (
//"fmt"
i "ghthor/init"
//"runtime"
)

// (type)Init structs Allow for Simplified Construction
// especially with complex embedded types
type CommInit struct {
BufferLen int
}

type ComplexCommInit struct {
CommInit
NumChan int
}

// Defines a channel with embedded Buffer Length for easy cloning of an instance
type Comm struct {
i.InitVar
comm chan interface {}
}

// Constructor
func (c *Comm) Init(arg interface {}) (interface {}) {
// Call to embedded Init func that Checks for nil and reassigns arg
to i.Default
arg = c.InitVar.Init(arg)
var initArg CommInit

// Allow for Weird combos of arg for initialization
if x, ok := arg.(CommInit); ok {
initArg = x
} else if x, ok := arg.(int); ok {
initArg = CommInit{BufferLen:x}
} else {
initArg = CommInit{BufferLen:0}
}

// Initialize c *Comm
c.comm = make(chan interface {}, initArg.BufferLen)
c.InitArg = &initArg
return c
}

// Clean-up (Destructor)
func (c *Comm) Dispose(disposeComplete chan string) {
//TODO: Maybe have this do some reporting, like if there are things
left on the channel when it got closed
close(c.comm)
}

// These 2 struct's and func's Make the code a little ezier to read
down the road
type CommIn struct {
in chan interface {}
}
type CommOut struct {
out chan interface {}
}

func (c *Comm) AsIn() (in *CommIn) {
in = new(CommIn)
in.in = c.comm
return
}

func (c *Comm) AsOut() (out *CommOut) {
out = new(CommOut)
out.out = c.comm
return
}

// Comm that has multiple channels of communication
type ComplexComm struct {
i.InitVar
comm []*Comm
}

// Constructor
func (cc *ComplexComm) Init(arg interface {}) (interface {}) {
// nil Check
arg = cc.InitVar.Init(arg)
var initArg ComplexCommInit
if x, ok := arg.(ComplexCommInit); ok {
initArg = x
} else if x, ok := arg.(int); ok {
initArg = ComplexCommInit{NumChan:x}
} else if x, ok := arg.(CommInit); ok {
initArg = ComplexCommInit{NumChan:2, CommInit:x}
} else {
initArg = ComplexCommInit{NumChan:2} // CommInit.BufferLen defaults to 0
}

/* What I thought was that the statement
case ComplextCommInit:
was taking the place of
if x, ok := arg.(ComplexCommInit); ok
But now I understand that
switch arg.(type)
is just querying arg's declared type which is interface {}
I misunderstood Type Switch, for Type Assertion Switch

switch arg.(type) {
case ComplexCommInit:
initArg = arg.(ComplexCommInit)
case int:
initArg.NumChan = arg.(int)
initArg.BufferLen = 0
case CommInit:
initArg.NumChan = 2
initArg.BufferLen = arg.(CommInit).BufferLen
default:
initArg.NumChan = 2
initArg.BufferLen = 0
}
*/
cc.comm = make([]*Comm, initArg.NumChan)
for i := 0; i < len(cc.comm); i++ {
cc.comm[i] = new(Comm).Init(initArg.CommInit).(*Comm)
}
cc.InitArg = &initArg
return cc
}

// Destructor
func (cc *ComplexComm) Dispose(disposeComplete chan string) {
for i := 0; i < len(cc.comm); i++ {
cc.comm[i].Dispose(disposeComplete)
}
}

roger peppe

未讀,
2010年3月30日 上午10:05:012010/3/30
收件者:Will Walthall、golan...@googlegroups.com
On 30 March 2010 13:55, Will Walthall <ght...@gmail.com> wrote:

> /*      What I thought was that the statement
>                case ComplextCommInit:
>        was taking the place of
>                if x, ok := arg.(ComplexCommInit); ok

it is!

>        But now I understand that
>                switch arg.(type)
>        is just querying arg's declared type which is interface {}
>        I misunderstood Type Switch, for Type Assertion Switch

no, i think you understood just fine.

switch x := x.(type) {
case Foo:
print("foo")
default:
print("other")
}

is exactly equivalent to this, AFAIK:

if x, ok := x.(Foo); ok {
print("foo")
}else{
print("other")
}

Will Walthall

未讀,
2010年3月30日 上午10:17:572010/3/30
收件者:roger peppe、golan...@googlegroups.com
Ok so type Assertions are quite cheap, awesome. I've been awake to
long I suppose because you are correct.
package main

import (


"fmt"
i "ghthor/init"
//"runtime"

//"ghthor/node"
)

type test struct {
i.InitVar
name string
}

func (t *test) Init(name interface {}) (interface {}) {
initVar := t.InitVar.Init(name)
switch initVar.(type) {
case *test:
t.name = name.(*test).name
case string:
t.name = initVar.(string)
default:
t.name = "Invalid Initialization Object"
}
t.InitArg = initVar
return t
}

func main() {
fmt.Printf("\n\n");

temp := new(test).Init("I've Been Awake to Long I guess").(*test)
taco := new(test).Init("Taco").(*test)
clone := new(test).Init(temp).(*test)

fmt.Println(temp.name, "\n", taco.name, "\n",clone.name)
}

:output:
I've Been Awake to Long I guess
Taco
I've Been Awake to Long I guess

回覆所有人
回覆作者
轉寄
0 則新訊息