実はうまく動かせなかったTensorRT-RTX 1.0
以前、上記の記事を投稿したのですが、同梱してあるtensorrt_rtx.exeを実行してみただけに過ぎず、実際にアプリケーションに組み込んではいませんでした。
というのは、実はexeでは動いたのですが、コードではONNXからTensorRT-RTXのengineに変換してくれなかったのです。
8月のお盆休みの頃にTensorRT-RTX 1.1 が来て、リリースノートを見るとonnxparser関連と思われる修正がいくつかあったので、再び試してみました!
TensorRTからTensorRT-RTXへの移行
ドキュメントにも書かれているようにTensorRTとTensorRT-RTXは、クラスや名前空間が基本的に同じになっています。
なので、TensorRTが動いているなら、TensorRT-RTXがほぼそのまま動きます。
実際、推論側は、TensorRTのコードから何の変更もせずに動作しました。
onnxparser側は少し修正をしたので、参考までに貼ってみます。
当方の環境は
- Windows11
- visualstudio2022
- TensorRT-RTX 1.1
- CUDA 12.9
- TensorRT 10.11
元々のコードは、私のyolov8.onnx(FP32)をTensorRT(FP16)にするものです。
onnx2trtrtx
//my_yolov8m.onnxファイルを読み込んでTensorRTのエンジンファイルmy_yolov8m.engineを出力する関数
extern "C" __declspec(dllexport) int onnx2trt()
{
using namespace std;
using namespace nvinfer1;
using namespace Microsoft::WRL;
using namespace winrt::Windows::Storage;
class Logger : public ILogger
{
void log(Severity severity, const char* msg) noexcept override
{
// 情報レベルのメッセージを抑制する
if (severity <= Severity::kWARNING)
cout << msg << endl;
}
};
// ILogger のインスタンス化
Logger logger;
// ローカルフォルダのパスを取得
auto localFolder = ApplicationData::Current().LocalFolder();
std::wstring localAppDataPath = localFolder.Path().c_str();
// アプリケーション専用のフォルダパスを組み立てる
std::wstring appFolderPath = std::wstring(localAppDataPath) + std::wstring { L"\\WoLNamesBlackedOut" };
std::wstring engineFilePath = appFolderPath + std::wstring{ L"\\my_yolov8m_s_20250525.engine" };
std::string engineFilePathStr(engineFilePath.length(), 0);
std::transform(engineFilePath.begin(), engineFilePath.end(), engineFilePathStr.begin(), [](wchar_t c) {
return (char)c;
});
const char* engineFilePathCStr = engineFilePathStr.c_str();
// ビルダーを作成する
auto builder = std::unique_ptr<IBuilder>(createInferBuilder(logger));
// ネットワークを作成する(明示的なバッチ)
uint32_t flag = 1U << static_cast<uint32_t>
(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
auto network = unique_ptr<INetworkDefinition>(builder->createNetworkV2(flag));
// ONNXパーサーを作成する: parser
auto parser = unique_ptr<nvonnxparser::IParser>(nvonnxparser::createParser(*network, logger));
// ファイルの読み取り
const char* file_path = "./my_yolov8m_s_dy.onnx";
parser->parseFromFile(file_path, static_cast<int32_t>(ILogger::Severity::kWARNING));
// trtがモデルを最適化する方法を指定するビルド構成を作成する
auto config = unique_ptr<IBuilderConfig>(builder->createBuilderConfig());
// 設定の構成
+ config->setNbComputeCapabilities(1);
+ config->setComputeCapability(ComputeCapability::kCURRENT, 0);
// ワークスペースのサイズ
- config->setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, 100U << 20);
+ config->setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, static_cast<size_t>(4096U) << 20);
// 精度の設定
- config->setFlag(nvinfer1::BuilderFlag::kFP16); //TensorRT-RTXでは無効。ONNXでFP16とする
+ //config->setFlag(nvinfer1::BuilderFlag::kFP16); //TensorRT-RTXでは無効。ONNXでFP16とする
// 最適化構成を作成して設定する
auto profile = builder->createOptimizationProfile();
profile->setDimensions("images", OptProfileSelector::kMIN, Dims4{ 1, 3, 736, 1280 });
profile->setDimensions("images", OptProfileSelector::kOPT, Dims4{ 8, 3, 736, 1280 });
profile->setDimensions("images", OptProfileSelector::kMAX, Dims4{ 16, 3, 736, 1280 });
config->addOptimizationProfile(profile);
// クリエーションエンジン
auto engine = unique_ptr<IHostMemory>(builder->buildSerializedNetwork(*network, *config));
if (!engine)
{
cout << "Engine build failed!" << endl;
return -1;
}
//シリアル化保存エンジン
ofstream engine_file(engineFilePathCStr, ios::binary);
//assert(engine_file.is_open() && "Failed to open engine file");
engine_file.write((char*)engine->data(), engine->size());
engine_file.close();
cout << "Engine build success!" << endl;
return 0;
}
簡単に説明(そんなにわかってない)
+ config->setNbComputeCapabilities(1);
+ config->setComputeCapability(ComputeCapability::kCURRENT, 0);
TensorRT-RTXを実行するGPUの数(1)と、作成する事前コンパイルエンジンの互換性を動いているPCのGPUに合わせなさい、というような内容です。
ゲームFF14が動くようなパソコンをターゲットにしたWindowsアプリなので、配布先でそれぞれの専用のTensorRT-RTXエンジンを作ってくれたらいい、というようなイメージです。
- config->setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, 100U << 20);
+ config->setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, static_cast<size_t>(4096U) << 20);
こちら、元のコードでは実行時に次のようなエラーが出ました。
0x00007FFDBADD7F7A で例外がスローされました (WoLNamesBlackedOut.exe 内): Microsoft C++ の例外: nvinfer1::OutOfMemory (メモリの場所 0x000000448C477370)。
0x00007FFDBADD7F7A で例外がスローされました (WoLNamesBlackedOut.exe 内): Microsoft C++ の例外: nvinfer1::TRTException (メモリの場所 0x000000448C47AEF0)。
0x00007FFDBADD7F7A で例外がスローされました (WoLNamesBlackedOut.exe 内): Microsoft C++ の例外: [rethrow] (メモリの場所 0x0000000000000000)。
ワークスペースのメモリが少ないから、ということのようでしたが、単純に増やしただけではダメだったので、4GBと大幅に増やした上にstatic_cast<size_t>
もつけました。
今回は動かすことが目的だったので、本当に必要な量がどの程度なのかは確認していません。
- config->setFlag(nvinfer1::BuilderFlag::kFP16);
+ //config->setFlag(nvinfer1::BuilderFlag::kFP16); //TensorRT-RTXでは無効。ONNXでFP16とする
TensorRT-RTXでは、FP16など精度を指定することができなくなりました。
あっても邪魔はしないのですが、FP32にフォールバックするだけなので、コメントアウトしています。
余談ですが、
Python Ultralyticsでonnxエクスポートをしているですが、いつの間にかdynamic、FP16を同時に設定できるようになってました。(以前はできなかった)
TensorRTでも今後FP16など精度を指定することができなくなるようなので、ONNXで事前に設定する方法を確立しておかないといけないなと感じています。
実際にTensorRT-RTXで変換、推論してみて
変換
以前はTensorRTへの変換に6分かかっていたのですが、TensorRT-RTXは、触れ込み通りすぐに終わりました。
事前変換をして、実行時に少しJIT変換を処理が始まる、というような挙動になりました。
JIT変換はキャッシュを残しておけば、次回からいきなり実行できるそうです。
TensorRTに比べると変換はかなり早いです。
推論
TensorRTに比べてダイエットしている分、多少劣っているだろうなーと思っていたのですが・・・
すみません、これ以上は自分の口からは言えません(ライセンス的に
まさかDirectMLとxxxxxxxx
モデルによって結構性能差がある、というか、チューニングしてる・してない がありそうな気がしています。
ライセンスについて
ライセンスに、勝手にベンチマークとか載せたらダメよ、って書いてあるのですが、β版でリリース時に更新忘れてたのかも?とか思いましたが、1.1でも同じままでした。
アプリにTensorRT-RTXを実装できたら、これくらいの性能が出るよ!と表示したいと思っていたので、ライセンスに記載されていた、legalnotices☆nvidia.com にコンタクトを取ってみました。
メール送信して1カ月くらい経った時に、リリースする前に下書きを見せてください、的な返信を頂きました。
たぶん投稿しようとしている内容があまりに変でなければOKを頂けるのではないかと思います。
今後もずっとこのやり方で行くつもりなのか、よくわからないところではありますが、1.1で変えなかったのでこのまま行くのかなー?と思います。