• scorpio task(2023)
    • P0: 功能相关
    • P1: 基础优化
    • P1:整体性能优化
    • P2:功能支持
    • 整体目标: 负责跟踪进展
  • Scorpio测试需求(stage 1)
  • Scorpio设计方案
    • 32bit tar方案
    • 2.global变量支持
    • ISA-2.0和ISA-3.0抽取公共部分
    • L2访问
    • MixInstr Opt
    • performance分析
      • conv3d算子性能分析
      • depthwise算子性能分析
    • Scorpio RegClass实现
    • smr使用方法
    • SVMM2指令对VR的分配要求
    • VA Bank Conflict Resolver on Scorpio3.0
    • vacc分配和打包约束
    • VACC和VR类型冲突
    • VACC间接使用方法
    • vldi.tar_t0/t1问题
    • 不同数据类型copy/spill测试
    • 验证elementwise在大latency下的性能
  • Scorpio需求管理

scorpio task(2023)

P0: 功能相关

qa spill 优化以及warning & Maskbit spill问题解决
intrinsics接口梳理,user guide更新
intrinsics立即数前端检测报错,暂hold,其他后端前端检查的文件用的是clang-tblgen产生,todo。
汇编/反汇编测试补全
超越函数优化以及inline版本: 参考网络里面的性能情况。 精度风险,事先验证(msf精度比泰勒展开差)。
同步到最新ISA
打包规则update
Itineary update
smr 分配 :
全局访存: 物理上不通。需要确认。
3.0卡姆问题fix
编程文档, f16支持,64bit支持情况说明

P1: 基础优化

va bank功能增强
vectorizer enable ?? 做与否的依据; elemwise fusion ;  深入了解其它team需求
特殊latency优化, forwarding
1d算子性能优化(fft, gemm.)
slot选择, vector c.tvst/m.tvst
3.0 swp & hwloop enable:benchmark选取,性能提升数据,功能正确
unroll策略: pragma clang unroll(full) 指定unroll策略,并在编程手册中告知用户规则

P1:整体性能优化

1d bm相关工作
优化BU模型算子性能
2d性能跟convgen对比(8个)

P2:功能支持

O0/O1支持和测试
tar数组  潘晨_Chen
64bit scalar支持
stl 支持

整体目标: 负责跟踪进展

benchmark性能: fft, gemm
模型性能: 分解OCR模型调用的算子,或者跟算子要。 优先级最高的。 按照热点函数来排优先级。
2d跟convgen对比,展示。
问题: OCR用到的1D算子是2.0的么?需不需要重写?

