// Test FP16 and W8 execution bridges for released group32 weights. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Clock=std::chrono::steady_clock; void Check(int rc,const char* where){if(rc)throw std::runtime_error(std::string(where)+": "+std::to_string(rc));} template std::vector Read(const std::string& path,size_t count){ std::ifstream f(path,std::ios::binary|std::ios::ate); if(!f||f.tellg()!=std::streamoff(count*sizeof(T)))throw std::runtime_error("bad input "+path); f.seekg(0);std::vector x(count); if(!f.read(reinterpret_cast(x.data()),count*sizeof(T)))throw std::runtime_error("read "+path); return x; } struct Matrix { int m,k,n,offset;bool w8; rknn_matmul_ctx ctx=0;rknn_matmul_io_attr io{}; rknn_tensor_mem *a=nullptr,*b=nullptr,*c=nullptr; std::vector scales,as; Matrix(int rows,int inner,int cols,int begin,int core,bool quant,std::span weight):m(rows),k(inner),n(cols),offset(begin),w8(quant),scales(n,1.f),as(m,1.f){ try{ rknn_matmul_info info{};info.M=m;info.K=k;info.N=n;info.iommu_domain_id=3; info.type=w8?RKNN_INT8_MM_INT8_TO_INT32:RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT32; info.B_layout=info.AC_layout=RKNN_MM_LAYOUT_NATIVE; Check(rknn_matmul_create(&ctx,&info,&io),"create"); Check(rknn_matmul_set_core_mask(ctx,static_cast(1<0?mx/127:1;} for(int j=0;j(b->virt_addr)[idx]=std::clamp(int(std::nearbyint(row[j]/scales[col])),-127,127); else static_cast<__fp16*>(b->virt_addr)[idx]=static_cast<__fp16>(row[j]); } } Check(rknn_mem_sync(ctx,b,RKNN_MEMORY_SYNC_TO_DEVICE),"sync B"); Check(rknn_matmul_set_io_mem(ctx,a,&io.A),"bind A"); Check(rknn_matmul_set_io_mem(ctx,b,&io.B),"bind B"); Check(rknn_matmul_set_io_mem(ctx,c,&io.C),"bind C"); }catch(...){Release();throw;} } ~Matrix(){Release();} void Release(){if(c)rknn_destroy_mem(ctx,c);if(b)rknn_destroy_mem(ctx,b);if(a)rknn_destroy_mem(ctx,a);if(ctx)rknn_matmul_destroy(ctx);} void Run(std::span input,std::span output,int out_stride){ const int subk=w8?16:8; for(int r=0;r0?mx/127:1;} for(int j=0;j(a->virt_addr)[idx]=std::clamp(int(std::nearbyint(input[r*k+j]/as[r])),-127,127); else static_cast<__fp16*>(a->virt_addr)[idx]=static_cast<__fp16>(input[r*k+j]); } } Check(rknn_mem_sync(ctx,a,RKNN_MEMORY_SYNC_TO_DEVICE),"sync A"); Check(rknn_matmul_run(ctx),"run"); Check(rknn_mem_sync(ctx,c,RKNN_MEMORY_SYNC_FROM_DEVICE),"sync C"); for(int r=0;r(c->virt_addr)[i])*as[r]*scales[col]:static_cast(c->virt_addr)[i]; } } }; int main(int argc,char**argv)try{ if(argc!=6)throw std::runtime_error("usage: probe DIRECTORY ROWS fp16|w8 CORES ITERATIONS"); std::string path=argv[1],mode=argv[3];int m=std::stoi(argv[2]),cores=std::stoi(argv[4]),iters=std::stoi(argv[5]); if((m!=1&&m!=128)||(cores!=1&&cores!=3)||iters<1||(mode!="w8"&&mode!="fp16"))throw std::runtime_error("invalid arguments"); auto shape=Read(path+"/shape.bin",3);int k=shape[0],n=shape[1],group=shape[2]; auto codes=Read(path+"/codes.bin",size_t(k)*n); auto scales=Read(path+"/scales.bin",size_t(n)*k/group); auto input=Read(path+"/input-"+std::to_string(m)+".bin",size_t(m)*k); std::vector weight(size_t(n)*k),output(size_t(m)*n); for(int col=0;col> matrices; int offset=0;size_t bytes=0; for(int core=0;core(m,k,width,offset,core,mode=="w8",weight));offset+=width; bytes+=matrices.back()->io.B.size; } double load_ms=std::chrono::duration(Clock::now()-start).count(); auto run=[&]{ if(cores==1){matrices[0]->Run(input,output,n);return;} // Three worker launches are included here; production uses persistent workers. std::vector threads;std::vector errors(cores); for(int c=0;cRun(input,output,n);}catch(...){errors[c]=std::current_exception();}}); for(auto&t:threads)t.join();for(auto&e:errors)if(e)std::rethrow_exception(e); }; run();run();run(); double error=0,refnorm=0,implerr=0,implnorm=0; // Validate a complete first row against scalar independent reference math. float as=1;for(int j=0;j0?ws/127:1; int64_t integer=0; for(int j=0;j(input[j]))*double(float(static_cast<__fp16>(weight[col*k+j]))); } if(mode=="w8")implemented=float(integer)*as*ws; error+=std::pow(output[col]-expected,2);refnorm+=expected*expected; implerr+=std::pow(output[col]-implemented,2);implnorm+=implemented*implemented; } const double implrmse=std::sqrt(implerr/implnorm); if(!std::isfinite(implrmse)||implrmse>1e-4)throw std::runtime_error("reference mismatch "+std::to_string(implrmse)); std::vector times; for(int i=0;i(Clock::now()-start).count());} std::sort(times.begin(),times.end()); std::cout<<"{\"path\":\""<