/*
* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TRT_PARSER_UTILS_H
#define TRT_PARSER_UTILS_H
#include
#include
#include
#include
#include
#include
#ifndef _MSC_VER
#include
#else
#define NOMINMAX
#include
#endif
#include "NvInfer.h"
namespace parserutils
{
//輸出parserName相關的錯誤訊息,優先使用getLogger()來輸出,如果找不到,則使用std:cerr來輸出
#define RETURN_AND_LOG_ERROR_IMPL(ret, message, parserName) \
do \
{ \
std::string errorMsg = parserName + std::string{message}; \
if (getLogger()) getLogger()->log(nvinfer1::ILogger::Severity::kERROR, errorMsg.c_str()); \
else std::cerr << "WARNING: Logger not found, logging to stderr.\n" << errorMsg << std::endl; \
return (ret); \
} while (0)
// Helper function to compute unpadded volume of a Dims (1 if 0 dimensional)
//計算Dims各維的乘積,即體積
inline int64_t volume(const nvinfer1::Dims& d)
{
int64_t v = 1;
for (int64_t i = 0; i < d.nbDims; i++)
v *= d.d[i];
return v;
}
//在Unix及Windows系統下,輸出當前記憶體的使用情況
// Show some debugging output about how much memory is free
inline void printMem(const char* where)
{
#if !defined _MSC_VER && !defined __QNX__
//代表是一般的Unix系統?
const unsigned mb = 1024 * 1024;
//實體記憶體的page數量
auto pages = static_cast<uint64_t>(sysconf(_SC_PHYS_PAGES));
//當前實體記憶體中空著的page數量
auto avPages = static_cast<uint64_t>(sysconf(_SC_AVPHYS_PAGES));
//page的大小,單位是byte
auto pageSize = static_cast<uint64_t>(sysconf(_SC_PAGE_SIZE));
//輸出當前空著的記憶體大小及總記憶體大小
std::cout << " (memory) " << where << " : Free(MB) = " << avPages * pageSize / mb << " total(MB)=" << pages * pageSize / mb << std::endl;
#elif !defined __QNX__
//代表是Windows系統
const unsigned mb = 1024 * 1024;
MEMORYSTATUSEX statex;
//在呼叫GlobalMemoryStatusEx前須設定好dwLength
statex.dwLength = sizeof(statex);
//填滿statex資料結構
GlobalMemoryStatusEx(&statex);
std::cout << " (memory) " << where << " : Free(MB) = " << statex.ullAvailPhys / mb << " total(MB)=" << statex.ullTotalPhys / mb << std::endl;
#endif
}
//回傳資料型別的大小
// Compute size of datatypes
inline unsigned int elementSize(nvinfer1::DataType t)
{
switch (t)
{
case nvinfer1::DataType::kINT32: return 4;
case nvinfer1::DataType::kFLOAT: return 4;
case nvinfer1::DataType::kHALF: return 2;
case nvinfer1::DataType::kINT8: return 1;
}
//如果參數t不是上面任一種型別就會報錯
assert(0);
return 0;
}
//將nvinfer1::Dims轉成人看得懂的形式輸出
inline std::ostream& operator<<(std::ostream& o, const nvinfer1::Dims& dims)
{
o << "[";
for (int i = 0; i < dims.nbDims; i++)
//如果i為0則不用加逗號
o << (i ? "," : "") << dims.d[i];
o << "]";
return o;
}
//將nvinfer1::DataType轉成人看得懂的形式輸出
inline std::ostream& operator<<(std::ostream& o, nvinfer1::DataType dt)
{
switch (dt)
{
case nvinfer1::DataType::kINT32: o << "Int32"; break;
case nvinfer1::DataType::kFLOAT: o << "Float"; break;
case nvinfer1::DataType::kHALF: o << "Half"; break;
case nvinfer1::DataType::kINT8: o << "Int8"; break;
}
return o;
}
/*
nvinfer1::DimsCHW
定義於TensorRT/include/NvInfer.h
*/
//從Dims中抽取CHW三維的長度後回傳
inline nvinfer1::DimsCHW getCHW(const nvinfer1::Dims& d)
{
assert(d.nbDims >= 3);
//因為CHW必為末三維
return nvinfer1::DimsCHW(d.d[d.nbDims - 3], d.d[d.nbDims - 2], d.d[d.nbDims - 1]);
}
/*
獲取Dims的末三維,如果Dims的長度小於三,就從前面用filter這個數字來補上?
*/
inline nvinfer1::DimsCHW getCHWWithExpansion(const nvinfer1::Dims& d, int filler)
{
if (d.nbDims == 0)
return nvinfer1::DimsCHW(filler, filler, filler);
else if (d.nbDims == 1)
return nvinfer1::DimsCHW(filler, filler, d.d[0]);
else if (d.nbDims == 2)
return nvinfer1::DimsCHW(filler, d.d[0], d.d[1]);
else
return nvinfer1::DimsCHW(d.d[d.nbDims - 3], d.d[d.nbDims - 2], d.d[d.nbDims - 1]);
}
/*
因為Dims的後三維必為CHW,
所以這裡將倒數第三維之前的所有維度給合併成一個維度,
當作batch size
*/
inline int combineIndexDimensions(int batchSize, const nvinfer1::Dims& d)
{
int x = batchSize;
for (int i = 0; i < d.nbDims - 3; i++)
x *= d.d[i];
return x;
}
//無條件進位的除法
template <typename A, typename B>
inline A divUp(A m, B n)
{
return (m + n - 1) / n;
}
} // namespace parserhelper
#endif // PARSER_HELPER_H
觀察RETURN_AND_LOG_ERROR_IMPL
這個macro,可以發現兩個特別之處,一是在除最後一行外的每行末尾都加上了\
,二是使用do{} while(0)
這個看似沒用的敍述來包住函數的主要內容。有關這兩點,詳見C multiline macro。
這個檔案中對<<
進行了兩次overload,分別用於輸出nvinfer1::Dims
及nvinfer1::DataType
型別的物件,詳見C++ Overload stream insertion operator and stream extraction operator。
詳見C predefined macros __FILE__,__LINE__,__func__。
printMem
函數裡用到了__QNX__
這個macro。
根據Wiki - QNX,QNX是一個面向嵌入式系統的類Unix作業系統。
而根據Pre-defined Compiler Macros,在QNX 4.x作業系統上,編譯器會有預定義的macro__QNX__
。
printMem
函數裡用到了sysconf
這個函數。
根據Linux Programmer’s Manual - SYSCONF(3),sysconf
函數的簽名為:
#include
long sysconf(int name);
其作用為:
get configuration information at run time
即獲取程式運行時的config資訊。
以下是printMem
中用到的sysconf
的參數:
根據sysinfoapi.h header,Windows作業系統下的sysinfoapi.h
檔裡,定義了MEMORYSTATUSEX
資料結構及GlobalMemoryStatusEx
函數。
而根據MEMORYSTATUSEX structure:
Contains information about the current state of both physical and
virtual memory, including extended memory.
The GlobalMemoryStatusEx function stores information in this structure.
這個資料結構裡記錄了實體及虛擬記憶體(包含了擴展記憶體)的當前狀態。GlobalMemoryStatusEx
函數則用於將資訊存入這個資料結構中。
以下為MEMORYSTATUSEX
裡的變數:
MEMORYSTATUSEX
資料結構本身的大小。在呼叫GlobalMemoryStatusEx
之前必須將這個值設定好。根據GlobalMemoryStatusEx function,GlobalMemoryStatusEx
函數的簽名為:
BOOL GlobalMemoryStatusEx(
LPMEMORYSTATUSEX lpBuffer
);
其作用為:
Retrieves information about the system's current usage of
both physical and virtual memory.
即獲取當前系統中實體及虛擬記憶體的使用情況。
C multiline macro
C++ Overload stream insertion operator and stream extraction operator
C predefined macros __FILE__,__LINE__,__func__
Wiki - QNX
Pre-defined Compiler Macros
Linux Programmer’s Manual - SYSCONF(3)
sysinfoapi.h header
MEMORYSTATUSEX structure
GlobalMemoryStatusEx function