昇腾AI原生创新算子挑战赛(S1赛季)复盘-GreaterEqual算子
收藏回复举报
昇腾AI原生创新算子挑战赛(S1赛季)复盘-GreaterEqual算子
发表于2024-05-31 16:12:01
0 查看
  • 正文前感谢昇腾各位工作人员,没有你们的辛勤就没有我们的进步
  • 本文立意交流大赛GreaterEqual算子编译过程
  • 这道题难点在于如何实现算法公式,以及其中广播逻辑
  • 本代码源于0xcccccccc仓库代码,做了部分修改,再次感谢0xcccccccc分享
  • 本代码与LessEqual是相反算子

cke_4989.png

  • 这种算子难在如何处理广播状态,普通状态只涉及了类型转换,这里不再赘述
  • 核心处理思路如下
  • min算子 当X1<X2, X2 = X1,  X1 = X1-X2 = 0
  • sub算子 当X1>X2, X2 = X2, X1= X1-X2 >0
  • 再通过compare-select来进行数据赋值,然后输出数据

if constexpr (std::is_same_v<T, int8_t>) {
            auto float_x1 = B_x1.Get<half>();
            auto float_x2 = B_x2.Get<half>();
            Cast(float_x1, x1, RoundMode::CAST_NONE, length);
            Cast(float_x2, x2, RoundMode::CAST_NONE, length);
            Min(float_x2, float_x1, float_x2, length);
            Sub(float_x1, float_x1, float_x2, length);
            Compare(bits, float_x1, zero, CMPMODE::NE, length);
            Select(result, bits, zero, half(1), SELMODE::VSEL_TENSOR_SCALAR_MODE, length);
            Cast(inty, result, RoundMode::CAST_ROUND, length);
        }
        else {
            Min(x2, x1, x2, length);
            Sub(x1, x1, x2, length);
            if constexpr (std::is_same_v<T, int32_t>) {
                auto val = B_x1.Get<float>();
                auto float_zero = B_x2.Get<float>();
                Cast(val, x1, RoundMode::CAST_NONE, length);
                Compare(bits, val, float_zero, CMPMODE::NE, length);
            }
            else if constexpr (std::is_same_v<T, float>) {
                auto float_zero = B_x2.Get<float>();
                Compare(bits, x1, float_zero, CMPMODE::NE, length);
            }
            else { //half
                Compare(bits, x1, zero, CMPMODE::NE, length);
            }
            Select(result, bits, zero, half(1), SELMODE::VSEL_TENSOR_SCALAR_MODE, length);
            Cast(inty, result, RoundMode::CAST_ROUND, length);
        }

  • 这里有两点要注意
    • 0xcccccccc源代码不支持少维的数据处理,如下数据生成,则结果会报错

shape_x = [3,16, 16, 4]
    shape_y = [1,1,4]
    shape_out = [3,16,16,4]
    dtype = np.float16

    input_x = np.random.uniform(-100, 100, shape_x)
    input_y = np.random.uniform(-100, 100, shape_y)

  • 缘由是以下代码reduce1,reduce2针对少维广播情况,不能准确生成

