I did, but couldn't infer my answer out of the assembly. I see the
following below, but it's not clear if the call is made, or if the linker
does some optimization after the compiler step. See below:

"".T1.Do STEXT nosplit size=1 args=0x8 locals=0x0
0x0000 00000 (check-interface-noop-call.go:15) TEXT "".T1.Do(SB), NOSPLIT,
$0-8
0x0000 00000 (check-interface-noop-call.go:15) FUNCDATA $0,
gclocals·2a5305abe05176240e61b8620e19a815(SB)
0x0000 00000 (check-interface-noop-call.go:15) FUNCDATA $1,
gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (check-interface-noop-call.go:15) RET
0x0000 c3                                               .
"".main STEXT size=84 args=0x0 locals=0x18
0x0000 00000 (check-interface-noop-call.go:19) TEXT "".main(SB), $24-0
0x0000 00000 (check-interface-noop-call.go:19) MOVQ (TLS), CX
0x0009 00009 (check-interface-noop-call.go:19) CMPQ SP, 16(CX)
0x000d 00013 (check-interface-noop-call.go:19) JLS 77
0x000f 00015 (check-interface-noop-call.go:19) SUBQ $24, SP
0x0013 00019 (check-interface-noop-call.go:19) MOVQ BP, 16(SP)
0x0018 00024 (check-interface-noop-call.go:19) LEAQ 16(SP), BP
0x001d 00029 (check-interface-noop-call.go:19) FUNCDATA $0,
gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (check-interface-noop-call.go:19) FUNCDATA $1,
gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (check-interface-noop-call.go:22) LEAQ go.itab."".T1,"".I(SB),
AX
0x0024 00036 (check-interface-noop-call.go:22) TESTB AL, (AX)
0x0026 00038 (check-interface-noop-call.go:22) MOVQ go.itab."".T1,"".I+24(SB),
AX
0x002d 00045 (check-interface-noop-call.go:22) MOVQ $1, 8(SP)
0x0036 00054 (check-interface-noop-call.go:22) LEAQ runtime.zerobase(SB), CX
0x003d 00061 (check-interface-noop-call.go:22) MOVQ CX, (SP)
0x0041 00065 (check-interface-noop-call.go:22) PCDATA $0, $0
0x0041 00065 (check-interface-noop-call.go:22) CALL AX
0x0043 00067 (check-interface-noop-call.go:26) MOVQ 16(SP), BP
0x0048 00072 (check-interface-noop-call.go:26) ADDQ $24, SP
0x004c 00076 (check-interface-noop-call.go:26) RET
0x004d 00077 (check-interface-noop-call.go:26) NOP
0x004d 00077 (check-interface-noop-call.go:19) PCDATA $0, $-1
0x004d 00077 (check-interface-noop-call.go:19) CALL
runtime.morestack_noctxt(SB)
0x0052 00082 (check-interface-noop-call.go:19) JMP 0


On Fri, Dec 1, 2017 at 1:59 PM, Michael Banzon <mich...@banzon.dk> wrote:

> I don’t have specific knowledge about how this works - but wouldn’t it be
> fairly easy to test by having the compiler emit the assembly code?
>
> --
> Michael Banzon
> https://michaelbanzon.com/
>
>
>
>
> Den 1. dec. 2017 kl. 19.42 skrev Ugorji Nwoke <ugo...@gmail.com>:
>
> Thanks Ian.
>
> This is not so much about de-virtualization. It's about not making a call
> to an empty function i.e. I expect that interface calls come down to
> 1. dereference the pointer to the i-tab,
> 2. jump to the appropriate function in the i-tab,
> 3. call that function.
>
> If that function is a no-op, is the runtime smart enough to skip that
> function call completely (e.g. replacing the entry in the i-tab with a
> NOP)? That way, making an interface call to an empty function becomes just
> a dereference+jump, and *not* dereference+jump+(unnecessary)func-call ?
>
> Currently, in my code, within a tight loop, i have a lot of:
> for {
>     if v.HasSeparators() { v.ReadMapElem() }
>     ...
>     if v.HasSeparators() { v.ReadMapEnd() }
>     ...
> }
>
> If the func call is elided, then I will just call the empty function all
> the time and remove the if/conditional checks. The code will become:
> for {
>     v.ReadMapElem()
>     ...
>     v.ReadMapEnd()
>     ...
> }
>
>
> On Friday, December 1, 2017 at 10:18:38 AM UTC-5, Ian Lance Taylor wrote:
>>
>> On Fri, Dec 1, 2017 at 2:40 AM, Ugorji Nwoke <ugo...@gmail.com> wrote:
>> >
>> > I know that a no-op function call is optimized away, as it is inlined
>> to
>> > nothing.
>> >
>> > However, what about a no-op interface call?
>> >
>> > See sample code:
>> >
>> > type I interface { Do(int) }
>> > type T1 struct{}
>> > func (_ T1) Do(i int) {}
>> > func main() {
>> > var v I
>> > v = T1{}
>> > v.Do(1)
>> > }
>> >
>> > Is it safe to assume the following that calling T1.Do(...) via an
>> interface
>> > costs a dereference + jump (but NO function call overhead)?
>> >
>> > I currently have code where I do a lot of conditional checks to
>> determine
>> > whether to make that interface call or not.
>> > However, if I know that the no function call overhead is done when the
>> > dynamic function is a no-op, then I will just call the interface
>> > method all the time, and not try to be smart within code.
>> >
>> > I tried looking at "go tool compile -S main.go" output, but I still see
>> a
>> > CALL in there, but don't know whether the linker/compiler/something
>> > optimizes this out.
>>
>> In general, no.  The compiler doesn't currently try to figure out the
>> dynamic type of an interface (an optimization known as
>> devirtualization).  An interface method can only be inlined when the
>> dynamic type is known.
>>
>> The cost of calling an empty method is quite low.  It's not zero, but
>> I am skeptical that the cost of the call is more than the cost of the
>> conditional checks you mention.
>>
>> 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.
> For more options, visit https://groups.google.com/d/optout.
>
>
>

-- 
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.

Reply via email to