Control Functions

控制函数(control functions)主要包括控制BMLang使用时的初始化、模式设置、启动编译生成目标文件等。

控制函数常用于BMLang程序的Tensor和Operator之前和之后。 比如在写Tensor和Operator之前,可能需要做初始化和模式设置。 在完成Tensor和Operator编写之后,可能需要启动编译和反初始化。

初始化函数

初始化Function,在一个BMLang程序程序的开头使用。 在BMNetC等前端下编写新算子时,内部已经init,用户不需要再init。

初始化函数接口如下所示,包括模式、芯片型号、输出目录的选择。

void init(
  BmLangMode mode,  // [in] set mode
  const string& chip_name = "BM1682", // [in] set target Sophon TPU
  const string& out_dir = ""); // [in] set bmodel save directory, default: ./compilation

模式设置

BMLang有三种工作模式:COMPUTE_CPU, COMPILE_TPU 和 BOTH 模式。可以通过下面的接口实现:

在初始化时已经设置了模式,初始化后也允许更改模式,但是不管如何更改,都要保证Tensor和Operator语句执行过程中所处的模式是一致的。

// [in] mode
void set_mode(BmLangMode mode);

typedef enum bmlang_mode {
  COMPUTE_CPU,
  COMPILE_TPU,
  BOTH,
  UNKNOWN_MODE
} BmLangMode;
  • 计算模式(COMPUTE_CPU):使用CPU来模拟计算,该模式是用来Debug BMLang code的计算是否正确。

  • 编译模式(COMPILE_TPU):面向TPU进行编译,生成runtime可运行TPU的bmodel。

  • BOTH模式:BOTH同时包含COMPUTE_CPU和COMPILE_TPU。

量化计算模式

当前很多深度学习网络被量化来减小模型系数量,以及提升网络在后端处理器中的运行性能。 然而,由于后端处理器的计算限制各不相同(不如位宽限制),导致了相同量化计算operator在不同后端处理器有不同的计算结果。

这里BMLang提供了量化计算模式选择接口,以便用户在调试时选择需要的CPU模拟计算 (CPU模拟计算只发生在BMLang工作模式为COMPUTE_CPU或者BOTH)。

量化计算模式选择接口如下所示,其中

  • DEFAULT_QUANTIZE:默认的量化计算方式,CPU模拟计算时,所有中间累加结果是INT32。

  • TPU_DEBUG:对应于SOPHON TPU的量化计算,CPU完全模拟TPU的计算结果。比如TPU某些中间结果位宽是16bit,CPU模拟计算同样模拟了16bit位宽的情况。

  • TFLITE:对应Tensorflow Lite的计算方式,CPU模拟计算在处理Scale值时,会采用TFLite的计算方式。

    // [in] mode
    void set_quantize_mode(BmLangQuantizeMode mode);
    
    typedef enum {
       TFLITE,
       TPU_DEBUG,
       DEFAULT_QUANTIZE,
       UNKNOWN_QUANTIZE_MODE
    } BmLangQuantizeMode;
    

启动Compile

对初始化到当前的BMLang程序进行编译,生成目标芯片的二进制可执行文件。

启动编译的接口如下所示,其中name为用户设置,Runtime时通过指定该name可启动对应的计算。详细请见NNToolchain文档的 BMRuntime。

opt_level为编译优化级别,目前提供0,1,2三种优化级别,级别越高BMCompiler对性能优化程度越高, 但是BMCompile生成TPU可执行文件时所需要的时间开销也越大。

dyn_cpl表示是否选用动态编译,false: 不采用动态编译,使用静态编译;true:则是使用动态编译。 若使用的是静态编译,在Runtime时,输入的Tensor的shape则是固定不可变的。 若使用的是动态编译,在Runtime时,用户可任意变化输入Tensor的shape值,只要shape值都小于启动 compile时的shape设定即可。

enable_profile即是否打开编译时的Profiling功能。若为true,则启动compile后,会在目标目录中生成 一个编译时的Profile文本文件。Profile文本文件说明请参考NNToolchain中的Profile章节。

void compile(
  const string& name,  // [in] set funtion name
  int opt_level = 2,   // [in] set optimization level
  bool dyn_cpl = false,  // [in] if use dynamic compile
  bool enable_profile = false); // [in] if generate profiling result

SOPHGO的编译器可在编译时对算子的输出结果进行比对验证,从而保证编译后生成的结果是正确的。若遇到 错误,SOPHGO编译器可报告出那个算子的计算结果出了问题,从而便于调试Compile。

要达到这一功能,需要提供进行比对验证的参考结果,这个参考由BMLang使用者来控制。 使用方式请见以下接口。

接口中,inp_name为要编译的网络的所有输入Tensor名字,inp_data则对应inp_name的数据指针,inp_num为整个网络输入Tensor的数量。 BMCompiler会在编译启动时,使用这些输入数据进行一边编译生成TPU可执行文件,一边使用TPU的c model进行模拟计算。

ref_name为用户想要比对验证的中间Operator输出Tensor的名字,ref_data则对应ref_name的数据指针,ref_num为参考结果的数量。 BMCompiler在编译启动时,会将TPU C model模拟计算的结果与这些给入的参考结果进行比对,若有错则进行报错。

fixpoint_cmp_margin为定点计算结果所运行的最大误差,默认是0。

void compile_with_check(
    const std::string& name,  // [in] set function name
    char** inp_name,          // [in]
    void** inp_data,          // [in]
    int inp_num,              // [in]
    char** ref_name,          // [in]
    void** ref_data,          // [in]
    int ref_num,              // [in]
    int opt_level = 2,        // [in]
    bool dyn_cpl = false,     // [in]
    bool enable_profile = false, // [in]
    int  fixpoint_cmp_margin = 0); // [in]

这里提供了另一种形式来启动编译时比对验证,如下接口所示。 其中inp_tensor为BMLang编程时所有输入Tensor,ref_tensor则是要进行比对验证的Tensor。

这里要注意的是,该接口仅在BMLang工作模式为BOTH时有效。

void compile_with_check(
    const std::string& name,  // [in] set function name
    std::vector<Tensor*> inp_tensor, // [in]
    std::vector<Tensor*> ref_tensor, // [in]
    int opt_level = 2,               // [in]
    bool dyn_cpl = false,            // [in]
    bool enable_profile = false,     // [in]
    int  fixpoint_cmp_margin = 0);   // [in]

反初始化

在BMLang程序最后,需要进行反初始化结束。只有在反初始化后,之前所启动的 Compile生成TPU可执行目标执行才会存盘到所指定的输出目录中。

void deinit();