Conditional jumps, especially those that are hard to predict, are fairly expensive, so they should be avoided if possible. One way to avoid them is to use conditional moves and similar instructions (like sete). As far as I can tell, RuyJit never does this and I think it should.
For example, take these two methods:
[MethodImpl(MethodImplOptions.NoInlining)]staticlongsete_or_mov(boolcond){returncond?4:0;}[MethodImpl(MethodImplOptions.NoInlining)]staticlongcmov(longlongValue){longtmp1=longValue&0x00000000ffffffff;returntmp1==0?longValue:tmp1;}For both of them, RyuJit generates a conditional jump:
; Assembly listing for method Program:sete_or_mov(bool):long; Emitting BLENDED_CODE for X64 CPU with SSE2; optimized code; rsp based frame; partially interruptible; Final local variable assignments;; V00 arg0 [V00,T00] ( 3, 3 ) bool -> rcx; V01 tmp0 [V01,T01] ( 3, 2 ) int -> rax;# V02 OutArgs [V02 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00];; Lcl frame size = 0G_M60330_IG01:G_M60330_IG02: 84C9 testcl,cl7504jne SHORT G_M60330_IG03 33C0 xoreax,eax EB05 jmp SHORT G_M60330_IG04G_M60330_IG03: B804000000 moveax,4G_M60330_IG04: 4863C0 movsxdrax,eaxG_M60330_IG05: C3 ret; Total bytes of code 17, prolog size 0 for method Program:sete_or_mov(bool):long; ============================================================; Assembly listing for method Program:cmov(long):long; Emitting BLENDED_CODE for X64 CPU with SSE2; optimized code; rsp based frame; partially interruptible; Final local variable assignments;; V00 arg0 [V00,T00] ( 4, 3.5) long -> rcx; V01 loc0 [V01,T01] ( 3, 2.5) long -> rax;# V02 OutArgs [V02 ] ( 1, 1 ) lclBlk ( 0) [rsp+0x00];; Lcl frame size = 0G_M53075_IG01:G_M53075_IG02: B8FFFFFFFF moveax,0xFFFFFFFF 4823C1 andrax,rcx 4885C0 testrax,rax7401je SHORT G_M53075_IG04G_M53075_IG03: C3 retG_M53075_IG04: 488BC1 movrax,rcxG_M53075_IG05: C3 ret; Total bytes of code 18, prolog size 0 for method Program:cmov(long):long; ============================================================
For comparison, here are the same methods compiled using Clang and GCC with -O1 (by Compiler Explorer):
GCC 6.2:
sete_or_mov(bool):test dil, dil setne almovzxeax,alsalrax,2retcmov(unsigned long):moveax,editestrax,rax cmove rax,rdiret
Clang 3.9.0:
sete_or_mov(bool): # @sete_or_mov(bool)movzxeax, dilshlrax,2retcmov(unsigned long): # @cmov(unsigned long)moveax,edimovecx,4294967295andrcx,rdi cmove rax,rdiret
category:cq
theme:basic-cq
skill-level:expert
cost:large
impact:small
Conditional jumps, especially those that are hard to predict, are fairly expensive, so they should be avoided if possible. One way to avoid them is to use conditional moves and similar instructions (like
sete). As far as I can tell, RuyJit never does this and I think it should.For example, take these two methods:
For both of them, RyuJit generates a conditional jump:
For comparison, here are the same methods compiled using Clang and GCC with
-O1(by Compiler Explorer):GCC 6.2:
Clang 3.9.0:
category:cq
theme:basic-cq
skill-level:expert
cost:large
impact:small