Commit d8f53cee authored by yangql's avatar yangql
Browse files

Initial commit

parents
############################ 在线安装依赖 ###############################
#cd ./3rdParty
#pip install rbuild-master.tar.gz
############################ 离线安装依赖 ###############################
# 安装依赖
cd ./3rdParty/rbuild_depend
pip install click-6.6-py2.py3-none-any.whl
pip install six-1.15.0-py2.py3-none-any.whl
pip install subprocess32-3.5.4.tar.gz
pip install cget-0.1.9.tar.gz
# 安装rbuild
cd ../
pip install rbuild-master.tar.gz
# 设置cmake的最低版本
cmake_minimum_required(VERSION 3.5)
# 设置项目名
project(GPT2)
# 设置编译器
set(CMAKE_CXX_COMPILER g++)
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=c++17) # 2.2版本以上需要c++17
set(CMAKE_BUILD_TYPE release)
# 添加头文件路径
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Src/
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/
$ENV{DTKROOT}/include/
${CMAKE_CURRENT_SOURCE_DIR}/depend/include/)
include_directories(${INCLUDE_PATH})
# 添加依赖库路径
set(LIBRARY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/depend/lib64/
$ENV{DTKROOT}/lib/)
link_directories(${LIBRARY_PATH})
# 添加依赖库
set(LIBRARY onnxruntime
)
link_libraries(${LIBRARY})
# 添加源文件
set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/GPT2.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/tokenization.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/utf8proc.c
${CMAKE_CURRENT_SOURCE_DIR}/Src/Utility/Filesystem.cpp)
# 添加可执行目标
add_executable(GPT2 ${SOURCE_FILES})
# GPT
本示例主要通过使用ONNXRuntime C++ API对GPT2模型进行推理,包括预处理、模型推理以及数据后处理。
## 模型简介
GPT(Generative Pre-trained Transformer)系列模型以不断堆叠transformer中的decoder模块为特征提取器,提升训练语料的规模和质量、模型的参数量进行迭代更新。GPT-1主要通过在无标签的数据上学习一个通用的语言模型,再根据特定的任务进行微调处理有监督任务;GPT-2在GPT-1的模型结构上使用更多的模型参数和数据集,训练一个泛化能力更强的词向量模型。GPT-3更是采用海量的模型参数和数据集,训练了一个更加强大的语言模型。
| 模型 | 发布时间 | 参数量 | 预训练数据量 |
| ----- | ------------ | -------- | ------------ |
| GPT-1 | 2018 年 6 月 | 1.17 亿 | 约 5GB |
| GPT-2 | 2019 年 2 月 | 15 亿 | 40GB |
| GPT-3 | 2020 年 5 月 | 1,750 亿 | 45TB |
本次采用GPT-2模型进行诗词生成任务,模型文件下载链接:https://pan.baidu.com/s/1KWeoUuakCZ5dualK69qCcw, 提取码:4pmh。将GPT2_shici.onnx模型文件保存在Resource/文件夹下。整体模型结构如下图所示,也可以通过netron工具:https://netron.app/ 查看GPT-2的模型结构。
<img src="./Images/GPT_01.png" style="zoom:100%;" align=middle>
## 预处理
在将文本输入到模型之前,需要做如下预处理:
1.加载词汇表
2.文本编码
首先,根据提供的词汇表路径,通过cuBERT::FullTokenizer()函数加载词汇表,用于后续对输入文本的编码操作。其次,将词汇表中的内容依次保存到vector容器output中,用于数据后处理中的解码操作。
```c++
cuBERT::FullTokenizer tokenizer = cuBERT::FullTokenizer("../Resource/vocab_shici.txt");
std::ifstream infile;
std::string buf;
std::vector<std::string> output;
infile.open("../Resource/vocab_shici.txt");
while (std::getline(infile,buf))
{
output.push_back(buf);
}
```
文本编码的实现方法主要封装在GPT2::Preprocessin()函数中,通过输入问题question,进行数据重构,在输入序列中的起始位置处加入起始标志符[CLS],之后拼接问题question的编码信息,从而完成数据预处理过程。
```c++
ErrorCode GPT2::Preprocessing(cuBERT::FullTokenizer tokenizer,
char *question,
std::vector<long unsigned int> &input_id)
{
// 对问题进行分词操作
int max_seq_length =1000;
std::vector<std::string> tokens_question;
tokens_question.reserve(max_seq_length);
tokenizer.tokenize(question, &tokens_question, max_seq_length);
// 将文本数据转换为数值型数据
input_id.push_back(tokenizer.convert_token_to_id("[CLS]"));
for (int i=0;i<tokens_question.size();++i)
{
input_id.push_back(tokenizer.convert_token_to_id(tokens_question[i]));
}
return SUCCESS;
}
```
## 推理
对于GPT-2这种生成式语言模型来说,模型不是仅执行一次推理就结束,而是需要执行多次推理,才能得到最终的答案。如下图所示,GPT-2模型每次推理仅生成一个词,通过将生成的词与输入数据拼接,输入到模型中继续下一次的推理,直到循环结果或者生成[SEP]结束标识符才结束推理。
<img src="./Images/GPT_02.png" style="zoom:70%;" align=middle>
具体GPT-2模型的推理,如下代码所示。首先,通过gpt2.Inference()函数实现模型的具体推理细节,推理结果保存在outputs中。其次,对每次推理结果进行判断,当判断为[SEP]结束标志符时,结束循环完成推理,否则就将推理结果outputs加入到输入数据input_id中,继续下一次的模型推理。
```c++
// 推理
for(int i=0;i<50;++i)
{
long unsigned int outputs = gpt2.Inference(input_id);
if(outputs == 102) // 当outputs等于102时,即[SEP]结束标志符,退出循环。
{
break;
}
input_id.push_back(outputs);
}
```
在GPT2::Inference()函数具体实现了GPT-2模型的推理过程,主要做如下处理:
1.设置输入shape并执行推理
```c++
long unsigned int GPT2::Inference(const std::vector<long unsigned int> &input_id)
{
// 保存预处理后的数据
long unsigned int input[1][input_id.size()];
for (int j=0;j<input_id.size();++j)
{
input[0][j] = input_id[j];
}
// 设置输入shape
std::array<int64_t, 2> inputShapes{1, (int)input_id.size()};
// 创建输入数据
auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
auto inputTensorSize = 1 * input_id.size();
std::vector<float> inputTensorValues(inputTensorSize);
int64_t* input_test = (int64_t*)input;
Ort::Value inputTensor = Ort::Value::CreateTensor<int64_t>(memoryInfo, input_test, inputTensorValues.size(), inputShapes.data(), inputShapes.size());
std::vector<Ort::Value> intput_tensors;
intput_tensors.push_back(std::move(inputTensor));
// 推理
auto output_tensors = session->Run(Ort::RunOptions{nullptr}, input_node_names.data(), intput_tensors.data(), input_node_names.size(), output_node_names.data(), output_node_names.size());
// 保存推理结果
const float* data = output_tensors[0].GetTensorMutableData<float>();
...
}
```
1.执行推理,GPT-2模型的推理结果results是一个std::vector<Ort::Value>类型,包含一个输出,所以result = results[0]。result中一共包含了input_id.size() * 22557个概率值,其中,input_id.size()代表输入数据的长度,22557代表了词汇表中词的数量。
## 数据后处理
得到模型推理结果后,还需要对数据做如下后处理:
1.排序
2.解码
```c++
long unsigned int GPT2::Inference(const std::vector<long unsigned int> &input)
{
...
// 保存模型推理出的概率值
long unsigned int n = 0;
std::vector<Predictions> resultsOfPredictions(22557);
for(int i=(input.size()-1)*22557; i<input.size()*22557; ++i)
{
resultsOfPredictions[n].index = n;
resultsOfPredictions[n].predictionvalue = data[i];
++n;
}
// 对概率值进行排序操作
std::sort(resultsOfPredictions.begin(), resultsOfPredictions.end(), CompareM);
return resultsOfPredictions[0].index;
}
```
1.排序。首先,保存数据,因为每次推理都会对每个token生成词汇表中下一个词的概率,例如输入shape为(1,4),词汇表长度为22557,则一共生成4*22557个概率,但是不需要获得全部的概率,只需要保存最后一个token生成的概率。通过设计了一个结构体Predictions,其中包含了两个成员变量,索引index和概率值predictionvalue,将推理结果保存在结构体中,其次,采用std::sort()函数进行排序操作,取概率值最大的作为预测结果,最后,返回概率值最大的索引值。
```C++
for(int i=0;i<score.size();++i)
{
result.push_back(output[score[i]]);
}
```
2.解码。score是一个vector容器,存储了推理结果的索引值,另外,在数据预处理的过程中,已经将词汇表中的词依次存储在了vector容器output中。因此,通过给output提供下标score[i],便可以取出对应的词,即将数值型数据解码为文本数据,得到最终的答案。
\ No newline at end of file
# GPT
本示例主要通过使用ONNXRuntime Python API对GPT2模型进行推理,包括预处理、模型推理以及数据后处理。
## 模型简介
GPT(Generative Pre-trained Transformer)系列模型以不断堆叠的transformer中的decoder模块为特征提取器,提升训练语料的规模和质量、网络的参数量进行迭代更新。GPT-1主要是通过在无标签的数据上学习一个通用的语言模型,再根据特定的任务进行微调处理有监督任务;GPT-2在GPT-1的模型结构上使用更多的模型参数和数据集,训练一个泛化能力更强的词向量模型。GPT-3更是采用海量的模型参数和数据集,训练了一个更加强大的语言模型。
| 模型 | 发布时间 | 参数量 | 预训练数据量 |
| ----- | ------------ | -------- | ------------ |
| GPT-1 | 2018 年 6 月 | 1.17 亿 | 约 5GB |
| GPT-2 | 2019 年 2 月 | 15 亿 | 40GB |
| GPT-3 | 2020 年 5 月 | 1,750 亿 | 45TB |
本次采用GPT-2模型进行诗词生成任务,模型文件下载链接:https://pan.baidu.com/s/1KWeoUuakCZ5dualK69qCcw , 提取码:4pmh 。将GPT2_shici.onnx模型文件保存在Resource/文件夹下。整体模型结构如下图所示,也可以通过netron工具:https://netron.app/ 查看GPT-2的模型结构。
<img src="./Images/GPT_01.png" style="zoom:100%;" align=middle>
## 预处理
在将文本输入到模型之前,需要做如下预处理:
1.加载词汇表,根据提供的路径加载词汇表
2.文本编码,根据词汇表对输入的文本进行编码
在数据预处理的过程中,首先加载词汇表,根据提供的词汇表路径,通过transformers库中的BertTokenizerFast函数实现词汇表的加载。完成词汇表的加载后,就可以正常对输入的文本进行编码处理,从而将文本数据转换为数值型数据。
```python
# 加载词汇表
vocab_file = os.path.join('../Resource/', 'vocab_shici.txt')
tokenizer = BertTokenizerFast(vocab_file, sep_token="[SEP]", pad_token="[PAD]", cls_token="[CLS]")
```
完成词汇表的加载后,就可以进行文本编码。首先,输入一段文本数据,通过tokenizer.encoder()将文本数据编码为数值型数据。其次,进行数据重构,创建了一个input_ids列表,开头加入[CLS]起始标志符,并将数值型数据拼接到后面。最后,将input_ids列表中的数据都转换为np.int64类型,并将一维数据扩展了二维数据,完成数据的预处理过程。
```python
# 对输入文本进行编码
text = input("user:")
text_ids = tokenizer.encode(text, add_special_tokens=False)
# 数据重构
input_ids = [tokenizer.cls_token_id]
input_ids.extend(text_ids)
input_ids = np.array(input_ids, dtype=np.int64)
input_ids = np.expand_dims(input_ids, axis=0)
```
## 推理
完成数据预处理后,就可以执行模型推理。推理过程主要做如下处理:
1.设置最大输入shape
2.循环推理
```Python
# 模型推理
for _ in range(max_len):
# 推理
result = dcu_session.run(None, input_feed={input_name: input_ids})
npresule =np.array(result[0])
logits = [float(x) for x in npresule.flatten()]
...
# 当推理得到[SEP]结束标志符,则结束for循环停止生成
if next_token == tokenizer.convert_tokens_to_ids('[SEP]'):
break
# 将推理结果next_token和input_ids进行拼接
next_token = np.array(next_token, dtype=np.int64)
input_ids = np.append(input_ids, next_token)
input_ids = np.expand_dims(input_ids, axis=0)
```
1.循环推理,GPT-2模型不像其他模型一样只需要执行一次推理,而是需要循环执行多次推理才能完成。首先,模型推理限定在for循环中,将输入数据input_ids,输入到dcu_session.run({...})中执行推理,生成一个token的id。其次,将推理结果拼接到输入数据input_ids中,执行下一次循环。最后,当循环结束或者生成的词为[SEP]结束标志符时,完成GPT-2模型的整体推理。如下图所示,为GPT-2模型的一次完整推理过程。
<img src="./Images/GPT_02.png" style="zoom:70%;" align=middle>
## 数据后处理
获得模型推理结果后,并不能直接作为答案输出,还需要进行数据后处理操作,才能得到最终的结果。具体主要做如下后处理:
1.排序
2.解码
```Python
for _ in range(max_len):
...
# 排序
score = []
for index in range((input_ids.shape[1]-1)*22557, input_ids.shape[1]*22557):
score.append(logits[index])
index_and_score = sorted(enumerate(score), key=lambda x: x[1], reverse=True)
# 取出概率值最大的作为预测结果
next_token = index_and_score[0][0]
...
history.append(next_token)
# 解码
text = tokenizer.convert_ids_to_tokens(history)
print("chatbot:" + "".join(text))
```
1.排序,每次推理都会对每个token生成词汇表中下一个词的概率,例如输入shape为(1,4),词汇表长度为22557,则一共生成4*22557个概率,但是不需要获得全部的概率,只需要得到最后一个token生成的概率。得到相应的概率后,采用sorted()函数进行排序操作,取概率值最大的作为预测结果。
2.解码,因为history列表中存放的推理结果都是对应token的id,还需要进行解码将对应的id转换文本。解码主要采用BertTokenizerFast中的convert_ids_to_tokens()函数将数值型数据转换为相应的文本数据,从而得到对应的答案。
import os
import numpy as np
from transformers import BertTokenizerFast
import onnxruntime as ort
# 加载词汇表
print("INFO: Complete loading the vocabulary")
vocab_file = os.path.join('../Resource/', 'vocab_shici.txt')
tokenizer = BertTokenizerFast(vocab_file, sep_token="[SEP]", pad_token="[PAD]", cls_token="[CLS]")
# 加载模型
print("INFO: Parsing and compiling the model")
sess_options = ort.SessionOptions()
#设置图优化
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_BASIC
#是否开启profiling
sess_options.enable_profiling = False
#加载模型
dcu_session = ort.InferenceSession("../Resource/GPT2_shici.onnx",sess_options,providers=['ROCMExecutionProvider'],)
input_name=dcu_session.get_inputs()[0].name
print('开始和GPT2对诗,输入CTRL + Z以退出')
while True:
try:
history = []
text = input("user:")
text_ids = tokenizer.encode(text, add_special_tokens=False)
history.extend(text_ids)
input_ids = [tokenizer.cls_token_id]
input_ids.extend(text_ids)
input_ids = np.array(input_ids, dtype=np.int64)
input_ids = np.expand_dims(input_ids, axis=0)
max_len = 50
for _ in range(max_len):
# 推理
result = dcu_session.run(None, input_feed={input_name: input_ids})
npresule =np.array(result[0])
logits = [float(x) for x in npresule.flatten()]
# 对于[UNK]的概率设为无穷小,模型的预测结果不可能是[UNK]
logits[tokenizer.convert_tokens_to_ids('[UNK]')] = -float('Inf')
# 排序
score = []
for index in range((input_ids.shape[1]-1)*22557, input_ids.shape[1]*22557):
score.append(logits[index])
index_and_score = sorted(enumerate(score), key=lambda x: x[1], reverse=True)
# 取概率值最大的作为预测结果
next_token = index_and_score[0][0]
if next_token == tokenizer.convert_tokens_to_ids('[SEP]'): # 遇到[SEP]结束标志符,结束循环
break
history.append(next_token) # 结果存放在response列表中
next_token = np.array(next_token, dtype=np.int64)
input_ids = np.append(input_ids, next_token)
input_ids = np.expand_dims(input_ids, axis=0)
text = tokenizer.convert_ids_to_tokens(history)
print("chatbot:" + "".join(text))
except KeyboardInterrupt:
break
os
numpy
transformers
# Generative Pre-Training2(GPT2)
## 模型介绍
GPT2模型:第二代生成式预训练模型(Generative Pre-Training2),使用ONNXRuntime推理框架进行推理。
## 模型结构
GPT2主要使用Transformer的Decoder模块为特征提取器,并对Transformer Decoder进行了一些改动,原本的Decoder包含了两个Multi-Head Attention结构,而GPT2只保留了Mask Multi-Head Attention。
## Python版本推理
本次采用GPT-2模型进行诗词生成任务,模型文件下载链接:https://pan.baidu.com/s/1KWeoUuakCZ5dualK69qCcw , 提取码:4pmh ,并将GPT2_shici.onnx模型文件保存在Resource/文件夹下。下面介绍如何运行python代码示例,Python示例的详细说明见Doc目录下的Tutorial_Python.md。
### 下载镜像
在光源中下载镜像:
```python
docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:ort1.14.0_migraphx3.0.0-dtk22.10.1
```
### 设置Python环境变量
```
export PYTHONPATH=/opt/dtk/lib:$PYTHONPATH
```
### 安装依赖
```python
# 进入gpt2 ort工程根目录
cd <path_to_gpt2_ort>
# 进入示例程序目录
cd Python/
# 安装依赖
pip install -r requirements.txt
```
### 运行示例
```python
python gpt2.py
```
如下所示,采用交互式界面,通过输入开头诗词,GPT2模型可以生成后续的诗句。
```
user:江上归帆天际开
chatbot:江上归帆天际开,江头别棹日边回。风尘满地音书绝,鸿雁不来春又来。
user:我亦孤山冷淡郎
chatbot:我亦孤山冷淡郎,爱梅不作一般香。水边篱落坡仙笑,羔酒空浇入甲黄。
user:七言绝句
chatbot:七言绝句古无有,五字长城今在前。我欲从君乞妙语,笔端三昧要亲传。
user:春风吹絮满江南
chatbot:春风吹絮满江南,一片离情酒半酣。记得小桥和雪看,梅花无数簇晴岚。
```
## C++版本推理
本次采用GPT-2模型进行诗词生成任务,模型文件下载链接:https://pan.baidu.com/s/1KWeoUuakCZ5dualK69qCcw , 提取码:4pmh ,并将GPT2_shici.onnx模型文件保存在Resource/文件夹下。下面介绍如何运行C++代码示例,C++示例的详细说明见Doc目录下的Tutorial_Cpp.md。
### 下载镜像
```
docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:ort1.14.0_migraphx3.0.0-dtk22.10.1
```
### 构建工程
```
rbuild build -d depend
```
### 设置环境变量
将依赖库依赖加入环境变量LD_LIBRARY_PATH,在~/.bashrc中添加如下语句:
```
export LD_LIBRARY_PATH=<path_to_gpt2_ort>/depend/lib64/:$LD_LIBRARY_PATH
```
然后执行:
```
source ~/.bashrc
source /opt/dtk/env.sh
```
### 运行示例
```cpp
# 进入gpt2 ort工程根目录
cd <path_to_gpt2_ort>
# 进入build目录
cd build/
# 执行示例程序
./GPT2
```
如下所示,采用交互式界面,通过输入开头诗词,GPT2模型可以推理出后续的诗句。
```
question:江上归帆天际开
chatbot:江上归帆天际开,江头别棹日边回。风尘满地音书绝,鸿雁不来春又来。
question:我亦孤山冷淡郎
chatbot:我亦孤山冷淡郎,爱梅不作一般香。水边篱落坡仙笑,羔酒空浇入甲黄。
question:七言绝句
chatbot:七言绝句古无有,五字长城今在前。我欲从君乞妙语,笔端三昧要亲传。
question:春风吹絮满江南
chatbot:春风吹絮满江南,一片离情酒半酣。记得小桥和雪看,梅花无数簇晴岚。
```
## 源码仓库及问题反馈
https://developer.hpccube.com/codes/modelzoo/gpt2_ort
## 参考
https://github.com/Morizeyao/GPT2-Chinese
\ No newline at end of file
This diff is collapsed.
#include <GPT2.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <Filesystem.h>
#include <SimpleLog.h>
#include <algorithm>
#include <stdexcept>
#include <tokenization.h>
namespace ortSamples
{
GPT2::GPT2()
{
}
GPT2::~GPT2()
{
}
ErrorCode GPT2::Initialize()
{
// 获取模型文件
std::string modelPath="../Resource/GPT2_shici.onnx";
// 判断路径
if(Exists(modelPath)==false)
{
LOG_ERROR(stdout,"%s not exist!\n",modelPath.c_str());
return MODEL_NOT_EXIST;
}
//加载模型
OrtROCMProviderOptions rocm_options;
rocm_options.device_id = 0;
sessionOptions.AppendExecutionProvider_ROCM(rocm_options);
sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_BASIC);
LOG_INFO(stdout,"succeed to load model: %s\n",GetFileName(modelPath).c_str());
session = new Ort::Session(env, modelPath.c_str(), sessionOptions);
return SUCCESS;
}
static bool CompareM(Predictions a, Predictions b)
{
return a.predictionvalue > b.predictionvalue;
}
long unsigned int GPT2::Inference(const std::vector<long unsigned int> &input_id)
{
int64_t input[1][input_id.size()];
for (int j=0;j<input_id.size();++j)
{
input[0][j] = static_cast<int64_t>(input_id[j]);;
}
// 获取模型输入属性
input_node_names = {"input"};
// 获取模型输出属性
output_node_names = {"output","304","value.3","532","value.11","758","value.19","984","value.27","1210","value.35","1436","value.43","1662","value.51","1888","value.59","2114","value.67","2340","value.75","2566","value.83","2792","value.91"};
// 设置输入shape
std::array<int64_t, 2> inputShapes{1, (int)input_id.size()};
// 创建输入数据
auto memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
auto inputTensorSize = 1 * input_id.size();
std::vector<float> inputTensorValues(inputTensorSize);
int64_t* input_test = (int64_t*)input;
Ort::Value inputTensor = Ort::Value::CreateTensor<int64_t>(memoryInfo, input_test, inputTensorValues.size(), inputShapes.data(), inputShapes.size());
std::vector<Ort::Value> intput_tensors;
intput_tensors.push_back(std::move(inputTensor));
// 推理
auto output_tensors = session->Run(Ort::RunOptions{nullptr}, input_node_names.data(), intput_tensors.data(), input_node_names.size(), output_node_names.data(), output_node_names.size());
// 保存推理结果
const float* data = output_tensors[0].GetTensorMutableData<float>();
long unsigned int n = 0;
std::vector<Predictions> resultsOfPredictions(22557);
for(int i=(input_id.size()-1)*22557; i<input_id.size()*22557; ++i)
{
resultsOfPredictions[n].index = n;
resultsOfPredictions[n].predictionvalue = data[i];
++n;
}
// 对于[UNK]的概率设为无穷小,模型的预测结果不可能是[UNK]
resultsOfPredictions[100].predictionvalue = -10000;
// 排序
std::sort(resultsOfPredictions.begin(), resultsOfPredictions.end(), CompareM);
return resultsOfPredictions[0].index;
}
ErrorCode GPT2::Preprocessing(cuBERT::FullTokenizer tokenizer,
char *question,
std::vector<long unsigned int> &input_id)
{
// 分词操作
int max_seq_length =1000;
std::vector<std::string> tokens_question;
tokens_question.reserve(max_seq_length);
tokenizer.tokenize(question, &tokens_question, max_seq_length);
// 保存编码信息
input_id.push_back(tokenizer.convert_token_to_id("[CLS]"));
for (int i=0;i<tokens_question.size();++i)
{
input_id.push_back(tokenizer.convert_token_to_id(tokens_question[i]));
}
return SUCCESS;
}
}
\ No newline at end of file
#ifndef __GPT2_H__
#define __GPT2_H__
#include <cstdint>
#include <string>
#include <tokenization.h>
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
namespace ortSamples
{
typedef enum _ErrorCode
{
SUCCESS=0,
MODEL_NOT_EXIST,
CONFIG_FILE_NOT_EXIST,
FAIL_TO_LOAD_MODEL,
FAIL_TO_OPEN_CONFIG_FILE,
}ErrorCode;
typedef struct _Predictions
{
long unsigned int index;
float predictionvalue;
}Predictions;
class GPT2
{
public:
GPT2();
~GPT2();
ErrorCode Initialize();
ErrorCode Preprocessing(cuBERT::FullTokenizer tokenizer,
char *question,
std::vector<long unsigned int> &input_id);
long unsigned int Inference(const std::vector<long unsigned int> &input_id);
private:
std::vector<const char*> input_node_names;
std::vector<const char*> output_node_names;
Ort::Session *session;
Ort::Env env = Ort::Env(ORT_LOGGING_LEVEL_ERROR, "ONNXRuntime");
Ort::SessionOptions sessionOptions = Ort::SessionOptions();
};
}
#endif
\ No newline at end of file
#include <Filesystem.h>
#include <algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#include <Windows.h>
#else
#include <unistd.h>
#include <dirent.h>
#endif
// 路径分隔符(Linux:‘/’,Windows:’\\’)
#ifdef _WIN32
#define PATH_SEPARATOR '\\'
#else
#define PATH_SEPARATOR '/'
#endif
using namespace std;
namespace ortSamples
{
static std::vector<std::string> SplitString(std::string str, std::string separator)
{
std::string::size_type pos;
std::vector<std::string> result;
str+=separator;//扩展字符串以方便操作
int size=str.size();
for(int i=0; i<size; i++)
{
pos=str.find(separator,i);
if(pos<size)
{
std::string s=str.substr(i,pos-i);
result.push_back(s);
i=pos+separator.size()-1;
}
}
return result;
}
#if defined _WIN32 || defined WINCE
const char dir_separators[] = "/\\";
struct dirent
{
const char* d_name;
};
struct DIR
{
#ifdef WINRT
WIN32_FIND_DATAW data;
#else
WIN32_FIND_DATAA data;
#endif
HANDLE handle;
dirent ent;
#ifdef WINRT
DIR() { }
~DIR()
{
if (ent.d_name)
delete[] ent.d_name;
}
#endif
};
DIR* opendir(const char* path)
{
DIR* dir = new DIR;
dir->ent.d_name = 0;
#ifdef WINRT
string full_path = string(path) + "\\*";
wchar_t wfull_path[MAX_PATH];
size_t copied = mbstowcs(wfull_path, full_path.c_str(), MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
dir->handle = ::FindFirstFileExW(wfull_path, FindExInfoStandard,
&dir->data, FindExSearchNameMatch, NULL, 0);
#else
dir->handle = ::FindFirstFileExA((string(path) + "\\*").c_str(),
FindExInfoStandard, &dir->data, FindExSearchNameMatch, NULL, 0);
#endif
if (dir->handle == INVALID_HANDLE_VALUE)
{
/*closedir will do all cleanup*/
delete dir;
return 0;
}
return dir;
}
dirent* readdir(DIR* dir)
{
#ifdef WINRT
if (dir->ent.d_name != 0)
{
if (::FindNextFileW(dir->handle, &dir->data) != TRUE)
return 0;
}
size_t asize = wcstombs(NULL, dir->data.cFileName, 0);
CV_Assert((asize != 0) && (asize != (size_t)-1));
char* aname = new char[asize + 1];
aname[asize] = 0;
wcstombs(aname, dir->data.cFileName, asize);
dir->ent.d_name = aname;
#else
if (dir->ent.d_name != 0)
{
if (::FindNextFileA(dir->handle, &dir->data) != TRUE)
return 0;
}
dir->ent.d_name = dir->data.cFileName;
#endif
return &dir->ent;
}
void closedir(DIR* dir)
{
::FindClose(dir->handle);
delete dir;
}
#else
# include <dirent.h>
# include <sys/stat.h>
const char dir_separators[] = "/";
#endif
static bool isDir(const string &path, DIR* dir)
{
#if defined _WIN32 || defined WINCE
DWORD attributes;
BOOL status = TRUE;
if (dir)
attributes = dir->data.dwFileAttributes;
else
{
WIN32_FILE_ATTRIBUTE_DATA all_attrs;
#ifdef WINRT
wchar_t wpath[MAX_PATH];
size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
status = ::GetFileAttributesExW(wpath, GetFileExInfoStandard, &all_attrs);
#else
status = ::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &all_attrs);
#endif
attributes = all_attrs.dwFileAttributes;
}
return status && ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
#else
(void)dir;
struct stat stat_buf;
if (0 != stat(path.c_str(), &stat_buf))
return false;
int is_dir = S_ISDIR(stat_buf.st_mode);
return is_dir != 0;
#endif
}
bool IsDirectory(const string &path)
{
return isDir(path, NULL);
}
bool Exists(const string& path)
{
#if defined _WIN32 || defined WINCE
BOOL status = TRUE;
{
WIN32_FILE_ATTRIBUTE_DATA all_attrs;
#ifdef WINRT
wchar_t wpath[MAX_PATH];
size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
status = ::GetFileAttributesExW(wpath, GetFileExInfoStandard, &all_attrs);
#else
status = ::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &all_attrs);
#endif
}
return !!status;
#else
struct stat stat_buf;
return (0 == stat(path.c_str(), &stat_buf));
#endif
}
bool IsPathSeparator(char c)
{
return c == '/' || c == '\\';
}
string JoinPath(const string& base, const string& path)
{
if (base.empty())
return path;
if (path.empty())
return base;
bool baseSep = IsPathSeparator(base[base.size() - 1]);
bool pathSep = IsPathSeparator(path[0]);
string result;
if (baseSep && pathSep)
{
result = base + path.substr(1);
}
else if (!baseSep && !pathSep)
{
result = base + PATH_SEPARATOR + path;
}
else
{
result = base + path;
}
return result;
}
static bool wildcmp(const char *string, const char *wild)
{
const char *cp = 0, *mp = 0;
while ((*string) && (*wild != '*'))
{
if ((*wild != *string) && (*wild != '?'))
{
return false;
}
wild++;
string++;
}
while (*string)
{
if (*wild == '*')
{
if (!*++wild)
{
return true;
}
mp = wild;
cp = string + 1;
}
else if ((*wild == *string) || (*wild == '?'))
{
wild++;
string++;
}
else
{
wild = mp;
string = cp++;
}
}
while (*wild == '*')
{
wild++;
}
return *wild == 0;
}
static void glob_rec(const string &directory, const string& wildchart, std::vector<string>& result,
bool recursive, bool includeDirectories, const string& pathPrefix)
{
DIR *dir;
if ((dir = opendir(directory.c_str())) != 0)
{
/* find all the files and directories within directory */
try
{
struct dirent *ent;
while ((ent = readdir(dir)) != 0)
{
const char* name = ent->d_name;
if ((name[0] == 0) || (name[0] == '.' && name[1] == 0) || (name[0] == '.' && name[1] == '.' && name[2] == 0))
continue;
string path = JoinPath(directory, name);
string entry = JoinPath(pathPrefix, name);
if (isDir(path, dir))
{
if (recursive)
glob_rec(path, wildchart, result, recursive, includeDirectories, entry);
if (!includeDirectories)
continue;
}
if (wildchart.empty() || wildcmp(name, wildchart.c_str()))
result.push_back(entry);
}
}
catch (...)
{
closedir(dir);
throw;
}
closedir(dir);
}
else
{
printf("could not open directory: %s", directory.c_str());
}
}
void GetFileNameList(const string &directory, const string &pattern, std::vector<string>& result, bool recursive, bool addPath)
{
// split pattern
vector<string> patterns=SplitString(pattern,",");
result.clear();
for(int i=0;i<patterns.size();++i)
{
string eachPattern=patterns[i];
std::vector<string> eachResult;
glob_rec(directory, eachPattern, eachResult, recursive, true, directory);
for(int j=0;j<eachResult.size();++j)
{
if (IsDirectory(eachResult[j]))
continue;
if(addPath)
{
result.push_back(eachResult[j]);
}
else
{
result.push_back(GetFileName(eachResult[j]));
}
}
}
std::sort(result.begin(), result.end());
}
void GetFileNameList2(const string &directory, const string &pattern, std::vector<string>& result, bool recursive, bool addPath)
{
// split pattern
vector<string> patterns = SplitString(pattern, ",");
result.clear();
for (int i = 0; i<patterns.size(); ++i)
{
string eachPattern = patterns[i];
std::vector<string> eachResult;
glob_rec(directory, eachPattern, eachResult, recursive, true, directory);
for (int j = 0; j<eachResult.size(); ++j)
{
string filePath = eachResult[j];
if (IsDirectory(filePath))
{
filePath = filePath + "/";
for (int k = 0; k < filePath.size(); ++k)
{
if (IsPathSeparator(filePath[k]))
{
filePath[k] = '/';
}
}
}
if (addPath)
{
result.push_back(filePath);
}
else
{
if (!IsDirectory(filePath))
{
result.push_back(GetFileName(filePath));
}
}
}
}
std::sort(result.begin(), result.end());
}
void RemoveAll(const string& path)
{
if (!Exists(path))
return;
if (IsDirectory(path))
{
std::vector<string> entries;
GetFileNameList2(path, string(), entries, false, true);
for (size_t i = 0; i < entries.size(); i++)
{
const string& e = entries[i];
RemoveAll(e);
}
#ifdef _MSC_VER
bool result = _rmdir(path.c_str()) == 0;
#else
bool result = rmdir(path.c_str()) == 0;
#endif
if (!result)
{
printf("can't remove directory: %s\n", path.c_str());
}
}
else
{
#ifdef _MSC_VER
bool result = _unlink(path.c_str()) == 0;
#else
bool result = unlink(path.c_str()) == 0;
#endif
if (!result)
{
printf("can't remove file: %s\n", path.c_str());
}
}
}
void Remove(const string &directory, const string &extension)
{
DIR *dir;
static int numberOfFiles = 0;
if ((dir = opendir(directory.c_str())) != 0)
{
/* find all the files and directories within directory */
try
{
struct dirent *ent;
while ((ent = readdir(dir)) != 0)
{
const char* name = ent->d_name;
if ((name[0] == 0) || (name[0] == '.' && name[1] == 0) || (name[0] == '.' && name[1] == '.' && name[2] == 0))
continue;
string path = JoinPath(directory, name);
if (isDir(path, dir))
{
Remove(path, extension);
}
// �ж���չ��
if (extension.empty() || wildcmp(name, extension.c_str()))
{
RemoveAll(path);
++numberOfFiles;
printf("%s deleted! number of deleted files:%d\n", path.c_str(), numberOfFiles);
}
}
}
catch (...)
{
closedir(dir);
throw;
}
closedir(dir);
}
else
{
printf("could not open directory: %s", directory.c_str());
}
// ����RemoveAllɾ��Ŀ¼
RemoveAll(directory);
}
string GetFileName(const string &path)
{
string fileName;
int indexOfPathSeparator = -1;
for (int i = path.size() - 1; i >= 0; --i)
{
if (IsPathSeparator(path[i]))
{
fileName = path.substr(i + 1, path.size() - i - 1);
indexOfPathSeparator = i;
break;
}
}
if (indexOfPathSeparator == -1)
{
fileName = path;
}
return fileName;
}
string GetFileName_NoExtension(const string &path)
{
string fileName=GetFileName(path);
string fileName_NoExtension;
for(int i=fileName.size()-1;i>0;--i)
{
if(fileName[i]=='.')
{
fileName_NoExtension=fileName.substr(0,i);
break;
}
}
return fileName_NoExtension;
}
string GetExtension(const string &path)
{
string fileName;
for (int i = path.size() - 1; i >= 0; --i)
{
if (path[i]=='.')
{
fileName = path.substr(i, path.size() - i);
break;
}
}
return fileName;
}
string GetParentPath(const string &path)
{
string fileName;
for (int i = path.size() - 1; i >= 0; --i)
{
if (IsPathSeparator(path[i]))
{
fileName = path.substr(0, i+1);
break;
}
}
return fileName;
}
static bool CreateDirectory(const string &path)
{
#if defined WIN32 || defined _WIN32 || defined WINCE
#ifdef WINRT
wchar_t wpath[MAX_PATH];
size_t copied = mbstowcs(wpath, path.c_str(), MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
int result = CreateDirectoryA(wpath, NULL) ? 0 : -1;
#else
int result = _mkdir(path.c_str());
#endif
#elif defined __linux__ || defined __APPLE__
int result = mkdir(path.c_str(), 0777);
#else
int result = -1;
#endif
if (result == -1)
{
return IsDirectory(path);
}
return true;
}
bool CreateDirectories(const string &directoryPath)
{
string path = directoryPath;
for (;;)
{
char last_char = path.empty() ? 0 : path[path.length() - 1];
if (IsPathSeparator(last_char))
{
path = path.substr(0, path.length() - 1);
continue;
}
break;
}
if (path.empty() || path == "./" || path == ".\\" || path == ".")
return true;
if (IsDirectory(path))
return true;
size_t pos = path.rfind('/');
if (pos == string::npos)
pos = path.rfind('\\');
if (pos != string::npos)
{
string parent_directory = path.substr(0, pos);
if (!parent_directory.empty())
{
if (!CreateDirectories(parent_directory))
return false;
}
}
return CreateDirectory(path);
}
bool CopyFile(const string srcPath, const string dstPath)
{
std::ifstream srcFile(srcPath,ios::binary);
std::ofstream dstFile(dstPath,ios::binary);
if(!srcFile.is_open())
{
printf("can not open %s\n",srcPath.c_str());
return false;
}
if(!dstFile.is_open())
{
printf("can not open %s\n", dstPath.c_str());
return false;
}
if(srcPath==dstPath)
{
printf("src can not be same with dst\n");
return false;
}
char buffer[2048];
unsigned int numberOfBytes=0;
while(srcFile)
{
srcFile.read(buffer,2048);
dstFile.write(buffer,srcFile.gcount());
numberOfBytes+=srcFile.gcount();
}
srcFile.close();
dstFile.close();
return true;
}
bool CopyDirectories(string srcPath, const string dstPath)
{
if(srcPath==dstPath)
{
printf("src can not be same with dst\n");
return false;
}
// ȥ������·���ָ���
srcPath = srcPath.substr(0, srcPath.size() - 1);
vector<string> fileNameList;
GetFileNameList2(srcPath, "", fileNameList, true, true);
string parentPathOfSrc=GetParentPath(srcPath);
int length=parentPathOfSrc.size();
// create all directories
for(int i=0;i<fileNameList.size();++i)
{
// create directory
string srcFilePath=fileNameList[i];
string subStr=srcFilePath.substr(length,srcFilePath.size()-length);
string dstFilePath=dstPath+subStr;
string parentPathOfDst=GetParentPath(dstFilePath);
CreateDirectories(parentPathOfDst);
}
// copy file
for(int i=0;i<fileNameList.size();++i)
{
string srcFilePath=fileNameList[i];
if (IsDirectory(srcFilePath))
{
continue;
}
string subStr=srcFilePath.substr(length,srcFilePath.size()-length);
string dstFilePath=dstPath+subStr;
// copy file
CopyFile(srcFilePath,dstFilePath);
// process
double process = (1.0*(i + 1) / fileNameList.size()) * 100;
printf("%s done! %f% \n", GetFileName(fileNameList[i]).c_str(), process);
}
printf("all done!(the number of files:%d)\n", fileNameList.size());
return true;
}
}
// 文件以及目录处理
#ifndef __FILE_SYSTEM_H__
#define __FILE_SYSTEM_H__
#include <string>
#include <vector>
namespace ortSamples
{
// 路径是否存在
bool Exists(const std::string &path);
// 路径是否为目录
bool IsDirectory(const std::string &path);
// 是否是路径分隔符(Linux:‘/’,Windows:’\\’)
bool IsPathSeparator(char c);
// 路径拼接
std::string JoinPath(const std::string &base, const std::string &path);
// 创建多级目录,注意:创建多级目录的时候,目标目录是不能有文件存在的
bool CreateDirectories(const std::string &directoryPath);
/** 生成符合指定模式的文件名列表(支持递归遍历)
*
* pattern: 模式,比如"*.jpg","*.png","*.jpg,*.png"
* addPath:是否包含父路径
* 注意:
1. 多个模式使用","分割,比如"*.jpg,*.png"
2. 支持通配符'*','?' ,比如第一个字符是7的所有文件名:"7*.*", 以512结尾的所有jpg文件名:"*512.jpg"
3. 使用"*.jpg",而不是".jpg"
4. 空string表示返回所有结果
5. 不能返回子目录名
*
*/
void GetFileNameList(const std::string &directory, const std::string &pattern, std::vector<std::string> &result, bool recursive, bool addPath);
// 与GetFileNameList的区别在于如果有子目录,在addPath为true的时候会返回子目录路径(目录名最后有"/")
void GetFileNameList2(const std::string &directory, const std::string &pattern, std::vector<std::string> &result, bool recursive, bool addPath);
// 删除文件或者目录,支持递归删除
void Remove(const std::string &directory, const std::string &extension="");
/** 获取路径的文件名和扩展名
*
* 示例:path为D:/1/1.txt,则GetFileName()为1.txt,GetFileName_NoExtension()为1,GetExtension()为.txt,GetParentPath()为D:/1/
*/
std::string GetFileName(const std::string &path);
std::string GetFileName_NoExtension(const std::string &path);
std::string GetExtension(const std::string &path);
std::string GetParentPath(const std::string &path);
// 拷贝文件
bool CopyFile(const std::string srcPath,const std::string dstPath);
/** 拷贝目录
*
* 示例:CopyDirectories("D:/0/1/2/","E:/3/");实现把D:/0/1/2/目录拷贝到E:/3/目录中(即拷贝完成后的目录结构为E:/3/2/)
* 注意:
1.第一个参数的最后不能加”/”
2.不能拷贝隐藏文件
*/
bool CopyDirectories(std::string srcPath,const std::string dstPath);
}
#endif
// 简易日志
#ifndef __SIMPLE_LOG_H__
#define __SIMPLE_LOG_H__
#include <time.h>
#include <string>
#include <map>
#include <thread>
#include <mutex>
#if (defined WIN32 || defined _WIN32)
#include <Windows.h>
#else
#include <sys/time.h>
#endif
using namespace std;
/** 简易日志
*
* 不依赖于其他第三方库,只需要包含一个头文件就可以使用。提供了4种日志级别,包括INFO,DEBUG,WARN和ERROR。
*
* 示例1:
// 初始化日志,在./Log/目录下创建两个日志文件log1.log和log2.log(注意:目录./Log/需要存在,否则日志创建失败)
LogManager::GetInstance()->Initialize("./Log/","log1");
LogManager::GetInstance()->Initialize("./Log/","log2");
// 写日志
string log = "Hello World";
LOG_INFO(LogManager::GetInstance()->GetLogFile("log1"), "%s\n", log.c_str()); // 写入log1.log
LOG_INFO(LogManager::GetInstance()->GetLogFile("log2"), "%s\n", log.c_str()); // 写入log2.log
// 关闭日志
LogManager::GetInstance()->Close("log1");
LogManager::GetInstance()->Close("log2");
* 示例2:
// 将日志输出到控制台
string log = "Hello World";
LOG_INFO(stdout, "%s\n", log.c_str());
* 注意:
1. 需要C++11
2. 多线程的时候需要加锁(打开#define LOG_MUTEX),否则会导致日志显示混乱
*/
// #define LOG_MUTEX // 加锁
class LogManager
{
private:
LogManager(){}
public:
~LogManager(){}
inline void Initialize(const string &parentPath,const string &logName)
{
// 日志名为空表示输出到控制台
if(logName.size()==0)
return;
// 查找该日志文件,如果没有则创建
std::map<string, FILE*>::const_iterator iter = logMap.find(logName);
if (iter == logMap.end())
{
string pathOfLog = parentPath+ logName + ".log";
FILE *logFile = fopen(pathOfLog.c_str(), "a"); // w:覆盖原有文件,a:追加
if(logFile!=NULL)
{
logMap.insert(std::make_pair(logName, logFile));
}
}
}
inline FILE* GetLogFile(const string &logName)
{
std::map<string, FILE*>::const_iterator iter=logMap.find(logName);
if(iter==logMap.end())
{
return NULL;
}
return (*iter).second;
}
inline void Close(const string &logName)
{
std::map<string, FILE*>::const_iterator iter=logMap.find(logName);
if(iter==logMap.end())
{
return;
}
fclose((*iter).second);
logMap.erase(iter);
}
inline std::mutex &GetLogMutex()
{
return logMutex;
}
// Singleton
static LogManager* GetInstance()
{
static LogManager logManager;
return &logManager;
}
private:
std::map<string, FILE*> logMap;
std::mutex logMutex;
};
#ifdef LOG_MUTEX
#define LOCK LogManager::GetInstance()->GetLogMutex().lock()
#define UNLOCK LogManager::GetInstance()->GetLogMutex().unlock()
#else
#define LOCK
#define UNLOCK
#endif
// log time
typedef struct _LogTime
{
string year;
string month;
string day;
string hour;
string minute;
string second;
string millisecond; // ms
string microsecond; // us
string weekDay;
}LogTime;
inline LogTime GetTime()
{
LogTime currentTime;
#if (defined WIN32 || defined _WIN32)
SYSTEMTIME systemTime;
GetLocalTime(&systemTime);
char temp[8] = { 0 };
sprintf(temp, "%04d", systemTime.wYear);
currentTime.year=string(temp);
sprintf(temp, "%02d", systemTime.wMonth);
currentTime.month=string(temp);
sprintf(temp, "%02d", systemTime.wDay);
currentTime.day=string(temp);
sprintf(temp, "%02d", systemTime.wHour);
currentTime.hour=string(temp);
sprintf(temp, "%02d", systemTime.wMinute);
currentTime.minute=string(temp);
sprintf(temp, "%02d", systemTime.wSecond);
currentTime.second=string(temp);
sprintf(temp, "%03d", systemTime.wMilliseconds);
currentTime.millisecond=string(temp);
sprintf(temp, "%d", systemTime.wDayOfWeek);
currentTime.weekDay=string(temp);
#else
struct timeval tv;
struct tm *p;
gettimeofday(&tv, NULL);
p = localtime(&tv.tv_sec);
char temp[8]={0};
sprintf(temp,"%04d",1900+p->tm_year);
currentTime.year=string(temp);
sprintf(temp,"%02d",1+p->tm_mon);
currentTime.month=string(temp);
sprintf(temp,"%02d",p->tm_mday);
currentTime.day=string(temp);
sprintf(temp,"%02d",p->tm_hour);
currentTime.hour=string(temp);
sprintf(temp,"%02d",p->tm_min);
currentTime.minute=string(temp);
sprintf(temp,"%02d",p->tm_sec);
currentTime.second=string(temp);
sprintf(temp,"%03d",(int)(tv.tv_usec/1000));
currentTime.millisecond = string(temp);
sprintf(temp, "%03d", (int)(tv.tv_usec % 1000));
currentTime.microsecond = string(temp);
sprintf(temp, "%d", p->tm_wday);
currentTime.weekDay = string(temp);
#endif
return currentTime;
}
#define LOG_TIME(logFile) \
do\
{\
LogTime currentTime=GetTime(); \
fprintf(((logFile == NULL) ? stdout : logFile), "%s-%s-%s %s:%s:%s.%s\t",currentTime.year.c_str(),currentTime.month.c_str(),currentTime.day.c_str(),currentTime.hour.c_str(),currentTime.minute.c_str(),currentTime.second.c_str(),currentTime.millisecond.c_str()); \
}while (0)
#define LOG_INFO(logFile,logInfo, ...) \
do\
{\
LOCK; \
LOG_TIME(logFile); \
fprintf(((logFile == NULL) ? stdout : logFile), "INFO\t"); \
fprintf(((logFile == NULL) ? stdout : logFile), "[%s:%d (%s) ]: ", __FILE__, __LINE__, __FUNCTION__); \
fprintf(((logFile == NULL) ? stdout : logFile), logInfo, ## __VA_ARGS__); \
fflush(logFile); \
UNLOCK; \
} while (0)
#define LOG_DEBUG(logFile,logInfo, ...) \
do\
{\
LOCK; \
LOG_TIME(logFile);\
fprintf(((logFile==NULL)?stdout:logFile), "DEBUG\t"); \
fprintf(((logFile==NULL)?stdout:logFile), "[%s:%d (%s) ]: ", __FILE__, __LINE__, __FUNCTION__); \
fprintf(((logFile==NULL)?stdout:logFile),logInfo, ## __VA_ARGS__); \
fflush(logFile); \
UNLOCK; \
} while (0)
#define LOG_ERROR(logFile,logInfo, ...) \
do\
{\
LOCK; \
LOG_TIME(logFile);\
fprintf(((logFile==NULL)?stdout:logFile), "ERROR\t"); \
fprintf(((logFile==NULL)?stdout:logFile), "[%s:%d (%s) ]: ", __FILE__, __LINE__, __FUNCTION__); \
fprintf(((logFile==NULL)?stdout:logFile),logInfo, ## __VA_ARGS__); \
fflush(logFile); \
UNLOCK; \
} while (0)
#define LOG_WARN(logFile,logInfo, ...) \
do\
{\
LOCK; \
LOG_TIME(logFile);\
fprintf(((logFile==NULL)?stdout:logFile), "WARN\t"); \
fprintf(((logFile==NULL)?stdout:logFile), "[%s:%d (%s) ]: ", __FILE__, __LINE__, __FUNCTION__); \
fprintf(((logFile==NULL)?stdout:logFile),logInfo, ## __VA_ARGS__); \
fflush(logFile); \
UNLOCK; \
} while (0)
#endif // __SIMPLE_LOG_H__
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment