Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Improve inlinability of libuv success checking #1059

Merged
merged 3 commits into from
Aug 26, 2016

Conversation

benaadams
Copy link
Contributor

@benaadams benaadams commented Aug 21, 2016

Reviewing #1058

Currently the throwing int Check(int statusCode) and the non throwing int Check(int statusCode, out Exception error) are non-inlinable functions with the second being fairly large.

As one of these is called on every libuv call it would be better to have them much trimmer and inlinable; especially for the common success case.

[FAILED: unprofitable inline] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):int:this
[FAILED: unprofitable inline] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int):int:this

This changes the throwing check to ThrowIfErrored, which is what it does, and drops the return type from both (which nothing does anything with) as well as delegating the rarely taken error path to other functions to make the two functions much smaller and inlinable.

Note: Need coreclr post dotnet/coreclr#6103 for the magic FAILED: does not return; though will still work similarly, just might also inline the throw call.

/cc @halter73 @CesarBS @davidfowl

@benaadams
Copy link
Contributor Author

benaadams commented Aug 21, 2016

This unprofitable inline

[FAILED: unprofitable inline] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int):int:this

Inlines into 06000088 Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int):int:this
  [0 IL=0004 TR=000005 06000089] [FAILED: unprofitable inline] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):int:this
Budget: initialTime=111, finalTime=111, initialBudget=1110, currentBudget=1110
Budget: initialSize=518, finalSize=518
; Assembly listing for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int):int:this
; Emitting BLENDED_CODE for X64 CPU with SSE2
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
;  V00 this         [V00,T01] (  3,   3  )     ref  ->  rcx         this
;  V01 arg1         [V01,T00] (  4,   4  )     int  ->  rsi        
;  V02 loc0         [V02    ] (  3,   2  )     ref  ->  [rsp+0x28]   do-not-enreg[X] must-init addr-exposed ld-addr-op
;  V03 OutArgs      [V03    ] (  1,   1  )  lclBlk (32) [rsp+0x00]  
;
; Lcl frame size = 48
G_M62863_IG01:
       push     rsi
       sub      rsp, 48
       xor      rax, rax
       mov      qword ptr [rsp+28H], rax
       mov      esi, edx
G_M62863_IG02:
       lea      r8, bword ptr [rsp+28H]
       mov      edx, esi
       call     [Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):int:this]
       mov      rax, gword ptr [rsp+28H]
       test     rax, rax
       jne      SHORT G_M62863_IG05
G_M62863_IG03:
       mov      eax, esi
G_M62863_IG04:
       add      rsp, 48
       pop      rsi
       ret      
G_M62863_IG05:
       mov      rcx, gword ptr [rsp+28H]
       call     CORINFO_HELP_THROW
       int3     
; Total bytes of code 56, prolog size 12 for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int):int:this
; ============================================================

Becomes this successful inline

Successfully inlined Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:ThrowIfErrored(int):this (12 IL bytes) (depth 1) [below ALWAYS_INLINE size]

Inlines into 06000088 Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:ThrowIfErrored(int):this
  [0 IL=0006 TR=000012 06000089] [FAILED: does not return] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:ThrowError(int):this
Budget: initialTime=96, finalTime=96, initialBudget=960, currentBudget=960
Budget: initialSize=404, finalSize=404
; Assembly listing for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:ThrowIfErrored(int):this
; Emitting BLENDED_CODE for X64 CPU with SSE2
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
;  V00 this         [V00,T01] (  3,   2  )     ref  ->  rcx         this
;  V01 arg1         [V01,T00] (  4,   3  )     int  ->  rdx        
;  V02 OutArgs      [V02    ] (  1,   1  )  lclBlk (32) [rsp+0x00]  
;
; Lcl frame size = 40
G_M39754_IG01:
       sub      rsp, 40
       nop      
G_M39754_IG02:
       test     edx, edx
       jl       SHORT G_M39754_IG04
G_M39754_IG03:
       add      rsp, 40
       ret      
G_M39754_IG04:
       call     [Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:ThrowError(int):this]
       int3     
; Total bytes of code 21, prolog size 5 for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:ThrowIfErrored(int):this

