Many algorithms of my SASS optimizer work with code blocks. So I decided to collect some metrics about blocks, resources usage per block/function etc to find outliers (like too short blocks or blocks with anomaly high registers numbers). But before I present the results I should note that building of cyclomatic complexity for SASS is not easy task:
- approximately half of EIATTR attributes are undocumented. And yet, there are some very remarkable ones there - like EIATTR_COROUTINE_RESUME_ID_OFFSETS. Judging by the name they are clearly related to coroutines and so should be taken into account while carving code blocks. Unfortunately, there are no Cubin files in my collection that contain this attribute
- Predicated instructions. Well, this is not SASS-specific problem - for example old 32bit arm had them too. But for example how consider case with several instructions with predicates in the same block? Each of them can modify value of predicate register - then this will be another branch, right? So I just ignore predicates for now
- How to carve code blocks? For my needs, I require maximum-sized blocks to minimize the number of blocks used - so I ignore many cases like BSSY/BSYNC pairs. Sure you can modify my logic of CFG building and make it more similar to classic SSA - see function dg in dg2.pl and the preceding extensive commentary
All tests were conducted on libcublas.so.13.7.0.10 from CUDA SDK 13.4 for sm90 cubins, command line options for dg2.pl -gtT:
- -g to build CFG
- -t for registers/predicates tracking
- -T is new option added special to produce various
uselessmetrics
Count and length of blocks
150 public syms, avg len 191.973333 blocks
380 public syms, avg len 11.134211 blocks
184 public syms, avg len 54.494565 blocks
Resources usage per block
; 9881 uregs, avg 2.335382 per block
; 6436 preds, avg 1.521153 per block
; 497 upreds, avg 0.117466 per block
; 8879 uregs, avg 0.885509 per block
; 7386 preds, avg 0.736611 per block
; 401 upreds, avg 0.039992 per block
Resources used in single block
;;; Registers used in single block:
Here S at end stand for Starting blocks (and B for block having back-edge - loop). Track for R1 typically looks like:
; R1 in 0 end 27F S
; R54 in 12B0 end 1D6F B
; R60 in BF0 end 116F B
; R59 in BF0 end 116F B
; R57 in BF0 end 116F B
; R61 in BF0 end 116F B
; R1
Single write at address 0 and zero reads. Ok, lets look at address 0:
; 0 mask 8000 <-
; R2/*0*/ LDC R1,c[0][0x28] ?trans8 ;
; CB 28 stack_ptr
; mask 0 mask2 0
; used regs:
; R1: 80 write
Needless to note that this specific function does not call other functions - and still waste register to hold stack
Another example:; R2
; 10 mask 8000 <-
; R3
/*10*/ S2R R2,SR_CTAID.Y &wr=0x0 ?trans1 ;
; mask 0 mask2 0
; used regs:
; R2: 80 write
As you can guess this function does not use SR_CTAID.Y
It is quite likely there is undocumented ABI and ptxas can't optimize prologue for unused mandatory resources
Gaps in sequentially allocated registers
my hypothesis was that if EIATTR_REGCOUNT for some function was set to for example 32 - then those function should use registers in range R0 .. R31. So I decides to check if this is true.Surprisingly, there are gaps:
; RHoles max 53: R50 R52
; URHoles max 13: UR0 UR1 UR2 UR3 UR11
Here EIATTR_REGCOUNT has value 54, function used registers up to R53 and UR14. At the same time it never used R50, R52 etc
It seems that registers UR0-UR3 never used - this is again probably undocumented ABI, but why waste 2 general purpose register in middle? I can guess that inside PTXAS there were some optimization passes after register allocation (AllocateRegisters) and they eliminated some instructions. Or it's just bug
Комментариев нет:
Отправить комментарий