Scorpio测试需求(stage 1)

  • Vector C:+/-//<</>>/&&/||/^ vector op vector
    LHS va64u8(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va64i8(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32f16(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32bf16(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32u16(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32i16(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va16f32(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va16i32(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va16u32(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
  • Vector C:+/-//<</>>/&&/||/^ vector op scalar
    LHS va64u8(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va64i8(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32f16(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32bf16(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32u16(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va32i16(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va16f32(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va16i32(x2/x4) +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
    LHS va16u32(x2/x4) +/-/
    /<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^ +/-//<</>>/&&/||/^
  • 混合精度运算
    RSH:
    LHS */+ */+ */+
    LHS */+ */+ */+

Scorpio设计方案

32bit tar方案

方案一:

tar_t a, b;
int c, d;a = movsr2tar_t0(c);
b = movsr2tar_t1(d);变量a, b是不能分配到不同线程相同tar寄存器的。因为对compiler来说,只看到32个tar寄存器,不清楚变量a是thread0的,变量b是thread1的。
此时,寄存器分配效率会下降。假如,重复16次:
a0 = movsr2tar_t0(c0);
b0 = movsr2tar_t1(d0);
a1 = movsr2tar_t0(c1);
b1 = movsr2tar_t1(d1);
...
a15 = movsr2tar_t0(c15);
b15 = movsr2tar_t1(d15);a0, b0, a1, b1, ... a15, b15分别占用tar0-tar31。
如果再加一条movsr2tar_t0,就会发生spill。spill处理方案:
movsr2tar.t0 tar1, r5
movsr2tar.t1 tar2, r6
假如一共2个tar寄存器(tar1, tar2), 那么tar2会发生spill
movtar2sr.dual r8:r9, tar2 // save指令,只能用dual模式(因为compiler不清楚tar2是thread1的),并且需要2个连续sr
movsr2tar.t0 tar2, r7
tvld.itar vr2, [tar1, tar2]
...
movsr2tar.dual tar2, r8:r9 // restore指令,也是用dual模式
tvld.itar vr1, [tar1, tar2] // 此时 tar1=r5, tar2=r6

方案二:

tar_t a0, a1;
int64 c, d; // 64bit变量a0 = movsr2tar_dual(c); //高32bit,低32bit分别给两个线程赋值,跟2.0使用方法一样,只是这里需要传入int64.
a1 = movsr2tar_dual(d);
...
a31 = movsr2tar_dual(d);此时,可以定义32个tar类型变量。
如果超过32个tar变量,发生spill。spill方案:
同样使用movsr2tar.dual/movtar2sr.dual指令,跟方案一的spill一致。

方案一和方案二可以混合,结果跟方案一类似。

2.global变量支持

1、链接脚本中指定L2的地址范围,如下:

ENTRY(main)
SECTIONS {. = 0x10000;.text : { *(.text); *(.text.*) }.bss : { *(.bss); *(.bss.*) }. = 0x9000;.data : {*(.data);*(.data.*)}. = 0x9004;.rodata : {*(.rodata);*(.rodata.*)
}

上图所示:

text段从0x10000开始,data段起始于0x9000;rodata段指定0x9004开始。

如果data段超过4bytes,链接器会报错。

ld.lld: error: section .data load address range overlaps with .rodata
.data range is [0x9000, 0x9007]
.rodata range is [0x9004, 0x9008]

2、global变量放在sdmem上,算子说几KB就可以。
3. global 变量的使用场景:全局变量用于在kernel function之间进行共享,生命周期贯穿整个Excutable

var_t global;func_0(args) {use global;...
}func_1(args) {use global;...
}func_2(args) {use global;...
}...

为单个sip独享,并不会在多个sip之间同步,因此可以放在SDMEM当中;
4. SDMEM地址范围:

ISA-2.0和ISA-3.0抽取公共部分

创建一个FeatureStd,用来存放FeatureX和FeatureScorpio的公共特性,它不会对应到某个具体的处理器,因此这里没有使用“DTUFamily = Std"这种方式去设置DTUFamily这个Subtarget的attribute,而是给Subtarget添加了UseStd这个bool类型的Atrribute,这里FeatureStd会将"UseStd"设置成"true":

def FeatureStd: SubtargetFeature<"std", "UseStd", "true", "Use standard target feature">;def FeatureX : SubtargetFeature<"x", "DTUFamily", "X", "DTU AI Accelerator X", [FeatureStd]>;
...
def FeatureScorpio : SubtargetFeature<"scorpio", "DTUFamily", "Scorpio", "DTU AI Accelerator Scorpio", [FeatureStd]>;
...
def Dvx20 : HwMode<"+x,-mt">;
def Dvx20MT : HwMode<"+x,+mt">;
def Dvx10 : HwMode<"+leo">;
def Dvx30 : HwMode<"+scorpio,-mt">;
def Dvx30MT : HwMode<"+scorpio,+mt">;

Dvx30和Dvx30MT用来做数据类型选择,这里暂时使用ISA-2.0的实现:

// DTU Vector types by HwMode
def VI1   : ValueTypeByHwMode<[Dvx10,   Dvx20, Dvx20MT, DefaultMode],[v1024i1, v64i8, v64i8,   v1024i1]>;
def VI8   : ValueTypeByHwMode<[Dvx10,   Dvx20, Dvx20MT, DefaultMode],[v128i8,  v64i8, v64i8, v128i8]>;
def VI16  : ValueTypeByHwMode<[Dvx10,   Dvx20, Dvx20MT, DefaultMode],[v64i16,  v32i16, v32i16, v64i16]>;
def VI32  : ValueTypeByHwMode<[Dvx10,   Dvx20, Dvx20MT, DefaultMode],[v32i32,  v16i32, v16i32, v32i32]>;
def VF16  : ValueTypeByHwMode<[Dvx10,   Dvx20, Dvx20MT, DefaultMode],[v64f16,  v32f16, v32f16, v64f16]>;
def VF32  : ValueTypeByHwMode<[Dvx10,   Dvx20, Dvx20MT, DefaultMode],[v32f32,  v16f32, v16f32, v32f32]>;

FeatureStd的enable是自动完成的, DTUSubtarget在初始化的时候,如果得到当前的CPU类型是"x"或者"scorpio",则自动设置FeatureStd到FeatureBits中并且将UseStd变量置为true:

DTUSubtarget& DTUSubtarget::initializeProperties() {if (getCPU() == "generic" || getCPU() == "leo") {ISAVersion = 10;DTUFamily = Leo;} else if (getCPU() == "x") {ISAVersion = 20;DTUFamily = X;FeatureBitset FeatureBits = getFeatureBits();FeatureBits.set(DTU::FeatureStd);setFeatureBits(FeatureBits);UseStd = true;} else if (getCPU() == "scorpio") {ISAVersion = 30;DTUFamily = Scorpio;FeatureBitset FeatureBits = getFeatureBits();FeatureBits.set(DTU::FeatureStd);setFeatureBits(FeatureBits);UseStd = true;}if (isDTULoopVectorizeEnabled()) {HasMT = true;FeatureBitset FeatureBits = getFeatureBits();FeatureBits.set(DTU::FeatureMT);setFeatureBits(FeatureBits);}//"-Xclang -target-feature -Xclang  +smr" sets the FeatureBits to FeatureExplicitSMRFeatureBitset FeatureBits = getFeatureBits();if (FeatureBits[DTU::FeatureExplicitSMR])HasExplicitSMR = true;return *this;
}

L2访问

L2指令访问目的:不使用dma,直接通过指令来Load/store数据,利于算子后向兼容。
1、硬件实现:使用地址来区分L2还是L1
2、编译器实现:通过提供__dtu_l_vldda_l2(ptr); __dtu_l_vldda_l1(ptr)等不同intrinsics,算子根据ptr地址,手动选择intrinsics。
3、期望算子使用场景:

int elementwise(float* ptr1, float* ptr2, float* ptr3) {   // ptr1, ptr2, ptr3都指向L2#pragma clang loop unroll_count(200)for (int i = 0; i < size; i++) {va16f32 a = __dtu_l_vldda_l2(ptr1);va16f32 b = __dtu_l_vldda_l2(ptr2);va16f32 c = __dtu_m_mop_madd_dacc(a, b);__dtu_v_vstda_l2(c, ptr3);}
}
期望sip在执行上面代码时,其他sip不会读写ptr1/ptr2/ptr3地址。否则,编程者需要将float*改为float* volatile,来保证功能正确,并且这个情况下,compiler不会做优化。
我们认为这种细粒度(指令级)的同步,算子不会用到。
期望算子的使用方式,算子级别的串行方式:执行完上面代码后,通过hw sync,然后其他sip再访问ptr1,ptr2,ptr3的内容。
  1. tar寄存器仅支持L1 VDMEM寻址,因此所有tar寄存器寻址的load/store指令均不支持L2/L3寻址;全局访问,总线位宽是128byte/cycle,但是指令是可以超过的128byte,VLDQA/VSTDA指令是可以访问L2和L3。对于VLDQA,指令发射一个512byte的load,硬件是会拆成4个128byte发射出去。

因此可以实现全局访问的load/store指令: VST, VLD, VSTL, VLDL, VLDA, VLDLA, VSTA, VSTLA, VSTDA, VLDXDA, VSTXDA, VLDQA, VLDQA.imm1, VLD_SMR, VST_SMR

其中VLD_SMR和VST_SMR指令仅用于SMR的spill使用,不提供intrinsic接口;

MixInstr Opt

Factor 需求:为了尽快配合factor-api封装,以下用va f16mix计算为例(后续factor会整理3.0上所有的混精指令)。
demo验证:
1.在Finalize ISel and expand pseudo-instructions之前,cvt和mul ins以func call形式存在,ins replace应该在此之后实现;
2.考虑到copy指令消除,bankresolve和RA,实现应在这些pass之前实现

for (MachineBasicBlock &MBB : MF) {bool changed = true;while (changed) {changed = false;MRI = &MF.getRegInfo();MachineBasicBlock::iterator MII = MBB.begin();MachineBasicBlock::iterator lastMII = MBB.end();while (MII != lastMII) {if (MII->getOpcode() == DTU::M_MOP_MUL_f16_qa) {LLVM_DEBUG(dbgs() << "XXX dump begin \n");MBB.dump();MII->dump();MII->getOperand(0).dump();MachineInstr *defInst = MRI->getVRegDef(MII->getOperand(1).getReg());defInst->dump();        LLVM_DEBUG(dbgs() << "XXX dump end \n");// if one is qacc cvt from dacc, then useunsigned newOpcode = DTU::M_MOP_MUL_F16MIX_s8_da;MachineOperand op0 = MII->getOperand(0);// MachineOperand op1 = MII->getOperand(1);MachineOperand op2 = MII->getOperand(2);DebugLoc DL = (*MII).getDebugLoc();if (defInst->getOpcode() == DTU::M_MOP_CVT_da_rz_s8_f16){BuildMI(MBB, MII, DL, TII->get(newOpcode), op0.getReg()).addReg(defInst->getOperand(1).getReg()).addReg(op2.getReg());MBB.erase(MII);changed = true;break;} else {MII++;continue;}} else {MII++;continue;}}}
}


下图红色ir ==>> 绿色ir (当前log处已完成前2条指令的替换)

performance分析

conv3d算子性能分析

分析sdk/lib/ops/common/c_sources下面的conv3d* 相关2d算子,这类都是典型的2d算子:

conv3d_bpk_general_bf16.cc conv3d_bpk_general_ef32.cc conv3d_general_bf16.cc conv3d_general_ef32.cc
conv3d_bpk_general.cc conv3d_bpk_general_fp16.cc conv3d_general.cc conv3d_general_fp16.cc
一、分析了一下conv3d_bpk_general_bf16.cc文件,里面有很多函数,以函数conv3d_bf16_kerneln8ho1wo1为例:(这些优化点都是基于sip2.0指令集分析的)
优化点一:循环开始的两条s.tivld_itar没有打包,怀疑tar memory相关的alias没有分析出来,需要加 attribute “no_mem_alias_in_tar"看看效果。

1359 .LBB0_11:                               # %for.body58
1360                                         #   Parent Loop BB0_6 Depth=1
1361                                         #     Parent Loop BB0_9 Depth=2
1362                                         # =>    This Inner Loop Header: Depth=3
1363 #›   cycle: 0
1364 ›   {1365          ›  v.nop
1366          ›  m.nop
1367          ›  s.tivld_itar iv0, [ta_g1, 1]
1368          ›  s.nop16
1369          ›  c.nop
1370 ›   }
1371 #›   cycle: 1
1372 ›   {1373          ›  v.nop
1374          ›  m.nop
1375          ›  s.tivld_itar iv1, [ta_g1, 1]
1376          ›  s.nop16
1377          ›  c.nop
1378 ›   }
1379 #›   cycle: 2
1380 ›   {1381          ›  v.nop
1382          ›  m.nop
1383          ›  s.tivld_itar iv2, [ta_g1, 1]
1384          ›  s.nop16
1385          ›  c.nop
1386 ›   }

优化点二:m.ldsmr 没有和两条s.tvld_itar打包在一起

1602 #›   cycle: 30
1603 ›   {1604          ›  v.nop
1605          ›  m.ldsmr.mode0.bf.row smr0, vr4, 13
1606          ›  s.tvld_itar vr4, [ta_g2, 1]
1607          ›  s.nop16
1608          ›  c.nop
1609 ›   }
1610 #›   cycle: 31
1611 ›   {1612          ›  v.nop
1613          ›  m.ldsmr.mode0.bf.row smr0, vr5, 14
1614          ›  s.tvld_itar vr5, [ta_g2, 1]
1615          ›  s.nop16
1616          ›  c.nop
1617 ›   }
1618 #›   cycle: 32
1619 ›   {1620          ›  v.nop
1621          ›  m.ldsmr.mode0.bf.row smr0, vr6, 15
1622          ›  s.tvld_itar vr6, [ta_g2, 1]
1623          ›  s.nop16
1624          ›  c.nop
1625 ›   }

二、分析函数conv3d_bf16_kerneln8ho2wo2
这个函数核心循环开头和中间打包做的比较好,如下:

1650          ›  v.nop
1651          ›  m.nop
1652          ›  s.tivld_itar iv3, [ta_g1, 1]
1653          ›  s.tvld_itar vr3, [ta_g2, 1]
1654          ›  c.nop
1655 ›   }
1656 #›   cycle: 4
1657 ›   {1658          ›  v.nop
1659          ›  m.nop
1660          ›  s.tivld_itar iv4, [ta_g1, 1]
1661          ›  s.tvld_itar vr4, [ta_g2, 1]
1662          ›  c.nop
1663 ›   }
1664 #›   cycle: 5
1665 ›   {1666          ›  v.nop
1667          ›  m.nop
1668          ›  s.tivld_itar iv5, [ta_g1, 1]
1669          ›  s.tvld_itar vr5, [ta_g2, 1]
1670          ›  c.nop
1671 ›   }
1672 #›   cycle: 6
1673 ›   {1674          ›  v.nop
1675          ›  m.nop
1676          ›  s.tivld_itar iv6, [ta_g1, 1]
1677          ›  s.tvld_itar vr6, [ta_g2, 1]
1678          ›  c.nop1696 #›   cycle: 9
1697 ›   {1698          ›  v.nop
1699          ›  m.vmm.mode0.bf.vsel0.ld.row dacc25, iv0, smr0, vr0, 0
1700          ›  s.tivld_itar iv9, [ta_g1, 1]
1701          ›  s.tvld_itar vr9, [ta_g2, 1]
1702          ›  c.nop
1703 ›   }
1704 #›   cycle: 10
1705 ›   {1706          ›  v.nop
1707          ›  m.vmm.mode0.bf.vsel0.ld.row dacc21, iv1, smr0, vr1, 1
1708          ›  s.tivld_itar iv0, [ta_g1, 1]
1709          ›  s.tvld_itar vr0, [ta_g2, 1]
1710          ›  c.nop
1711 ›   }
1712 #›   cycle: 11
1713 ›   {1714          ›  v.nop
1715          ›  m.vmm.mode0.bf.vsel0.ld.row dacc17, iv2, smr0, vr2, 2
1716          ›  s.tivld_itar iv1, [ta_g1, 1]
1717          ›  s.tvld_itar vr1, [ta_g2, 1]
1718          ›  c.nop
1719 ›   }

但是循环尾的指令,打包不好,如下:

1919 #›   cycle: 37
1920 ›   {1921          ›  v.nop
1922          ›  m.vmm.mode0.bf.vsel0.ld.row dacc18, iv8, smr0, vr8, 28
1923          ›  s.nop16
1924          ›  s.nop16
1925          ›  c.nop
1926 ›   }
1927 #›   cycle: 38
1928 ›   {1929          ›  v.nop
1930          ›  m.vmm.mode0.bf.vsel0.ld.row dacc14, iv9, smr0, vr9, 29
1931          ›  s.nop16
1932          ›  s.nop16
1933          ›  c.nop
1934 ›   }
1935 #›   cycle: 39
1936 ›   {1937          ›  v.nop
1938          ›  m.vmm.mode0.bf.vsel0.ld.row dacc11, iv0, smr0, vr0, 30
1939          ›  s.nop16
1940          ›  s.nop16
1941          ›  c.nop
1942 ›   }
1943 #›   cycle: 40
1944 ›   {1945          ›  v.nop
1946          ›  m.vmm.mode0.bf.vsel0.ld.row dacc7, iv1, smr0, vr1, 31
1947          ›  s.nop16
1948          ›  s.nop16

原因是:循环尾部只剩下m.vmm一类指令了,没有其它指令可以打包了,所以效果很差,这个情况下,需要做swp优化,使循环尾部的m.vmm指令和循环开始的两条s.tvld_itar指令打包在一起,算了一下,尾部刚好8条m.vmm和循环开头的8个s.tvld_itar指令包可以打包在一起。

三、分析函数conv3d_bf16_kerneln8ho3wo3,跟上面的函数类似,需要swp优化解决,才能提升性能。
四、其他函数暂不分析,建议每个同事分析一些文件,然后统一一下意见,看看哪些优化点优先级最高,然后开始解决。

depthwise算子性能分析

以depthwise_conv2d_cmeq1_kle13_sle13_ldeq1_rdle13_bf16.cc文件为例,其他depthwise算子代码在sdk/lib/ops/common/c_sources下面

一、函数depthwise_conv2d_cmeq1_bf16_kernel_32分析
优化点一:最内层循环开始,怎么使用了v.tvld_itar指令,没有用s.tvld_itar指令?两条s.tvld_itar可以打包,但是两条v.tvld_itar不能打包
方案一:算子改动,将v.tvld_itar改为s.tvld_itar;

方案二:compiler实现slot选优功能;

1281 #›   cycle: 0
1282 ›   {1283          ›  v.tvld_itar vr17, [ta_g2, 1]
1284          ›  m.vmac.bf16.nacc dacc12, vr13, vr0
1285          ›  s.nop16
1286          ›  s.nop16
1287          ›  c.addi r25, 1
1288 ›   }
1289 #›   cycle: 1
1290 ›   {1291          ›  v.tvld_itar vr18, [ta_g2, 1]
1292          ›  m.vmac.bf16.nacc dacc13, vr14, vr0
1293          ›  s.nop16
1294          ›  s.nop16
1295          ›  c.nop
1296 ›   }
1297 #›   cycle: 2
1298 ›   {1299          ›  v.tvld_itar vr19, [ta_g2, 1]
1300          ›  m.vmac.bf16.nacc dacc14, vr15, vr0
1301          ›  s.nop16
1302          ›  s.nop16
1303          ›  c.nop
1304 ›   }
1305 #›   cycle: 3
1306 ›   {1307          ›  v.tvld_itar vr20, [ta_g2, 1]
1308          ›  m.vmac.bf16.nacc dacc0, vr1, vr0
1309          ›  s.nop16
1310          ›  s.nop16

优化点二:hwloop没做成,原因分析
优化点三:l slot 的addi没有替换成c slot addi
二、分析函数depthwise_conv2d_cmeq1_bf16_kernel_24,跟上面函数类似,估计同一个人写的。
三、其他函数有时间再分析

Scorpio RegClass实现

一、Scorpio上VACC寄存器概述
1、vacc寄存器增加到4096个

2、vacc寄存器支持直接寻址和间接寻址

3、根据不同访问方式,将指令分为下面四类:

a类:10 bit VACC域,直接访问VA寄存器,编码:VACC_index = {2b0, VACC[9:0]},对外不提供,RA仅能分配VACC[0-1023]
b类:10 bit VACC域,间接访问VA寄存器,编码:DACC_index = VAB + {2b0, VACC[9:0]},RA仅能分配VACC[0-1023]或DACC[0-511]或QACC[0-255]
c类:10 bit QACC域,间接访问VA寄存器,编码:DACC_index = VAB + {1b0, QACC[9:0], 1b0},RA仅能分配QACC[0-1023]或DACC[0-1023]或VACC[0-1023]
d类:12 bit VACC域,间接访问VA寄存器,编码:DACC_index = VAB + {VACC_hi[1:0], VACC[9:0]},RA可以分配VACC[0-4095]或DACC[0-2047]或QACC[0-1023]
二、compiler实现
1、汇编器

  • 对于a, b, d方式,VACC寄存器的编码跟sip2.0保持一致,即:vacc3编码成3,dacc3编码成6,qacc3编码成12;
  • 对于c方式,单独实现VACC寄存器编码,编码规则:vacc3编码成3, dacc3编码成3,qacc3编码成3;

2、CodeGen及相关MI Pass
CodeGen过程涉及两部分:一部分是td描述,二部分是cpp实现。

  • td描述专门为scorpio增加几个regclass,更名为:SVARegs, SDVARegs, SQVARegs, QVARegsOp, DVARegsOp
    对于a类指令,td描述使用VARegs
    对于b类指令,td描述使用VARegs/DVARegs/QVARegs
    对于c类指令,td描述使用QVARegsOp/DVARegsOp VARegsOp不需要提供;QVARegsOp实际是SQVARegs封装,但DVARegsOp不能是SDVARegs的封装,需要单独增加DACC[0-1023]的DACC Class,命名为SSDVARegs。
    对于d类指令,td描述使用SQVARegs/SDVARegs/SVARegs

  • cpp场景,根据不同pass作用分类:
    同时支持VARegs/DVARegs/QVARegs和SVARegs/SDVARegs/SQVARegs
    只支持SVARegs/SDVARegs/SQVARegs就可以,DTURegisterInfo.td里面,这三个RegClass需要分别包含VARegs/DVARegs/QVARegs
    3、inline asm

    inline asm需要提供不同的register constraints:

  • 对于a类指令,对应constraints为"va"

  • 对于b类指令,对应constraints为"va" “da” “qa”

  • 对于c类指令,对应constraints为"va" “sd” “fq”

  • 对于d类指令,对应constraints为"fa" “fd” “fq”

smr使用方法

1、类型定义:smr_t
2、使用方法一:

conv用法:使用swap#include <sip30intrin.h>void test() {va16f32 va, vb;smr_t input, ouput;tar_t tar1, tar2;for (int i = 0; i < n; i++) {v16f32 vr = __dtu_s_tvld_itar(tar1, tar2);input = __dtu_m_ldsmr_mode3_f_row_o2m(input, vr, i, 0);va = __dtu_m_vmm_mode3_f_nacc_vs0_ld_row(va, vr, output, input, vr, 0);  // 假设output变量分配了smr0, input变量分配了smr2;__dtu_v_swapsmr(output, input);}store(va);
}// 建议smr变量的定义和使用在一个code block内。如果定义在for外,使用在for内,会产生smr copy指令,性能影响很大。

3、使用方法二:

bn算子使用:不使用swap#include <sip30intrin.h>void test() {smr_t smr_mean, smr_var;v16f32 vr1, vr2;tar_t tar1, tar2;va16f32 vacc_mean, vacc_var;vr1 = __dtu_s_tvld_itar(tar1, tar2);vr2 = __dtu_s_tvld_itar(tar1, tar2);smr_var = __dtu_m_ldsmr_mode3_f_row(smr_var, vr1, 0);          // smr_var由编译器分配,假设分配smr2smr_var = __dtu_m_ldsmr_mode3_f_row(smr_var, vr2, 1);vr1 = __dtu_s_tvld_itar(tar1, tar2);vr2 = __dtu_s_tvld_itar(tar1, tar2);smr_mean = __dtu_m_ldsmr_mode3_f_row(smr_mean, vr1, 0);    // smr_mean由编译器分配,假设分配smr1smr_mean = __dtu_m_ldsmr_mode3_f_row(smr_mean, vr2, 1);vacc_mean = __dtu_m_vmm_mode3_f_vs0(vacc_mean, vr1, smr_mean);vacc_var = __dtu_m_vmm_mode3_f_vs0(vacc_var, vr1, smr_var);__dtu_c_movsr2naccovr(0x1);
}

SVMM2指令对VR的分配要求

1、硬件需求:SVMM2指令,做left shifting的时候,能否用VR1 VR2这样地址非对齐的两个VR?

2、汇编可以支持的,比如:

 svmm2.mode0 vacc0, vr2:vr3, smr0   // vr2:vr3 连续并且2对齐svmm2.mode0 vacc1, vr3:vr4, smr1   // vr3:vr4 连续并且不是2对齐

3、intrinsics:对于单条指令来说,可以分配对齐的2个连续vr,也可以分配非对齐的2个连续vr;但是两条svmm2的vr变量没有任何关联。

  v32f32 vr1, vr2;  // 定义2个 2VR变量__dtu_m_svmm2_mode0(vr1, smr0);    // 对于变量vr1,可能分配 vr15:vr16__dtu_m_svmm2_mode0(vr2, smr1);    // 对于变量vr2,可能分配 vr20:vr21也就是说对于intrinsics,每条指令中的vr变量会从下面列表中任意分配一个;不同的svmm2指令的vr变量没有任何关系。假如:第一条svmm2 intrinsics分配了vr15:vr16,第二条svmm2指令要求分配:vr16:vr17 ,这个intrinsics做不到,手写汇编可以支持。
VR1_VR2 = 7437,
VR3_VR4 = 7438,
VR5_VR6 = 7439,
VR7_VR8 = 7440,
VR9_VR10 = 7441,
VR11_VR12 = 7442,
VR13_VR14 = 7443,
VR15_VR16 = 7444,
VR17_VR18 = 7445,
VR19_VR20 = 7446,
VR21_VR22 = 7447,
VR23_VR24 = 7448,
VR25_VR26 = 7449,
VR27_VR28 = 7450,
VR29_VR30 = 7451,
VR31_VR0 = 7452,
VR0_VR1 = 7453,
VR2_VR3 = 7454,
VR4_VR5 = 7455,
VR6_VR7 = 7456,
VR8_VR9 = 7457,
VR10_VR11 = 7458,
VR12_VR13 = 7459,
VR14_VR15 = 7460,
VR16_VR17 = 7461,
VR18_VR19 = 7462,
VR20_VR21 = 7463,
VR22_VR23 = 7464,
VR24_VR25 = 7465,
VR26_VR27 = 7466,
VR28_VR29 = 7467,
VR30_VR31 = 7468,

VA Bank Conflict Resolver on Scorpio3.0

Background
MSLOT的MOP指令存在两条关于VACC寄存器的约束条件:

VACCs1[2] != VACCs2[2],即两个输入VACC寄存器应当位于不同的bank;
VACCd % 4 == VACCs1 % 4 == VACCs2 % 4;
在2.0上,违反这两条约束条件会报“illegal instruction”,3.0上已经去除了“illegal instruction”报错,但是在违反约束1时会产生硬件stall,约束2经确认,已经在3.0中移除,因为经过验证,这条约束会导致一定程度的性能下降,详情见vacc约束对性能影响。

硬件上的差别
相比于Pavo,Scorpio上VA bank划分和VACC数量都发生了变化。

Pavo VACC bank划分:以VACC为单位,总共1024个VACC被划分为两个bank:

Scorpio VACC bank 划分:以QACC为单位,总共1024个QACC被划分到4个bank中去:

SA3.0上,MOP指令也大大扩充了,此外,还有Maskbit作为输入操作数的情况需要考虑;

VA Bank Conflict Resolver三步走逻辑
STEP1:Pre-RA Analysis,以VACC寄存器作为节点,以VACC寄存器在MachineInstruction中的"Co-Used"的关系作为边,创建VACC干涉图,并尝试对干涉图中的节点着色(4个bank,分别对应四种颜色),原则为相邻节点不同色,若无法为当前节点选择颜色,则插入COPY VACC;

STEP2:Coalesing处理,在COPY VACC节点消除过程中,可能会继续引入VACC bank confilct,因此消除过程中要进行判断;

STEP3:RA Hint生成,真正的VACC 分配过程,为当前Virtual Register指定VACC bank,返回给RA;

Analysis Pass
代码逻辑在DTUBankConflictResolve,Scorpio3.0会进入DTUBankConflictResolve::colorizeFourBanks进行处理:

if (Subtarget->getISAVersion() == 20)return colorizeTwoBanks(MF);
else if (Subtarget->getISAVersion() == 30)return colorizeFourBanks(MF);
elsereturn false;

算法流程图:

Analysis Pass本身并不会直接分配VACC寄存器,只是对存在VACC Constraint的指令(MOP)所有的Input Operand进行分析,通过着色来避免同时作为Input Operand的Virtual Register分配到同一个VA Bank中去,这个分析Pass的作用后果是会插入一些COPY VA指令(其实更像一个Transform Pass?)

Coalesing
Coalesing是消除COPY VA的过程,盲目消除可能会重新引入VA Bank Conflict,因此需要进行判断,算法逻辑:


RA Hints
RA hints是Register Allocation的回调函数,这里会给目标Virtual Register提供一个Set,里边包含所有可供分配的物理寄存器:
算法流程图:

Hints计算方法:

...
if ((Subtarget->getISAVersion() > 20) && EnableBCResolver) {for (int BankIndex = BankStart * BankLength;BankIndex + BankLength < MaxRegIndex;BankIndex += BankStride * BankLength) {for (unsigned Offset = 0, E = BankLength; Offset != E; ++Offset) {Register Candidate = getNthAccReg(VirtReg, BankIndex + Offset);if (!MRI.isReserved(Candidate)) {if (!is_contained(Order, Candidate))assert(is_contained(Order, Candidate) &&"Target hint is outside allocation order.");Hints.push_back(Candidate);}}}} else {...

BankStride = FBC_TOTAL 为VA Bank数量的总数,ISA-2.0上有2个bank,而ISA-3.0上边有4个bank,BankStride = 4;

BankLength = 16(VA)/8(DA)/4(QA);

BankStart 为当前VACC/DACC/QACC寄存器选定的Color;BankIndex为当前选定Bank的起始Index;这里要将选定的Bank中所有的QA/DA/VA全都放入Hints Set中;
结果分析
前期的DTUBankConflictResolve Pass插入了很多COPY VA,指令选择的时候会变成MOVQA指令,消除VA bank conflict带来的性能提升和引入MOVQA带来的性能下降两者“一得一失”,需要权衡:
idft.cc:Zebu实测结果,未优化:58902cycle;优化后62977cycle;性能drop = 4075 cycle;

做VA分配优化,并且强制消除COPY VA 59066cycle,这一性能与优化前性能接近(相差160cycle,可认为是性能抖动),说明性能drop全部由为了解决Bank Conflict而插入的COPY VA导致的;

从上面的实测数据来看,消除VA bank conflict带来的性能提升被此过程中插入的COPY VA带来的开销所掩盖,代价大于收益;并且VA-BC数量为437,而插入的COPY VA数量为418,基本上等于说,每消除一个VA-BC就要插入一个COPY VA,这显然是不符合预期的;
问题分析:


if (!isVMaskReg(MF, Reg)) {FBankColor CurrentColor =cache->getColorLeastUsed(MF.getName(), CurrentKind, UnavailableColors);cache->setColor(MF.getName(), CurrentKind, CurrentColor, Reg);
}

这种Heuristic用于实现4个VA bank的分配平衡,而不是性能最优,因为强制为当前Virtual Reg分配Leat Used的Phy Reg,会频繁产生VA → VA的Copy;

查看库中所有创建COPY 节点的地方,没有与解决VA bank conflict直接相关的地方,都是process Tied operand,和PHI节点处理相关;

VA-BC Resolver是否打开不光影响到VA寄存器的分配,还会影响Register Coalaesce中COPY VA的消除,这也就是VA-BC Resolver 是否打开的处理结果的区别;

当前可以看到,VA-BC resolver是否打开产生的性能差距,主要是由Register Coalesce逻辑中的处理导致的,考虑VA-BC会丢失一些Coalesce的机会;

源头治理:查看产生这些COPY VA的原因,从根本上遏制其产生;

下游处理:coalesce逻辑中消除;

经过定位,发现是VA Coalesce判断逻辑中,对于VARelated Register所在的MI判断有误,只需要考虑其作为input Operand的情况:

遗留问题:

va_bc_test_1.cc va_bc_vm_test.cc

Zebu实测,va bank conflict解决之后,性能没有提升,需要找硬件确认;
构造的极端测试用例va_bc_test_1.cc和va_bc_vm_test.cc中,均含有无法着色的情况,在前期的Analysis 中可以通过插入COPY VA来解决,但是Analysis Pass的预着色解决不会对后续实际的RA产生影响,导致最终RA之后还是会有VA bank conflict产生;

vacc分配和打包约束

1、mop指令的Vaccs1 != vaccs2约束去除,但是编译器如果分配了同一个bank,性能下降1cycle,也就是说compiler还要尽量分配不同bank。

2、mop指令的模4约束去除,编译器任意分配后,可能不会带来性能下降,硬件会确认;已经确认,不影响。

3、VA的打包约束,调整到一个指令包最大4读4写。4读如果是不同bank,性能最好;4读可能是相同bank,性能次之;4读分成几个指令包,性能最差。// 需要微架构确认

VACC和VR类型冲突

背景:在硬件实现上,VACC和VR寄存器是大小一样的,都是512bit。如果vector elements是float类型,那么在llvm ir上,都表示为v16f32,也就是说vacc和vr寄存器都对应v16f32。

  • 如果vacc和vr都用v16f32来表示,会有以下系列问题:
    1.传参:

v16f32 test(v16f32 a, v16f32 b) {v16f32 c = __dtu_m_mop_madd_f32_va(a, b);return c;
}
为参数选择寄存器时,可能选择vr寄存器也可能选择vacc寄存器(取决于vr/vacc在td中的前后顺序)。如果选择vr寄存器,实际上madd指令需要vacc寄存器,就会需要插入额外的movvr2va指令。
同理,对于fp16来说,dacc的类型是v32f16,跟vr的v32f16也重合。此时,无法选择到底是VR还是DACC寄存器。

2.vector c编程


v16f32 test() {v16f32 a, b;v16f32 c = a + b;return c;
}
此时,CodeGen过程根本不清楚要产生vacc的加法指令,还是产生vr的加法指令。

3.向量化时,也需要根据情况,来选择vacc/vr寄存器

  • (现在的方案)vacc和vr采用不同类型,目前vr是用v16f32, vacc使用v4f32,可以避免上面的问题,但是会引入其他问题。
    1.自动向量化:如果不改向量化算法实现,就会将4个float向量化为vacc,导致结果出错;
    2.跟elements num相关系列优化出错,比如:SROA,shufflevector优化,都需要考虑每个vector的元素个数,导致功能出错。
    新方案:

  • 为vacc新增类型va16f32,大小为16个float
    该方案可能行不通,因为C层面的输入是16个float(也就是typedef float attribute((vector_size(64), aligned(128))) v16f32),解析出ast时,无法确定是v16f32还是va16f32;因为llvm ir中获取getVectorType(numelement, element_type),只输入2个参数,也无法判断该生成llvm_v16f32_ty,还是llvm_va16f32_ty。需要 张昕_Michael 确认一下。

VACC间接使用方法

vacc数量增加到4096个,由于编码限制,有一些指令只能访问0-1023个vacc,有的能间接访问0-4095个vacc。

访问vacc共分下面4类,假如:

第一类:L.VLDA等直接访问0-1023

第二类:L.MOVVA等能通过VAB方式访问到0-4095个VACC,10bits VACC编码

第三类:MOP等能通过VAB访问到0-4095个VACC,10bits QACC编码

第四类:L.VLDDA等 12bits编码,也需要通过VAB间接访问到4096个寄存器

场景一:一个函数内只出现第一类(直接访问)

func() {L.VLDA   // 只能分配0-1023,不能分配1024-4095L.VSTA   // 只能分配0-1023,不能分配1024-4095
}
缺点:算子只能使用0-1023个寄存器,支持spill

场景二:一个函数混合使用直接访问(第一类)和间接访问(第二三四类,且VAB不为0)

func() {L.VLDA   // 只能分配0-1023,访问不了1024-4095L.MOVVA  // 只能分配0-1023,访问不了1024-4095,由于L.VLDA的访问限制MOP      // 只能分配0-1023,访问不了1024-4095,由于L.VLDA的访问限制L.VLDDA  // 只能分配0-1023,访问不了1024-4095,由于L.VLDA的访问限制
}
此时,编译器为了保证正确性,只能分配0-1023个VACC或0-512个DACC或0-256个QACC,支持spill。

场景三:一个函数内只有间接访问指令(第二三四类)

__attribute__((dtu_maxinum_vacc=8))
func() {L.MOVVA  // 可以通过VAB来访问到0-4095MOP      // 可以通过VAB来访问到0-4095L.VLDDA  // 可以通过VAB来访问到0-4095
}
此时必须使用dtu_maxinum_vacc,并且不能发生vacc/dacc/qacc register spill。

场景四:一个函数内只有第四类指令(12bits编码),并且vab=0,相当于直接访问,类似xiaowen的idft写法。

func() {L.VLDDA  // 全部由编译器来负责分配0-4095
}
此时不能使用dtu_maxinum_vacc,vab必须为0,全部va变量由编译器来分配,支持vacc/dacc/qacc register spill。
}

总的来说:

1、如果在一个函数内,既出现间接访问指令,又出现直接访问vacc的指令,那么编译器只能利用1024个寄存器。

2、间接访问方法,还是跟sip2.0一样,需要用dtu_maxinum_vacc来控制,像笪健的group_conv一样,能满足间接访问形式的开发需求么?// 张涛老师已经确认可以。

3、场景一二三四,编译器都会支持,哪些是算子常用的写法?

vldi.tar_t0/t1问题

一、tar赋值相关指令:

ivldi_tar.t0:将立即数imm16写入thread0的tar寄存器

ivldi_tar.t1:将立即数imm16写入thread1的tar寄存器

ivldi_tar.dual:将立即数imm16写入两个thread相同index的tar寄存器

movsr2tar:将sr的低16bit赋值给thread0的tar,将sr的高16bit赋值给thread1的相同index的tar寄存器

movtar2sr:将thread0的tar值赋给sr的低16bit,将thread1相同index的tar值赋给sr的高16bit

movsr2tar.dual:将16bit数据分别赋给thread0/thread1的tar寄存器,或者同时赋给thread0/thread1的tar寄存器

movtar:将每一个thread的tar值赋给另一个tar寄存器

二、intrinsics使用方法

1、不同线程不同tar寄存器

tar_t tar1 = __dtu_ivldi_tar.t0(imm0);

tar_t tar2 = __dtu_ivldi_tar.t1(imm1);

由于tar寄存器对compiler只看到单个线程内的数量,也就是32个。用户分线程使用时,不要误以为有64个tar,避免spill。

2、分线程使用,发生spill

tar_t tar1 = __dtu_ivldi_tar.t0(imm0, tar1); // 假如分配了thread0的tar1寄存器

tar_t tar2 = __dtu_ivldi_tar.t1(imm1, tar2); // 假如分配了thread1的tar2寄存器

// 如果tar1发生spill,会插入以下指令

movtar2sr r6, tar1 // 将两个线程的tar1保存到r6,本意是想只保存thread0的tar1.

movsr2tar tar1, r6 // 将r6值恢复给两个线程的tar1,本意是想只恢复thread0的tar1.

spill后可以恢复正确值,只是多操作了1个线程而已。

3、分线程使用,操作相同index tar寄存器

tar_t tar1 = __dtu_ivldi_tar.t0(imm0, tar1); // 假如分配了thread0的tar1寄存器

tar1 = __dtu_ivldi_tar.t1(imm2, tar1); // 假如想分配thread1的tar1寄存器,intrinsics参数需要写对。

如果想用ivldi_tar.t0/ivldi_tar.t1接口给相同tar index赋值,需要上面的使用方法。

不建议使用t0/t1的指令,使用dual指令,输入2个SR(当tar是32bits时)。

不同数据类型copy/spill测试

tar寄存器
copy
测试用例:clang/test/DTU_test/scorpio/customer/reduce_window.cc

测试结果:tar寄存器O0, O3的copy支持。

spill
测试用例:

测试结果:部分支持,待测试

div/dvr寄存器
copy
测试用例:

#include <sip30intrin.h>struct compound {v32i16x2 m0;v64i8x2 m1;v32f16x2 m2;v32i16x2 m3;v64i8x2 m4;v32f16x2 m5;v32i16x2 m6;v64i8x2 m7;v32f16x2 m8;v32i16x2 m9;v64i8x2 m10;v32f16x2 m11;v32i16x2 m12;v64i8x2 m13;v32f16x2 m14;
};
struct compound callee(v32i16x2 a0, v64i8x2 a1, v32f16x2 a2,v32i16x2 a3, v64i8x2 a4, v32f16x2 a5, v32i16x2 a6, v64i8x2 a7,v32f16x2 a8, v32i16x2 a9, v64i8x2 a10, v32f16x2 a11, struct compound b) {return b;
}
struct compound caller() {v32i16x2 a0;v64i8x2 a1;v32f16x2 a2;v32i16x2 a3;v64i8x2 a4;v32f16x2 a5;v32i16x2 a6;v64i8x2 a7;v32f16x2 a8;v32i16x2 a9;v64i8x2 a10;v32f16x2 a11;struct compound b;return callee(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, b);
}

测试结果:O0的copy支持

spill
测试用例:

测试结果:支持,待测试

va/da/qa寄存器
copy/spill
测试用例:

测试结果:部分支持,待测试

vcc_g寄存器
copy
测试用例:

测试结果:支持,待测试

spill
测试用例:

测试结果:不支持,待测试

smr寄存器
不支持copy/spill

特殊寄存器
copy/spill都不支持

vr/iv寄存器
测试用例:

测试结果:copy支持,spill不支持。待测试

sva/sda/sqa寄存器
copy
测试用例:

spill
测试用例:

maskbit寄存器
测试用例:

__attribute__((dtu_maxinum_vacc(4)))
void test(va16f16x4 va) {vbool128_t v1 = __dtu_m_remapva_f16_h_m128(va);vbool128_t v2 = __dtu_m_remapva_f16_h_m128(va);vbool128_t v3 = __dtu_m_remapva_f16_h_m128(va);vbool128_t v4 = __dtu_m_remapva_f16_h_m128(va);vbool128_t v5 = __dtu_m_remapva_f16_h_m128(va);va16i8x4 r1 = __dtu_m_remapva_s8_m128_b(v1);va16i8x4 r2 = __dtu_m_mpr_remapva_s8_m128_b(v2);va16u8x4 r3 = __dtu_m_remapva_u8_m128_b(v3);va16u8x4 r4 = __dtu_m_mpr_remapva_u8_m128_b(v4);va16f32x4 r5 = __dtu_m_mpr_remapva_f32_m128_w(v5);
}

测试结果:fatal error: error in backend: VRegs/DVRegs/QVregs spill happen.

验证elementwise在大latency下的性能

验证case:

void func(char *__restrict A, char *__restrict B, char *__restrict C) __attribute((no_mem_alias_in_vldst)){va16f32 a, b, c;
#pragma clang loop unroll_count(2)     //循环展开数会根据情况需要调整
for (int i = 0; i < 1000; i++) {a = __dtu_l_vlda(A, 1);
b = __dtu_l_vlda(B, 1);
c = __dtu_m_mop_madd_f32_va(a, b);
__dtu_l_vsta_w(c, C, 1);
}
return;
}

验证修改:将__dtu_l_vlda latency设置为202,其它参数与2.0一致。
可优化点:
1.mov指令未与madd打包

  • 汇编
.LBB0_1:                                # %for.body# =>This Inner Loop Header: Depth=1
#    cycle: 0{v.nopm.nopl.vlda vacc0, [r4], 1c.nop}
#    cycle: 1{v.nopm.nopl.vlda vacc4, [r5], 1c.nop}
#    cycle: 2{v.nopm.nopl.vlda vacc1, [r4], 1c.nop}
#    cycle: 3{v.nopm.nopl.vlda vacc5, [r5], 1c.nop}
#    cycle: 4{v.nopm.nopl.addia.s r7, r7, -1c.nop}
#    cycle: 5{v.nopm.nopl.mov r8, r6c.nop}
#    cycle: 6{v.nopm.nopl.mov r9, r6c.nop}
#    cycle: 202{v.nopm.mop.madd.f32.va vacc0, vacc0, vacc4s.nop16s.nop16c.nop}
#    cycle: 204{v.nopm.mop.madd.f32.va vacc5, vacc1, vacc5s.nop16s.nop16c.nop}
#    cycle: 214{v.nopm.nopl.vsta.w vacc0, [r8], 1c.nop}
#    cycle: 216{v.nopm.nopl.vsta.w vacc5, [r9], 1c.nop}
}

原因分析:madd依赖于vld的数据加载,需要在202cycle后,所以mov提前打包
2.st未与madd打包
原因分析:L_VSTA_w与M_MOP_MADD_f32_va存在bank冲突
调度日志:

*** Examining Available
Latency Priority QueueNumber of Queue Entries: 2SU(72):   $vacc37 = M_MOP_MADD_f32_va killed $vacc33, killed $vacc37, implicit $vab_m_s1, implicit $vab_m_s2, implicit $vab_m_dSU(73):   dead $r15 = L_VSTA_w killed $vacc0, killed $r15(tied-def 0), 1, implicit $vab_lv_s
*** Scheduling [241]: SU(72):   $vacc37 = M_MOP_MADD_f32_va killed $vacc33, killed $vacc37, implicit $vab_m_s1, implicit $vab_m_s2, implicit $vab_m_d
**** Adding to Packet[ 254] :SU(72):   $vacc37 = M_MOP_MADD_f32_va killed $vacc33, killed $vacc37, implicit $vab_m_s1, implicit $vab_m_s2, implicit $vab_m_d
**** Set BundleStart flag for insn:   $vacc37 = M_MOP_MADD_f32_va killed $vacc33, killed $vacc37, implicit $vab_m_s1, implicit $vab_m_s2, implicit $vab_m_d
Packet[254]:[0] SU(72)    $vacc37 = M_MOP_MADD_f32_va killed $vacc33, killed $vacc37, implicit $vab_m_s1, implicit $vab_m_s2, implicit $vab_m_d*** Examining Available
Latency Priority QueueNumber of Queue Entries: 1SU(73):   dead $r15 = L_VSTA_w killed $vacc0, killed $r15(tied-def 0), 1, implicit $vab_lv_s
[Scheduler] Hazard found: Read same bank VA between$vacc37 = M_MOP_MADD_f32_va killed $vacc33, killed $vacc37, implicit $vab_m_s1, implicit $vab_m_s2, implicit $vab_m_danddead $r15 = L_VSTA_w killed $vacc0, killed $r15(tied-def 0), 1, implicit $vab_lv_s
*** Finished cycle 241*** Examining Available
Latency Priority QueueNumber of Queue Entries: 1SU(73):   dead $r15 = L_VSTA_w killed $vacc0, killed $r15(tied-def 0), 1, implicit $vab_lv_s
*** Stall in cycle 242
}

3.循环展开后,没有实现vldvldvldvldmaddmaddvstvst的调度顺序

  • __restrict 只能修饰对应的指针,在intrisic后失效
  • no_mem_alias_in_vldst属性的实现中没有包含L_VLDA和L_VSTA_w,加上相应实现后测试达到预期。

Scorpio需求管理

外部需求
1、3.0算子写法的规范:gating 3.0算子大规模开发TR-37035【factor/op】支持不同GCU target的c func共存
2、3.0算子写法的规范:gating 3.0算子大规模开发 TR-36927【compiler性能优化】不再默认include sip intrinsic header,而是需要显示的include分类的intrinsic头文件 → 算子有新写法
3、CLT-266 将cast+同精度运算的pattern优化成scorpio上的混精指令 →对比krt与vector c
4、TR-35181 重构replaceConflictRegister这个pass
5、TR-37223 [Scorpio] 3.0上vr 使用 *= 运算符,会被编译成 va系列的指令
6、TR-33824 scorpio 使用extract 和insert 来绑定qacc 和 dacc时 会出现大量movda指令和spill指令,这些指令在zebu上会产生大量的idle, 需要优化 在llvm16上测试过了之后可以合入
7、TR-35557 优化 fft 指令stall dito
8、3.0算子写法的规范:对3.0算子大规模开发,有一定影响 TR-36928【compiler性能优化】算子在进行基本操作的时候,可以直接用c语言运算符进行,不需要调用函数
9、3.0算子写法的规范:对3.0算子大规模开发,有一定影响 TR-36929【compiler性能优化】探索:不加preload的编译效果
10、算子对于 Compiler的性能预期:
a. Common优化手段:3.0指令实现的超越函数,大部分优化到硬件极限的90% → done,kernel性能数据,多个超越函数一起或算子实测;
b. Common优化手段:在关闭硬件hash的场景下,优化VA bank conflict(目标: 90%的泛化2D算子,不发生VA bank conflict) → Zebu数据
c. Common优化手段:寄存器spill的优化(vacc/maskbit)(目标: 90%的泛化算子不发生) → movqa已经支持
d. 指令调度优化:(目标: 80%算子的核心循环内,不发生VA spill)
e. HW loop的优化(目标: 满足硬件指令约束条件的循环,90%可以生成HW loop指令) → 已达成
f. 针对BU模型,进行性能分析
g. SWP优化(阶段1):在tops里使能,性能不下降; 针对泛化的1D算子,进行初步优化 → (稳定的环境)
h. SWP优化(阶段2):针对泛化的1D算子,持续进行优化
i. 2D intrinsic和Convgen的性能对比

TODO:2D指令封装

内部需求(可以预见的风险)
1、长跳转/Relax scorpio (Risk)
2、Scorpio global变量支持和测试 (有global变量空间,测试是否支持)→ global 使用场景
3、va union使用问题 (PSE已经报了编译错误) → 暂时用insert/extract va,复杂场景需要支持
4、Pre-inc实现和测试 (vldqa.1自动生成,地址自增 P0)ptr++ → vldqa.1 VECTOR C
5、maskbit转换和测试 (if cond 表达→ maskbit)VECTOR C
6、TR-37785 Scorpio 3.0混合精度指令implicit Use漏掉MODE_WRK寄存器 (黎兴民_Alfred ) TCTL movsr2spr有没有这个接口
7、TR-37827 [Vector C]: vector = vector Op vector场景,测试unsigned int相关的运算生成的是int类型的汇编 (黎兴民_Alfred ) 再看一下
其他
1、TR-30250 支持main函数传参
2、TR-37167 [Benchmark][hwloop]: 循环的参数都为常量,循环次数超过1024次时,log打印不全
3、TR-27772: [LLVM]: sip30 -o0 执行bin出现virtual uint32_t sip::port_t::read32(uint32_t): Assertion c != NULL' fail 4、TR-27773: [LLVM]: sip30 -o0 执行bin出现void sip::logger_t::error(const char*, int, const char*, ...): Assertion 0’ failed.
5、TR-27792: [LLVM]: sip30 -o0编译出现fatal error: error in backend
6、汇编器/反汇编器 (QA加强测试)
7、动态栈支持和测试 (c/c++语法,标量的测试一下,看是否正确)
8、堆栈溢出检查和测试 (调研看是否能做)
9、O0支持和测试
10、Scorpio global变量支持和测试 (有global变量空间,测试是否支持)
11、DTUGEPPass的处理 (看一下是否有问题,能否移除)
12、SkipFunction的添加 (-o0也会进一些pass,开头加)
13、卡姆问题fix:目前的遗留bug
14、Add verify功能 (每个pass后加verify,验证pass是否正确) → ReplaceRegisterPass先加上
15、网络模型优化和测试
16、vectorize优化和测试
17、itineary update
18、3.0 llvm ir用-mcpu=x报错不友好 (不要segment fault,要显示target错误)

Scorpio Project相关推荐

  1. ideal如何创建dynamic web project

    步骤如下 ① file -> new -> project ② 选择 Java Enterprise -> next ③ create project from template - ...

  2. This version of Android Studio cannot open this project, please retry with Android Studio 3.5 or new

    今天github 下载一个库 导入 as 提示 This version of Android Studio cannot open this project, please retry with A ...

  3. Error:The SDK Build Tools revision (23.0.3) is too low for project ':app'. Minimum required is 25.0.

    导入github上项目的时候出现 Error:The SDK Build Tools revision (23.0.3) is too low for project ':app'. Minimum ...

  4. Error:(49, 1) A problem occurred evaluating project ':guideview'. Could not read script 'https://r

    出现问题如下: Error:(49, 1) A problem occurred evaluating project ':guideview'. > Could not read script ...

  5. IntelliJ IDEA 的Project structure说明

    IntelliJ IDEA 的Project structure可以在File->Project structure中打开,同时,在新建项目是IDE一般用向导的方式让你填写Project str ...

  6. 将Project的内容导出成单独的XPO文件

    AX跟VSS整合的版本管理可以通过创建知识库将当前层的代码全部签入到VSS中,但是如果不是一个团队开发solution,而是针对客户的需求随时做得一些小改动,一般都希望以Project的形式组织代码和 ...

  7. linux vim project,vim插件project的用法

    用任何编辑器写代码,文件管理的方便与否对编码效率影响很大.一般的IDE都有文件管理功能,并且用来的不错.在vim中,要实现较好的文件管理功能一般都靠插件.在有米实习的第一个月,自己一直用NERDTre ...

  8. android studio 同类,让Android Studio的Project视图和Anroid视图类似

    关于AS的Project与Android视图的不同,可以看我的博文 http://blog.csdn.net/siyehuazhilian/article/details/42123563 Andro ...

  9. invalid project description._[Project教程] 在Project软件中如何处理加班工时

    在微软Project软件中有处理加班工时的功能,但是我在系统课程中没有讲,为什么呢?就像Project软件中的[进度线]功能,这个功能还不够完善,所以暂时不建议大家使用.加班工时这个问题也是如此,总体 ...

最新文章

  1. Silverlight教程第四部分:使用 Style 元素更好地封装观感 (木野狐译)
  2. python上传本地文件_python3写的简单本地文件上传服务器实例
  3. OpenExpressApp对建模支持的初步计划
  4. VUE config/index.js文件配置
  5. 持续集成与自动化部署 - jenkins sonar代码质量管理平台 部署和基础使用(五)...
  6. ActionErrors 使用说明 struts1 validate 处理流程 详细教程(转)
  7. 09.VMWare虚拟机copy后网卡不是eth0解决办法
  8. Oracle 11g for Linux安装前准备工作
  9. 四款优秀的源代码扫描工具简介
  10. seay代码审计工具_代码审计入门实战
  11. 根据当前谷歌浏览器版本获取或更新更新chromedriver.exe
  12. CST学习:圆形贴片天线四元阵设计(一)阵元设计
  13. Rounding necessary
  14. JST日本压着端子SHD系列线对板连接器的PCB封装库
  15. 动态条形图展示人均GDP,最后以gif格式输出
  16. Driller分析与改进(一)
  17. python判断两个数是否互质_《算法》第一章——判断两个整数是否互质
  18. Ubuntu 16.04 如何进入系统文件/etc/profile修改内容
  19. 基于SpringBoot校园外卖配送系统的设计与实现【Java毕业设计·安装调试·代码讲解·文档报告】
  20. 32蜂鸣器天空之城代码_32篇SCI/EI论文、多项发明专利、本科生科研导师......这个华工青年,有担当!...

热门文章

  1. 服务器装win10系统很卡,刚装的WIN10系统卡顿慢?这样优化一下让你的系统飞起来!...
  2. Mysql存入emoji表情符号
  3. 记账后,快速导出账目数据到表格保存
  4. (收藏必备)cuda、cudnn、jetson等下载加速英伟达nvidia官网访问加速
  5. 打开NVIDIA官网下载CUDA和CUDNN很慢
  6. 【20140202】曼昆著《经济学原理》读书笔记
  7. 模型训练中的标注图片truncated和difficult的含义
  8. python编写一个名片_python名片 项目
  9. 我们最后一次需要你提供当前 Windows 密码
  10. 【HTML爱心】这首歌写给你听,我想请你闭上眼睛,这首可能不太动听,但是我有足够的用心~