@benaadams
Copy link
Contributor Author

benaadams commented Aug 21, 2016

This unprofitable inline

[FAILED: unprofitable inline] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):int:this

Inlines into 06000089 Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):int:this
  [0 IL=0006 TR=000019 060000A7] [FAILED: unprofitable inline] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:err_name(int):ref:this
  [0 IL=0014 TR=000028 060000A8] [FAILED: unprofitable inline] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:strerror(int):ref:this
  [1 IL=0074 TR=000111 060000C3] [below ALWAYS_INLINE size] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException:.ctor(ref,int):this
    [0 IL=0002 TR=000123 06002887] [FAILED: not inline candidate] System.Exception:.ctor(ref):this
Budget: initialTime=321, finalTime=337, initialBudget=3210, currentBudget=3210
Budget: initialSize=2114, finalSize=2114
; Assembly listing for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):int:this
; Emitting BLENDED_CODE for X64 CPU with SSE2
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
;  V00 this         [V00,T02] (  4,   3  )     ref  ->  rdi         this
;  V01 arg1         [V01,T01] (  8,   6  )     int  ->  rsi        
;  V02 arg2         [V02,T03] (  4,   3  )   byref  ->  rbx        
;  V03 loc0         [V03,T09] (  2,   1  )     ref  ->  rbp        
;  V04 loc1         [V04,T10] (  2,   1  )     ref  ->  rdi        
;  V05 tmp0         [V05,T00] (  9,   9  )     ref  ->  r14        
;  V06 tmp1         [V06,T05] (  3,   3  )     ref  ->  rax        
;  V07 tmp2         [V07,T04] (  4,   4  )     ref  ->  rdi        
;  V08 tmp3         [V08,T06] (  2,   2  )     ref  ->  rdx        
;  V09 tmp4         [V09,T11] (  1,   1  )     ref  ->  rcx        
;  V10 tmp5         [V10,T07] (  2,   2  )     ref  ->   r8        
;  V11 OutArgs      [V11    ] (  1,   1  )  lclBlk (32) [rsp+0x00]  
;  V12 cse0         [V12,T08] (  3,   1.5)    long  ->  r15        
;
; Lcl frame size = 40
G_M62864_IG01:
       push     r15
       push     r14
       push     rdi
       push     rsi
       push     rbp
       push     rbx
       sub      rsp, 40
       mov      rdi, rcx
       mov      esi, edx
       mov      rbx, r8
G_M62864_IG02:
       test     esi, esi
       jge      G_M62864_IG04
       mov      rcx, rdi
       mov      edx, esi
       call     [Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:err_name(int):ref:this]
       mov      rbp, rax
       mov      rcx, rdi
       mov      edx, esi
       call     [Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:strerror(int):ref:this]
       mov      rdi, rax
       mov      ecx, 6
       call     [CORINFO_HELP_READYTORUN_NEWARR_1]
       mov      r14, rax
       mov      r8, qword ptr [(reloc)]
       mov      r8, gword ptr [r8]
       mov      rcx, r14
       xor      edx, edx
       call     [CORINFO_HELP_ARRADDR_ST]
       call     [CORINFO_HELP_READYTORUN_NEW]
       mov      rcx, r14
       mov      dword ptr [rax+8], esi
       mov      r8, rax
       mov      rcx, r14
       mov      edx, 1
       call     [CORINFO_HELP_ARRADDR_ST]
       mov      r15, qword ptr [(reloc)]
       mov      r8, gword ptr [r15]
       mov      rcx, r14
       mov      edx, 2
       call     [CORINFO_HELP_ARRADDR_ST]
       mov      rcx, r14
       mov      r8, rbp
       mov      edx, 3
       call     [CORINFO_HELP_ARRADDR_ST]
       mov      r8, gword ptr [r15]
       mov      rcx, r14
       mov      edx, 4
       call     [CORINFO_HELP_ARRADDR_ST]
       mov      rcx, r14
       mov      r8, rdi
       mov      edx, 5
       call     [CORINFO_HELP_ARRADDR_ST]
       call     [CORINFO_HELP_READYTORUN_NEW]
       mov      rdi, rax
       mov      rcx, r14
       call     [System.String:Concat(ref):ref]
       mov      rdx, rax
       mov      rcx, rdi
       call     [System.Exception:.ctor(ref):this]
G_M62864_IG03:
       mov      rdx, qword ptr [(reloc)]
       mov      dword ptr [rdi+rdx], esi
       mov      rcx, rbx
       mov      rdx, rdi
       call     [CORINFO_HELP_CHECKED_ASSIGN_REF]
       jmp      SHORT G_M62864_IG05
G_M62864_IG04:
       xor      rax, rax
       mov      gword ptr [rbx], rax
G_M62864_IG05:
       mov      eax, esi
G_M62864_IG06:
       add      rsp, 40
       pop      rbx
       pop      rbp
       pop      rsi
       pop      rdi
       pop      r14
       pop      r15
       ret      
; Total bytes of code 269, prolog size 12 for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):int:this

Becomes this successful inline

Successfully inlined Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):this (17 IL bytes) (depth 1) [profitable inline]

Inlines into 0600008A Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):this
  [0 IL=0010 TR=000016 0600008B] [FAILED: noinline per IL/cached result] Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:GetError(int):ref:this
Budget: initialTime=111, finalTime=111, initialBudget=1110, currentBudget=1110
Budget: initialSize=518, finalSize=518
; Assembly listing for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):this
; Emitting BLENDED_CODE for X64 CPU with SSE2
; optimized code
; rsp based frame
; partially interruptible
; Final local variable assignments
;
;  V00 this         [V00,T02] (  3,   2.5)     ref  ->  rcx         this
;  V01 arg1         [V01,T00] (  4,   3.5)     int  ->  rdx        
;  V02 arg2         [V02,T01] (  3,   3  )   byref  ->   r8        
;  V03 tmp0         [V03,T03] (  3,   2  )   byref  ->  rsi        
;  V04 tmp1         [V04,T04] (  3,   2  )   byref  ->  rsi        
;  V05 tmp2         [V05,T05] (  3,   2  )     ref  ->  rdx        
;  V06 OutArgs      [V06    ] (  1,   1  )  lclBlk (32) [rsp+0x00]  
;
; Lcl frame size = 32
G_M62863_IG01:
       push     rsi
       sub      rsp, 32
G_M62863_IG02:
       mov      rsi, r8
       test     edx, edx
       jl       SHORT G_M62863_IG03
       xor      rdx, rdx
       jmp      SHORT G_M62863_IG04
G_M62863_IG03:
       call     [Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:GetError(int):ref:this]
       mov      rdx, rax
G_M62863_IG04:
       mov      rcx, rsi
       call     [CORINFO_HELP_CHECKED_ASSIGN_REF]
       nop      
G_M62863_IG05:
       add      rsp, 32
       pop      rsi
       ret      
; Total bytes of code 41, prolog size 5 for method Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.Libuv:Check(int,byref):this

@benaadams
Copy link
Contributor Author

Changed dasm file Microsoft.AspNetCore.Server.Kestrel-Check.dasm.txt 4.4MB

@halter73
Copy link
Member

LGTM. 🚀

@cesarblum
Copy link
Contributor

@benaadams Awesome change. Before we merge, can you add comments documenting that the code is factored that way to benefit from inlining? I want to avoid having someone change this in the future because they're not aware of what is being accomplished by having the code written this way.

{
return _uv.try_write(this, new[] { buf }, 1);
_uv.try_write(this, new[] { buf }, 1);
Copy link

@txdv txdv Aug 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryWrite returns the number of bytes written to a socket or an error.

I don't think that removing the returned value is really something one wants to do with this API. It is currently not used, but in the future, if one were to use it, one would check how much of the buffer would have been written by using the returned value and eventually queue the rest of the buffer for writing with a normal Write call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning value for try_write now

@benaadams
Copy link
Contributor Author

@CesarBS added comments

@cesarblum
Copy link
Contributor

@benaadams Thanks! 🚢

@benaadams benaadams closed this Aug 25, 2016
@benaadams benaadams reopened this Aug 25, 2016
@cesarblum cesarblum merged commit 7dc0a8c into aspnet:dev Aug 26, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants