| CANN训练营第二季社区任务 floor,Add the readme for the floor operator
Co-authored-by: xchencehn<xchencehn@163.com>
# message auto-generated for no-merge-commit merge:
!162 merge master into master
CANN训练营第二季社区任务 floor
Created-by: xchencehn
Commit-by: xchencehn
Merged-by: cann-robot
Description: ### 1 背景介绍
cann训练营二期算子开发任务
参考版本内置Floor算子的TBE实现,在昇腾NPU上使用Ascend C编程语言实现相同功能的算子。
1. 参考内置算子实现,需实现相同功能,包含相同数据类型和相同数据格式,其中int64,double数据类型可暂不支持,广播操作可暂不支持。
2. 算子性能要求持平原有TBE实现算子,当前可暂只支持在使用所有核进行计算时性能需不低于原有TBE算子95%。
3. TBE参考实现:
  kernel实现:/usr/local/Ascend/ascend-toolkit/latest/opp/built-in/op_impl/ai_core/tbe/impl/dynamic/
  API路径:/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/tbe/dsl
### 2 Floor算子现状分析
当前Floor算子的TBE实现如下:
```
@register_operator_compute("Floor", op_mode="dynamic", support_fusion=True, support_bfp16=True)
def floor_compute(input_x, output_y, kernel_name="floor"):
dtype_x = input_x.dtype.lower()
if not tbe_platform.api_check_support("tbe.dsl.floor", dtype_x):
input_x = tbe.cast_to(input_x, "float16")
if tbe_platform.api_check_support("tbe.dsl.floor", "f322f32"):
x_f32 = tbe.cast_to(input_x, "float32")
res = tbe.floor(x_f32, "float32")
else:
res = tbe.floor(input_x)
res = tbe.cast_to(res, dtype_x)
return res
@register_operator("Floor")
@para_check.check_op_params(para_check.REQUIRED_INPUT, para_check.REQUIRED_OUTPUT, para_check.KERNEL_NAME)
def floor(input_x, output_y, kernel_name="floor"):
...
check_list = ("bfloat16", "float16", "float32")
para_check.check_dtype(input_dtype, check_list, param_name="input_x")
...
```
支持 bfloat16 , float16 , float32 三中数据格式,计算逻辑如下:
1. 如果 输入的 bfloat16 ,就转成 float16
2. 然后看只要硬件支持 f322f32 floor ,就把输入转为 float32 调用tbe.floor,只有不支持 f322f32 floor 才使用 float16 调用tbe.floor
3. 计算完成后转回原来输入的数据类型
```mermaid
graph TD
A["开始 floor_compute"] --> B{"输入类型 dtype_x == bfloat16 ?"}
B -- 是 --> C["将输入 cast_to float16"]
B -- 否 --> D["保持输入类型不变"]
C --> E{"硬件支持 f32→f32 floor ?"}
D --> E
E -- 是 --> F["将输入 cast_to float32<br>执行 tbe.floor(float32)"]
E -- 否 --> G["执行 tbe.floor(当前类型)<br>(如 float16)"]
F --> H["结果 cast_to 原始 dtype_x"]
G --> H
H --> I["返回最终结果"]
```
### 3 Ascend C 算子设计
产品支持:
   Atlas A2 训练系列产品/Atlas 800I A2推理产品
接口支持:
   适配Aclnn接口和图模式调用
数据类型支持:
| 名称 | 类别 | dtype | format | shape | 介绍 |
| --- | --- | --- | --- | --- | --- |
| Input | 输入 | bfloat16, float16, float32 | ND | all | 输出 |
| y | 输出 | bfloat16, float16, float32 | ND | 同输入 | 输出 |
Host侧设计:
不需要广播,算子计算过程不涉及数据的维度信息,故在host侧将数据视为一维向量,仅考虑数据个数,不考虑数据维度信息。
任务均分:coreNum 根据输入长度和块大小动态调整,确保每个核心处理的数据块数均匀。
批量搬运:tileBlockNum 和 tileDataNum 计算单次搬运的数据量,通过 finalSmallTileNum 和 finalBigTileNum 确定小核/大核的搬运次数,将多次搬运合并为批量操作,减少冗余开销。尾块的处理逻辑确保不完整块也能被合并到计算流程中,避免数据碎片。
1) 分核策略
Host 侧将输入视为一维向量,按总元素数切分;
每个核至少处理 512 个元素,且处理数据需满足 512B 对齐;
将对齐后的输入按 block 均分到各核,不能整除时前部分核会多分 1 个 block,形成大小核(big/small core);
每个核心内部根据 UB 大小计算单次可处理的 tile 大小,核心数据再分成若干 tile + 1 个尾块。
2) 数据分块和内存优化策略
开启double buffer;
根据UB内存大小和预定义的BLOCK_SIZE及BUFFER_NUM,计算出每个Tile块的数据数量;
将输入数据按照计算出的Tile块大小进行切分,计算出每个core需要处理的数据块数量和最后一个block的剩余数据量;
将计算出的切分参数(如每个core的数据量、Tile块大小等)设置到FloorTilingData对象中。
3) tilingkey规划策略
不涉及
Kernel侧设计:
   kernel执行分为Init和Process两个阶段,其中Process再分为CopyIn、Compute、CopyOut三步;
   AscendC::Floor支持float16、float32,将bfloat16转成float32进行计算, 其他直接计算;
   开启double buffer,将流水并行,提高运算效率。
```mermaid
graph TD
A["开始 floor_compute"] --> B{"输入类型 TYPE_X == float32 ?"}
B -- 否 --> C[" Ascend::Cast 到 float32"]
C --> E["执行 Ascend::Floor 运算"]
E --> F[" Ascend::Cast 到TYPE_X"]
B -- 是 --> H["执行 Ascend::Floor 运算"]
F --> G["返回最终结果"]
H --> G
```
### 4 算子测试
采用aclnn调用测试
See merge request: cann/ops-math!162 | 3 个月前 |