이 글은 ONNX-MLIR Github에서 제공하는 커스텀 가속기 추가 가이드라인에 대한 한국어 해석을 담고 있습니다.
일반적으로 ONNX-MLIR 상에서는 커스텀 가속기를 플러그인 형태로 처리하며, ONNX-MLIR을 빌드하거나 ONNX 모델을 컴파일할 때 온-오프할 수 있는 방식으로 동작한다. 이는 보통 CMake를 통해서 이루어진다. NNPA 가속기 예제를 보면서 프로세스를 살펴보자.
ONNX-MLIR에서는 모든 가속기 관련 코드를 src/Accelerators 디렉토리 아래 별도 폴더로 저장한다. 즉 새로운 가속기를 추가하기 위해서는 해당 디렉토리에 가속기 전용 하위 디렉토리를 생성해야 한다.
생성한 폴더 이름은 ONNX-MLIR에서 해당 가속기의 이름으로 사용되고, 다음 용도로도 활용된다.
CMake가 해당 가속기 디렉토리 아래의 코드들을 빌드한다.
CMake 변수 ONNX_MLIR_ACCELERATORS를 설정해서 아래와 같이 빌드할 수 있다.
$ cd build
$ cmake .. -DONNX_MLIR_ACCELERATORS='accel1;accel2'
잘 모르겠다면 AccelNNPAHowToUseAndTest.md 문서를 확인해보자.
onnx-mlir 툴을 사용할 때, 해당 가속기를 위한 모델 컴파일 옵션을 사용 가능하다.
--maccel 옵션을 사용해서 수행할 수 있다.
$ onnx-mlir --maccel='가속기명' model.onnx
# --maccel 옵션은 CMake에서 빌드된 가속기만 사용 가능
onnx-mlir-opt 명령어를 실행할 때, 가속기 관련 최적화 패스 활성화가 가능하다.
onnx-mlir 툴을 사용할 때와 마찬가지로 --maccel 옵션을 추가해서 수행 가능하다.
$ onnx-mlir-opt --maccel='가속기명' --'패스명' model.mlir
# --maccel 옵션은 onnx-mlir과 마찬가지로 CMake에서 빌드된 가속기만 사용 가능
폴더 내부 구조의 경우 각 가속기에 따라 유동적으로 설정할 수 있으나 ONNX-MLIR의 최상위 폴더 구조를 따르는 것을 권장하고 있다.
각 가속기는 특정 매크로를 정의하고 이를 onnx_mlir::accel::Accelerator에 포함해야 한다. (잘 이해되지 않는다면 NNPA 예제를 보자. NNPAAccelerator.hpp를 src/Accelerators/NNPA/NNPAAccelerator.hpp에서 정의하고, 이를 Accelerator.hpp에서 include하고 있다.) 필수적으로 정의해야 하는 매크로는 다음과 같다.
- INSTRUMENTSTAGEENUM<accel_name>
- INSTRUMENTSTAGECL_ENUM<accel_name>
- PROFILEIRCL_ENUM<accel_name>
- OPTREPORTENUM<accel_name>
- OPTREPORTCL_ENUM<accel_name>
만약 가속기 이름이 ACCEL1이면 아래 예시와 같이 정의한다.
#define INSTRUMENTSTAGE_ENUM_ACCEL1
#define INSTRUMENTSTAGE_CL_ENUM_ACCEL1
#define PROFILEIR_CL_ENUM_ACCEL1
#define OPTREPORT_ENUM_ACCEL1
#define OPTREPORT_CL_ENUM_ACCEL1
MLIR에서 코드를 작성할 때는 보통 다이얼렉트와 패스를 설계한다. ONNX-MLIR도 마찬가지로, 가속기 지원을 위해서는 ONNX-MLIR에 해당 가속기에 맞는 다이얼렉트와 패스를 등록해야 한다.
ONNX-MLIR에서는 가속기 통합(지금 이 프로세스)을 위해 기본 클래스 onnx_mlir::accel:Accelerator를 제공하며 이를 상속해 커스텀 가속기 코드(다이얼렉트와 패스)를 작성할 수 있다. 사용자는 다이얼렉트와 패스 작성을 마친 뒤, 이를 등록하는 훅을 정의해야 한다.
다이얼렉트와 패스를 등록하는 훅은 다음과 같다.
//===--------------------------------------------------------------------===//
// onnx-mlir 관련 훅
//===--------------------------------------------------------------------===//
/// 해당 가속기에 필요한 변환을 추가하는 역할
virtual void addPasses(mlir::OwningOpRef<mlir::ModuleOp> &module,
mlir::PassManager &pm,
onnx_mlir::EmissionTargetType &emissionTarget) const = 0;
//===--------------------------------------------------------------------===//
// onnx-mlir-opt 관련 훅
//===--------------------------------------------------------------------===//
/// 가속기에 필요한 MLIR 다이얼렉트 등록
virtual void registerDialects(mlir::DialectRegistry ®istry) const = 0;
/// 가속기용 패스 등록 (명령어 옵션에서 사용 가능)
virtual void registerPasses(int optLevel) const = 0;
//===--------------------------------------------------------------------===//
// onnx-mlir 및 onnx-mlir-opt 공통 훅
//===--------------------------------------------------------------------===//
/// 가속기 패스 설정
virtual void configurePasses() const = 0;
//===--------------------------------------------------------------------===//
// onnx-to-krnl 변환 관련 훅
//===--------------------------------------------------------------------===//
/// TensorType을 MemRefType으로 변환
virtual mlir::MemRefType convertTensorTypeToMemRefType(
const mlir::TensorType tensorType) const = 0;
/// ONNXToKrnl 변환 대상 설정
virtual void conversionTargetONNXToKrnl(
mlir::ConversionTarget &target) const = 0;
/// ONNXToKrnl 변환 패턴 설정
virtual void rewritePatternONNXToKrnl(mlir::RewritePatternSet &patterns,
mlir::TypeConverter &typeConverter, mlir::MLIRContext *ctx) const = 0;
//===--------------------------------------------------------------------===//
// krnl-to-llvm 변환 관련 훅
//===--------------------------------------------------------------------===//
/// KrnlToLLVM 변환 대상 설정
virtual void conversionTargetKrnlToLLVM(
mlir::ConversionTarget &target) const = 0;
/// KrnlToLLVM 변환 패턴 설정
virtual void rewritePatternKrnlToLLVM(mlir::RewritePatternSet &patterns,
mlir::LLVMTypeConverter &typeConverter, mlir::MLIRContext *ctx) const = 0;
ONNX-MLIR에는 다양한 패스가 존재하지만 가속기 통합에 대해서는 두 개의 핵심 패스에 대한 훅만 제공한다. onnx-to-krnl, krnl-to-llvm만 제공하는데, 이 두 패스가 onnx-mlir의 첫 번째 및 마지막 변환 패스이기 때문이다. (ONNX-MLIR은 크게 ONNX Model > ONNX Dialect > Krnl Dialeact > LLVM 순으로 변환을 제공한다.)
onnx-to-krnl 패스
krnl-to-llvm 패스
예시로, ONNX-MLIR에서는 NNPA 가속기를 Built-in으로 지원한다. 이 가속기는 ZHigh, ZLow 다이얼렉트를 가지고 있다.
즉, ONNX 연산자를 ZHigh 다이얼렉트로 변환하고 KrnlToLLVM 변환 과정에서 ZLow 다이얼렉트로 변환하는 구조이다. (ONNX Model > ZHigh Dialect > ZLow Dialect > LLVM)
가속기 관련 테스트는 test 폴더 내에 배치해야 한다.
ONNX-MLIR에서는 두 가지 유형의 테스트를 제공한다.
test/mlir/accelerators/'가속기명'/
test/accelerators/'가속기명'/
6줄이지만..
src/Accelerators 디렉토리에 가속기 전용 폴더를 만들고, CMake 변수와 --maccel 옵션을 통해 가속기 코드를 빌드 및 컴파일 옵션으로 등록한다.
필수 매크로를 정의하고 onnx_mlir::accel::Accelerator를 상속받아 다이얼렉트와 패스 등록을 위한 후크를 구현하여 커스텀 기능을 통합한다.
MLIR 패스 검증을 위한 LIT 테스트는 test/mlir/accelerators, 기타 유닛/기능 테스트는 test/accelerators 디렉토리에 배치하여 가속기 기능을 검증한다.
출처: https://github.com/onnx/onnx-mlir/blob/main/docs/AddCustomAccelerators.md