周口网站关键词优化,黄山新洲建设集团网站,seo基础优化包括哪些内容,工程造价管理5.1 性能优化
5.1.1 计算优化
注#xff1a;看好c与cann的不同写法#xff0c;主动去理解api循环展开#xff08;Loop Unrolling#xff09;
循环展开就是把循环体复制多次#xff0c;减少循环控制的开销。比如原来循环100次#xff0c;展开成每次处理4个元素#xff0c…5.1 性能优化5.1.1 计算优化注看好c与cann的不同写法主动去理解api循环展开Loop Unrolling循环展开就是把循环体复制多次减少循环控制的开销。比如原来循环100次展开成每次处理4个元素循环25次。// 未展开的循环for(inti0;in;i){c[i]a[i]b[i];}// 展开后的循环每次处理4个元素for(inti0;in;i4){c[i]a[i]b[i];c[i1]a[i1]b[i1];c[i2]a[i2]b[i2];c[i3]a[i3]b[i3];}在Ascend C中向量化本身就有循环展开的效果因为向量指令一次处理多个元素。但有时候手动展开一些外层循环或者展开标量循环也能提升性能。展开的好处减少循环控制开销增加指令级并行度给编译器更多优化空间。展开的代价代码变长可能增加寄存器压力展开太多可能影响缓存。展开多少合适通常展开2-8倍比较合适具体要看循环体大小和硬件资源。指令流水线优化昇腾处理器的AI Core有指令流水线可以同时执行多条指令。但如果有数据依赖流水线会阻塞影响性能。减少数据依赖尽量让指令之间没有依赖或者依赖距离足够远这样流水线不会阻塞。// 有依赖的代码慢vec1Load(...);vec2Add(vec1,...);// 依赖vec1vec3Mul(vec2,...);// 依赖vec2// 减少依赖的代码快vec1Load(...);vec2Load(...);// 可以并行加载vec3Add(vec1,vec2);// 依赖vec1和vec2但vec1和vec2已经准备好了指令重排编译器会自动重排指令但有时候手动重排也能帮助编译器。指令合并用融合指令比如FusedMulAdd一条指令完成乘加减少指令数和依赖。计算融合Fusion计算融合就是把多个操作合并成一个减少中间结果的内存访问。元素级融合比如ReLU和Add可以融合成AddRelu一条指令完成加法和ReLU。// 分开写慢Add(temp,a,b);Relu(output,temp);// 融合快AddRelu(output,a,b);// output relu(a b)规约融合比如先求和再除以个数得到均值可以融合成一个操作。多算子融合比如Conv BN ReLU可以融合成一个算子减少内存访问和kernel启动开销。融合的好处是减少内存访问减少kernel启动开销但实现起来复杂一些。5.1.2 内存优化内存访问模式优化内存访问模式对性能影响很大好的访问模式能充分利用缓存和预取。连续访问尽量连续访问内存这样缓存效果好预取也有效。// 连续访问快for(inti0;in;i){c[i]a[i]b[i];// 连续访问a、b、c}// 随机访问慢for(inti0;in;i){c[i]a[indices[i]]b[indices[i]];// 通过索引访问不连续}访问对齐数据要对齐不对齐可能跨缓存行慢很多。合并访问如果多个小块数据要访问尽量合并成一次大访问。数据重用和缓存如果数据要用多次尽量在Local Memory里多待一会儿不要用完就扔。数据重用识别可以重用的数据尽量在Local Memory里缓存。// 数据重用示例Vectorfloat,256vec;vec.Load(local_tensor,offset);// 用vec做多个运算Add(result1,vec,vec);Mul(result2,vec,vec);Sqrt(result3,vec);// vec不用重复加载缓存友好的代码让访问的数据在时间和空间上都有局部性这样缓存命中率高。预取如果知道接下来要用什么数据可以提前加载让加载和计算重叠。内存对齐优化内存对齐很重要不对齐的话性能会下降很多。对齐要求向量加载通常要求数据按向量长度对齐比如256元素的向量要求256*sizeof(float)字节对齐。// 确保对齐alignas(256*sizeof(float))floatdata[1024];对齐检查写代码的时候要检查数据是否对齐不对齐的话要处理。对齐填充如果数据不对齐可以填充一些无效数据让有效数据对齐。5.1.3 并行优化多核并行策略昇腾处理器有多个AI Core可以并行执行。设计并行策略要考虑数据分块数据怎么分块让每个Core处理不同的块。负载均衡让每个Core的负载尽量均衡避免有些Core很忙有些Core很闲。同步开销并行的时候可能需要同步同步有开销要尽量减少同步次数。// 并行策略示例voidParallelProcess(){int32_tcore_idGetCoreId();int32_ttotal_coresGetTotalCores();// 计算这个Core要处理的数据范围int32_tblock_sizetotal_data/total_cores;int32_tstartcore_id*block_size;int32_tend(core_idtotal_cores-1)?total_data:startblock_size;// 处理这个范围ProcessBlock(start,end);// 如果需要同步一下Barrier();}数据分块策略数据分块要考虑块大小块太小了利用率低块太大了Local Memory装不下。通常块大小要适合Local Memory也要适合硬件特性。边界处理分块的时候要注意边界确保所有数据都被处理不重复不遗漏。分块方式可以按行分块、按列分块、按块分块选择哪种要看数据访问模式。负载均衡优化负载不均衡会影响整体性能要优化负载均衡。动态分配根据Core的负载情况动态分配任务。负载轻的Core多分点任务。工作窃取空闲的Core可以从忙碌的Core那里偷一些任务来做。自适应分块根据数据特点动态调整分块大小让每个Core的负载尽量均衡。性能监控监控每个Core的负载情况找出瓶颈调整策略。5.2 精度优化5.2.1 混合精度计算什么是混合精度混合精度就是不同地方用不同精度。比如计算用FP16快但精度低累加用FP32慢但精度高最后结果转回FP16。混合精度可以在保证精度的同时提升性能因为FP16的计算速度比FP32快很多。混合精度的实现// 概念性示例voidMixedPrecisionCompute(){// 输入是FP16Vectorhalf_t,256input_fp16;// 计算用FP16Vectorhalf_t,256temp_fp16;Mul(temp_fp16,input_fp16,input_fp16);// 累加用FP32Vectorfloat,256temp_fp32;Cast(temp_fp32,temp_fp16);// FP16转FP32// 累加Scalarfloatsum_fp32;ReduceSum(sum_fp32,temp_fp32);// 结果转回FP16Scalarhalf_tsum_fp16;Cast(sum_fp16,sum_fp32);}实现要点关键是要知道哪些地方用FP16哪些地方用FP32。通常计算用FP16累加、归一化这些用FP32。混合精度的优势性能提升FP16的计算速度是FP32的2倍内存带宽也是2倍性能提升明显。精度保证关键操作用FP32保证精度不损失太多。内存节省FP16占的内存是FP32的一半可以处理更大的模型。5.2.2 数值稳定性数值溢出和下溢FP16的数值范围比FP32小容易溢出和下溢。要避免数值问题。溢出处理如果值太大会溢出成无穷大或NaN。可以检查溢出或者用FP32计算。下溢处理如果值太小会下溢成0。可以加个小的epsilon避免除零。梯度裁剪在训练的时候可以裁剪梯度避免梯度爆炸。归一化的稳定性归一化操作比如BatchNorm要保证数值稳定。方差计算计算方差的时候如果方差太小除以方差会放大误差。可以加个epsilon。// 数值稳定的归一化Scalarfloatmean,var,eps1e-5;ComputeMeanAndVar(mean,var,input);// 加epsilon避免除零varvareps;Sqrt(var,var);// 归一化Sub(normalized,input,mean);Div(normalized,normalized,var);累加的精度累加操作容易丢失精度特别是累加很多小值的时候。Kahan累加用Kahan算法累加可以减少精度损失。分块累加先对块累加再对块结果累加精度损失小一些。5.2.3 误差分析误差来源数值计算的误差主要来自舍入误差浮点数表示有精度限制每次运算都可能舍入。截断误差算法本身的近似比如用泰勒级数近似函数。累积误差误差会累积运算越多误差越大。误差控制控制误差的方法提高精度关键计算用FP32减少舍入误差。算法改进用数值稳定的算法减少误差累积。误差监控监控误差如果误差太大调整策略。5.2.4 精度与性能平衡精度要求不同的应用对精度要求不同训练训练通常用FP16精度损失可以接受性能更重要。推理推理可能对精度要求高可以用FP32或者混合精度。特定场景有些场景对精度要求很高必须用FP32甚至更高精度。性能要求性能要求也要考虑实时性实时应用对性能要求高可能牺牲一点精度。吞吐量批量处理可能更看重吞吐量可以用FP16。延迟低延迟应用可能用FP16减少计算时间。平衡策略平衡精度和性能关键路径用高精度影响最终结果的关键计算用高精度。非关键路径用低精度不影响结果的中间计算用低精度。动态调整根据实际情况动态调整精度找到最佳平衡点。5.3 算子融合5.3.1 算子融合原理为什么需要融合算子融合可以减少内存访问融合后中间结果不用写回Global Memory减少内存访问。Kernel启动开销多个算子融合成一个减少kernel启动次数。数据重用融合后数据可以在Local Memory里重用不用重复加载。融合的收益融合的收益主要体现在性能提升减少内存访问和kernel启动性能提升明显。延迟降低减少数据传输延迟降低。资源利用更好地利用硬件资源提高利用率。5.3.2 融合策略设计可融合的算子常见的可融合组合元素级融合Add ReLU、Mul Add、Conv BN等。规约融合Sum Div求均值、Max ArgMax等。多算子融合Conv BN ReLU、MatMul Add ReLU等。融合的条件算子要能融合需要满足数据流兼容前一个算子的输出是后一个算子的输入数据流要匹配。计算兼容两个算子的计算可以合并不会冲突。内存兼容融合后内存使用要合理不能超出限制。融合策略设计融合策略要考虑融合顺序哪些算子先融合哪些后融合。融合粒度融合到什么程度是简单融合还是深度融合。融合代价融合有实现成本要考虑是否值得。5.3.3 融合算子实现简单融合简单融合就是把两个算子合并成一个kernel// 概念性示例Add ReLU融合externC__global__ __aicore__voidAddReluKernel(GlobalTensorfloatinput1,GlobalTensorfloatinput2,GlobalTensorfloatoutput,int32_ttotal_elements){LocalTensorfloatlocal_input1,local_input2,local_output;local_input1.Alloc(total_elements);local_input2.Alloc(total_elements);local_output.Alloc(total_elements);DataCopy(local_input1,input1,total_elements);DataCopy(local_input2,input2,total_elements);// 融合Add ReLUconstint32_tvector_length256;for(int32_ti0;itotal_elements;ivector_length){Vectorfloat,256vec1,vec2,vec_out;vec1.Load(local_input1,i);vec2.Load(local_input2,i);// 直接用AddRelu融合指令AddRelu(vec_out,vec1,vec2);// output relu(input1 input2)vec_out.Store(local_output,i);}DataCopy(output,local_output,total_elements);local_input1.Free();local_input2.Free();local_output.Free();}复杂融合复杂融合比如Conv BN ReLU需要仔细设计// 概念性示例Conv BN ReLU融合externC__global__ __aicore__voidConvBNReluKernel(...){// 1. 卷积计算Conv2D(...);// 2. BatchNorm不写回直接在Local Memory做BatchNormLocal(...);// 3. ReLU不写回直接在Local Memory做ReluLocal(...);// 4. 最后统一写回DataCopy(output,local_result,...);}复杂融合的好处是中间结果不用写回Global Memory但实现复杂代码维护成本高。5.3.4 融合效果评估性能评估评估融合效果要看执行时间融合后执行时间减少了多少。内存带宽内存访问减少了多少。资源利用率硬件资源利用率提升了多少。精度评估融合可能影响精度要评估精度损失融合后精度损失了多少是否可接受。数值稳定性融合后数值是否稳定会不会有溢出下溢。成本收益分析分析融合的成本和收益实现成本实现融合需要多少工作量代码复杂度增加多少。维护成本融合后的代码维护成本调试难度。收益性能提升多少是否值得投入。决策根据成本收益分析决定是否融合融合到什么程度。学习检查点学完这一篇你应该能做到这些掌握性能优化的方法包括计算优化、内存优化、并行优化。理解精度优化的技巧知道如何平衡精度和性能。理解算子融合的原理和方法能够设计和实现融合算子。能够对算子进行性能分析和优化找出瓶颈并改进。实践练习性能优化实践选择一个算子用Profiling工具分析性能找出瓶颈。尝试应用不同的优化方法比如循环展开、内存访问优化、并行优化对比优化前后的性能。混合精度实验实现一个算子的FP16版本和FP32版本对比性能和精度。尝试混合精度实现在关键地方用FP32其他地方用FP16。算子融合实践实现Add ReLU的融合算子对比融合前后的性能。尝试更复杂的融合比如Conv BN ReLU。优化案例分析分析一个复杂算子的优化案例理解优化思路和方法。总结优化经验形成自己的优化方法论。下一步掌握了高级优化技术后就可以学习调试和测试了。下一章会讲调试工具、测试方法、问题排查这些到时候你就能开发出高质量、高性能的算子了。