auto axes = context->GetAttrs()->GetListInt(1);
    if(flag){
        context->SetTilingKey(2);
        uint32_t reduce1[20] = {0};
        uint32_t reduce2[20] = {0};
        uint32_t shape[20] = {0};
        uint32_t d = 1;
        for(int i=0;i<outshape.GetDimNum();i++){
            shape[i] = outshape.GetDim(i);
            d *= outshape.GetDim(i);
            if(inshape1.GetDim(i) != outshape.GetDim(i)) reduce1[i] = 1;
            if(inshape2.GetDim(i) != outshape.GetDim(i)) reduce2[i] = 1;
        }
        uint32_t dim = outshape.GetDimNum();
        for(int i=dim-1;i>=1;i--){
            if(!reduce1[i - 1] && !reduce2[i - 1] && !reduce1[i] && !reduce2[i]){
                dim--;
                shape[i - 1] *= shape[i];
            }else{
                break;
            }
        }

  • 进而修改源码,当然代码细节可以再去琢磨,这里就没有去深究变量等写法

uint32_t outDimNum = outshape.GetDimNum();
    const gert::StorageShape* shape1 = context->GetInputShape(0);
    std::vector<uint64_t> inshapeVector1(outDimNum, 1);
    int n = outDimNum;
    for (int j = shape1->GetStorageShape().GetDimNum() - 1; j >= 0; --j) {
        inshapeVector1[--n] = shape1->GetStorageShape().GetDim(j);
    }

    const gert::StorageShape* shape2 = context->GetInputShape(1);
    std::vector<uint64_t> inshapeVector2(outDimNum, 1);
    n = outDimNum;
    for (int j = shape2->GetStorageShape().GetDimNum() - 1; j >= 0; --j) {
        inshapeVector2[--n] = shape2->GetStorageShape().GetDim(j);
    }



    bool flag = false;
    for(int i=0;i<outDimNum;i++){
        if(inshapeVector1[i] != inshapeVector2[i]) flag = true;
    }

    uint32_t shape[20] = {0};
    uint32_t reduce1[20] = {0};
    uint32_t reduce2[20] = {0};    
    uint32_t dim = outshape.GetDimNum();

    auto axes = context->GetAttrs()->GetListInt(1);
    if(flag){
        context->SetTilingKey(2);
        
        uint32_t d = 1;

        for(int i=0;i<outDimNum;i++){
            shape[i] = outshape.GetDim(i);
            d *= outshape.GetDim(i);
            if(inshapeVector1[i] != outshape.GetDim(i)) reduce1[i] = 1;
            if(inshapeVector2[i] != outshape.GetDim(i)) reduce2[i] = 1;
        }

  • 第二个注意的点是,算子要求输出DT_BOOL,0xcccccccc输出的数据并非DT_BOOL类型
    • 这个可以去看我之前文章,如何输出DT_BOOL类型
    • 0xcccccccc是在verify_result中去修改类型,从而进行数据比对,但是如果用Less_Equal官方测试案例中的py,会报错
    • 顺带提一嘴,这个知识点还有如何输出+INF,-INF,NAN类型,可以顺带回忆一下

def verify_result(real_result, golden):
    real_result = np.fromfile(real_result, dtype=np.int8) # 从bin文件读取实际运算结果
    golden = np.fromfile(golden, dtype=np.bool_) # 从bin文件读取预期运算结果

  • 重头戏在如何处理广播数据,明白以下代码就OK了
  • 这段代码卡了我许久,后面通过代入具体数据算是理解了要义
  • 如何用没有广播的数据表示广播后的数据
  • 其实也不难,回头看直骂自己傻(⊙_⊙)?笔

uint32_t d[21] = {0};
        uint32_t dn1[21] = {0};
        uint32_t dn2[21] = {0};
        dim = dim - 1;//2
        d[dim] = dn1[dim] = dn2[dim] = 1;//d2=dn12=dn22 = 1
        for(int k=dim-1;k>=0;k--){//k=1 K=0
            d[k] = d[k + 1] * shape[k];//d1=d2*shape[1] d0=d1*shape[0]
            if(reduce1[k] == 0){
                dn1[k] = dn1[k + 1] * shape[k];
            }else{
                dn1[k] = dn1[k + 1];
            }
            if(reduce2[k] == 0){
                dn2[k] = dn2[k + 1] * shape[k];
            }else{
                dn2[k] = dn2[k + 1];
            }
        }

        std::cout <<"d:" << " ";
        for(int i=0;i<=dim;i++){
            std::cout <<  d[i] << " ";
        }
        std::cout << std::endl;

        std::cout <<"dn1:" << " ";
        for(int i=0;i<=dim;i++){
            std::cout <<  dn1[i] << " ";
        }
        std::cout << std::endl;        

        std::cout <<"dn2:" << " ";
        for(int i=0;i<=dim;i++){
            std::cout <<  dn2[i] << " ";
        }
        std::cout << std::endl;


        std::cout <<"dim:" << (dim-1) << " ";
        std::cout << std::endl;


        for(int j=0;j<count;j++){//256  count = totalLength /shape[dim - 1]
            uint32_t start1 = 0, start2 = 0;
           
            for(int k=dim-1;k>=0;k--){             
                std::cout << " k:["<< k << "] ";   
                if(reduce1[k] == 0){
                    start1 += dn1[k + 1] * (j / d[k + 1] % shape[k]);
                }
                std::cout <<" s1:["<< start1 << "] "; 
                if(reduce2[k] == 0){
                    start2 += dn2[k + 1] * (j / d[k + 1] % shape[k]);
                }
                std::cout <<" s2:[" <<start2 << "] "; 

            }
            std::cout << std::endl;
        }

  • 最后贴出测试案例
shape_x = [3,16, 16, 4]
    shape_y = [1,1,4]
    shape_out = [3,16,16,4]
    dtype = np.float16

    input_x = np.random.uniform(-100, 100, shape_x)
    input_y = np.random.uniform(-100, 100, shape_y)

cke_880171.png

shape_x = [3,16, 16, 4]
    shape_y = [1,16,1,4]
    shape_out = [3,16,16,4]
    dtype = np.float16

cke_898225.png

我要发帖子