總綱)
GE DT用例開發(fā)總綱【免費下載鏈接】geGEGraph Engine是面向昇騰的圖編譯器和執(zhí)行器提供了計算圖優(yōu)化、多流并行、內(nèi)存復(fù)用和模型下沉等技術(shù)手段加速模型執(zhí)行效率減少模型內(nèi)存占用。 GE 提供對 PyTorch、TensorFlow 前端的友好接入能力并同時支持 onnx、pb 等主流模型格式的解析與編譯。項目地址: https://gitcode.com/cann/ge本文針對UT和ST兩類測試結(jié)合DT測試中常見的問題確定用例規(guī)范、基本要求。本文默認讀者已經(jīng)有了基本的UT/ST開發(fā)經(jīng)驗可以實現(xiàn)UT/ST用例因此關(guān)于用例的基礎(chǔ)寫法不會做詳細介紹。本文僅指導如何寫UT/ST請使用ge-dt-runnerskill編譯運行或參考docs/build.md全量執(zhí)行用例詳細指導文檔內(nèi)容適用場景測試框架指南測試基類、Graph DSL、faker、stub、checker構(gòu)造輸入、打樁、校驗輸出時查閱UT用例開發(fā)指導UT設(shè)計checklist、校驗方法、圖類UT、rt2 kernel UT編寫UT用例時查閱ST用例開發(fā)指導ST注釋要求、場景checklist、圖編譯/執(zhí)行校驗、DUMP階段列表編寫ST用例時查閱基礎(chǔ)知識何時新增DT測試用例一般來說在兩種情況下新增DT測試用例新需求開發(fā)時基于模塊設(shè)計設(shè)計并新增UT/ST用例修復(fù)問題時包含自驗證發(fā)現(xiàn)的問題、上庫時流水線發(fā)現(xiàn)的問題、上庫后被提單的問題等等。理論上只要發(fā)現(xiàn)了一個bug并且你在修復(fù)它就需要新增DT測試用例根據(jù)被測的行為你可以選擇新增UT用例或ST用例或者兩者都各增加幾個。建議的問題修復(fù)流程為DT測試工程GE倉UT/ST代碼位于tests目錄下tests目錄下的子目錄作用tests - acl_ut: ACL接口的UT用例目錄 - autofuse: 自動融合相關(guān)的UT/ST用例目錄 - benchmark: 性能用例的目錄 - bin: 測試可執(zhí)行產(chǎn)物及kernel二進制目錄 - cmake: 測試工程的公共cmake腳本 - depends: 底層依賴的樁代碼和測試輔助工具各子目錄說明如下 - slog: 日志接口樁slog、dlog、ascendalog - runtime: 昇騰運行時樁rtMalloc、rtMemcpy、rtLaunch、stream/event等 - hccl: 集合通信樁HCCL AllReduce等 - ascendcl: AscendCL API樁acl_rt、設(shè)備管理、內(nèi)存等 - op_stub: 算子實現(xiàn)樁提供fake算子實現(xiàn)供測試圖執(zhí)行器調(diào)度 - profiler: Profiling/Dump樁 - platform: 平臺/芯片信息查詢樁 - error_manager: 錯誤管理樁 - graph_tuner: 圖調(diào)優(yōu)樁 - aoe: AOEAscend Optimization Engine樁 - mmpa/mmpa2: 平臺無關(guān)的內(nèi)存/進程抽象API樁 - llm_datadist: LLM數(shù)據(jù)分發(fā)測試輔助樁 - helper_runtime: 輔助運行時樁tsd_client、grpc_server等 - aihacb_autofusion: 自動融合樁 - python: Python綁定的LLM封裝模塊 - checker: 僅頭文件的測試校驗工具mem_trace_checker、shape_checker等 - conf: 測試配置數(shù)據(jù)如error_code.json - symbol: 僅頭文件的符號形狀推斷測試輔助工具 - dflow: dflow模塊的UT/ST用例目錄含flow_graph、llm_datadist、pydflow、runner、udf等子模塊 - docs: 測試相關(guān)文檔目錄 - engines: 各執(zhí)行引擎的UT/ST用例目錄含cpueng、dvppeng、ffts_engine、hccl_engine、nn_engine、rts_engine、te_fusion - framework: 框架目錄包含UT/ST的公用測試代碼例如faker、stub、easy_graph等 - ge: GE核心模塊的UT/ST用例目錄含ut、st子目錄 - graph_metadef: 圖元數(shù)據(jù)定義模塊的UT/benchmark用例目錄 - parser: 模型解析模塊的UT/ST用例目錄 - python_tests: Python接口測試目錄 - test_c: C接口測試目錄不論是UT還是ST我們均使用googletest測試框架并提供了AddressSanitizer來做內(nèi)存類檢查、gcov來做覆蓋率統(tǒng)計。對于新增代碼UT覆蓋率要求超過90%ST覆蓋率要求超過80%。覆蓋率統(tǒng)計的具體操作請參考docs/build.md基本命令為# 帶 -c 參數(shù)運行測試自動生成覆蓋率統(tǒng)計文件到 cov/ 目錄 bash tests/run_test.sh -c [其他參數(shù)]前置條件需安裝lcovsudo apt-get install lcov和pip3 install coverage且編譯和運行環(huán)境的 gcc/gcov 版本需一致。新用例文件加入到構(gòu)建系統(tǒng)GE測試工程使用標準CMake構(gòu)建沒有自定義的注冊宏。新增用例文件后需修改對應(yīng)目錄的CMakeLists.txt。將源文件加入構(gòu)建變量# 方式一添加到顯式列出的源文件列表中 set(MY_TEST_FILES graph/optimize/foo_unittest.cc ) # 方式二已有 file(GLOB_RECURSE ...) 時新文件會被自動收錄用例規(guī)范規(guī)范1UT用例文件名為所測文件名_unittest.cc例如有一個類為Foo其位于foo.cc中那么對應(yīng)的UT用例文件名為:foo_unittest.cc。如果使用googletest的測試套測試用例的方式那么測試套的類名為FooUT代碼說明// foo_unittest.cc #include gtest/gtest.h class FooUT : public testing::Test {}; // 測試套定義類名為 FooUT TEST_F(FooUT, case_name) { // 測試用例定義 // ... }使用規(guī)范化的命名可以讓后續(xù)開發(fā)者查閱用例時更容易找到對應(yīng)文件所以務(wù)必遵守。規(guī)范2ST用例文件名為所測特性_systemtest.cc例如零拷貝的ST用例可以被命名為zero_copy_systemtest.cczero_copy_system_test.cc也是被接受的。ST用例的測試粒度為特性不要為了覆蓋率而用覆蓋率來測試某個模塊例如tensor_systemtest.cc。規(guī)范3UT用例名UT用例名使用如下模板被測接口_預(yù)期行為_場景被下劃線分割的每一部分均使用大駝峰命名。例如對FrameSelector的測試中希望測試接口SelectMainRoot當前圖為Main圖的子圖時預(yù)期行為是調(diào)用此接口后創(chuàng)建的Node仍然位于Main圖上用例名為SelectMainRoot_CreateOnMainRoot_CurrentFrameIsMainSubgraphs。有時僅期望接口成功那么用例名也可以簡寫為被測接口_場景Ok例如測試Tensor的默認構(gòu)造可以成功構(gòu)建出Tensor用例名可以為DefaultConstructor_ConstructOk。有時一個模塊分為很多子功能此時用例名的第一個字段也可以用子功能的名字子功能_預(yù)期行為_場景。規(guī)范4一個用例只校驗一個場景不論是UT還是ST用例一個用例僅應(yīng)該構(gòu)造一個場景并將其校驗完整。如果有其他場景需要新寫一個用例測試。這樣做的缺點是可能存在部分的代碼冗余和犧牲一些運行效率換來的優(yōu)點是后期易于維護。舉例說明如下用例是一個正面例子該用例通過構(gòu)造函數(shù)構(gòu)造了Tensor并完整校驗了默認值是否符合預(yù)期TEST_F(TensorUT, ConstructOk_V2) { TensorV2 tensor{{{8, 3, 224, 224}, {16, 3, 224, 224}}, // shape {ge::FORMAT_ND, ge::FORMAT_FRACTAL_NZ, {}}, // format kOnDeviceHbm, // placement ge::DT_FLOAT16, // dt nullptr}; const TensorV2 t2 tensor; EXPECT_EQ(t2.GetOriginShape(), Shape({8, 3, 224, 224})); EXPECT_EQ(t2.GetStorageShape(), Shape({16, 3, 224, 224})); EXPECT_EQ(t2.GetOriginFormat(), ge::FORMAT_ND); EXPECT_EQ(t2.GetStorageFormat(), ge::FORMAT_FRACTAL_NZ); StorageFormat storage_format{ge::FORMAT_ND, ge::FORMAT_FRACTAL_NZ, {}}; EXPECT_EQ(t2.GetFormat(), storage_format); EXPECT_EQ(t2.GetExpandDimsType(), ExpandDimsType{}); EXPECT_EQ(t2.GetPlacement(), kOnDeviceHbm); EXPECT_EQ(t2.GetDataType(), ge::DT_FLOAT16); EXPECT_EQ(t2.GetAddr(), nullptr); EXPECT_EQ(t2.GetDataint64_t(), nullptr); }可以接受的例子雖然在一個用例中校驗了多個子場景但還好都是校驗空指針場景TEST_F(ModelOutpusUT, UpdateOutputShape_Failed_WhenNullptr) { StorageShape shape {{8, 3, 224, 224}, {8, 1, 224, 224, 16}}; kernel::BuildTensorAttr attr {kOnHost, ge::DT_FLOAT16, {ge::FORMAT_NCHW, ge::FORMAT_NC1HWC0, {}}}; auto tensor TensorFaker().Shape({}).Format(ge::FORMAT_ND).DataType(ge::DT_FLOAT).Build(); // 首參數(shù)為空 auto context KernelRunContextFaker().Inputs({nullptr, tensor.GetTensor(), attr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); // 第二個參數(shù)為空 context KernelRunContextFaker().Inputs({shape, nullptr, attr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); // 第三個參數(shù)為空 context KernelRunContextFaker().Inputs({shape, tensor.GetTensor(), nullptr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); }如下例子不可接受TEST_F(SinkNodeBinTest, test_sink_node_bin_with_handle_success) { kernel::BinData bin_data; const char *bin_key1 key1; const char *bin_key2 key2; const char *empty_key ; struct FakeRuntime : RuntimeStubImpl { rtError_t rtRegisterAllKernel(const rtDevBinary_t *bin, void **handle) { static size_t registed_num 0x10; *handle (void *)(registed_num); return RT_ERROR_NONE; } }; GertRuntimeStub runtime(std::unique_ptrRuntimeStubImpl(new FakeRuntime())); auto run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key1, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10); run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key2, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10 1); run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key1, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10); // cached run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)empty_key, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10 2); run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)empty_key, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10 3); }上述例子中測試了SinkBin可以成功、測試了多次Sink同一個bin會做cache測試了empty key的場景這導致此測試用例的測試點很分散、測試意圖不清晰、用例名也無法取得簡單導致了后續(xù)的維護困難。規(guī)范5不允許將private define為public不論是UT還是ST均是基于公開接口測試即使是UT也是基于模塊的公開接口做測試。private函數(shù)、成員都是模塊的內(nèi)部實現(xiàn)不應(yīng)該成為測試或校驗點的范圍。這是為了用例的穩(wěn)定性考慮因為我們認為一個模塊的行為是相對穩(wěn)定的但是模塊的內(nèi)部實現(xiàn)是可以隨意變化的。規(guī)范6用例中不允許打開Debug級別日志或Dump圖開關(guān)Debug級別日志、Dump圖開關(guān)一般來說是用于開發(fā)者調(diào)試的工具打開Debug級別日志或Dump圖開關(guān)會極大地影響代碼的執(zhí)行速度在一個用例中打開了上述開關(guān)影響到此用例及其后繼執(zhí)行的所有用例進而導致整個UT/ST工程執(zhí)行速度很慢。因此請不要打開此開關(guān)如果為了調(diào)試方便那么僅在調(diào)試時打開上庫時需要將此開關(guān)關(guān)閉例外針對Debug日志、Dump圖測試時可以打開開關(guān)但是需要在用例結(jié)束時關(guān)閉規(guī)范7用例不可以有副作用一個用例的執(zhí)行不應(yīng)該對其他用例產(chǎn)生影響也不應(yīng)該對自己產(chǎn)生影響。例如如果用例中產(chǎn)生或修改了磁盤文件那么在TearDown中需要將對應(yīng)文件刪除或恢復(fù)如果用例中新增或修改了環(huán)境變量那么在TearDown中需要將其恢復(fù)如果用例中改變了單例或全局變量的狀態(tài)那么在TearDown中需要恢復(fù)規(guī)范8不可以依賴外部的初始化動作用例的初始化/去初始化操作應(yīng)該位于本用例的setup/teardown函數(shù)中進行一個用例不應(yīng)該依賴于外部的初始化。例如部分用例需要在執(zhí)行該用例前將某些so拷貝到指定路徑才可以正確執(zhí)行。如果一個用例依賴于某些so在特定路徑下該用例應(yīng)該在自己的setup函數(shù)中完成so的拷貝、在teardown中完成so的刪除?!久赓M下載鏈接】geGEGraph Engine是面向昇騰的圖編譯器和執(zhí)行器提供了計算圖優(yōu)化、多流并行、內(nèi)存復(fù)用和模型下沉等技術(shù)手段加速模型執(zhí)行效率減少模型內(nèi)存占用。 GE 提供對 PyTorch、TensorFlow 前端的友好接入能力并同時支持 onnx、pb 等主流模型格式的解析與編譯。項目地址: https://gitcode.com/cann/ge創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考