Skip to content

Commit 726c0fb

Browse files
committed
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into new_api
2 parents 05a2a1a + 07c48db commit 726c0fb

File tree

2 files changed

+244
-0
lines changed

2 files changed

+244
-0
lines changed

paddle/fluid/inference/tests/book/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,11 @@ inference_test(recommender_system)
3838
#inference_test(rnn_encoder_decoder)
3939
#inference_test(understand_sentiment ARGS conv)
4040
inference_test(word2vec)
41+
42+
# This is an unly work around to make this test run
43+
# TODO(TJ): clean me up
44+
cc_test(test_inference_nlp
45+
SRCS test_inference_nlp.cc
46+
DEPS paddle_fluid
47+
ARGS
48+
--model_path=${PADDLE_BINARY_DIR}/python/paddle/fluid/tests/book/recognize_digits_mlp.inference.model)
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include <sys/time.h>
16+
#include <time.h>
17+
#include <fstream>
18+
#include <thread> // NOLINT
19+
#include "gflags/gflags.h"
20+
#include "gtest/gtest.h"
21+
#include "paddle/fluid/inference/tests/test_helper.h"
22+
#ifdef PADDLE_WITH_MKLML
23+
#include <mkl_service.h>
24+
#include <omp.h>
25+
#endif
26+
27+
DEFINE_string(model_path, "", "Directory of the inference model.");
28+
DEFINE_string(data_file, "", "File of input index data.");
29+
DEFINE_int32(repeat, 100, "Running the inference program repeat times");
30+
DEFINE_bool(use_mkldnn, false, "Use MKLDNN to run inference");
31+
DEFINE_bool(prepare_vars, true, "Prepare variables before executor");
32+
DEFINE_int32(num_threads, 1, "Number of threads should be used");
33+
34+
inline double GetCurrentMs() {
35+
struct timeval time;
36+
gettimeofday(&time, NULL);
37+
return 1e+3 * time.tv_sec + 1e-3 * time.tv_usec;
38+
}
39+
40+
// This function just give dummy data for recognize_digits model.
41+
size_t DummyData(std::vector<paddle::framework::LoDTensor>* out) {
42+
paddle::framework::LoDTensor input;
43+
SetupTensor<float>(&input, {1, 1, 28, 28}, -1.f, 1.f);
44+
out->emplace_back(input);
45+
return 1;
46+
}
47+
48+
// Load the input word index data from file and save into LodTensor.
49+
// Return the size of words.
50+
size_t LoadData(std::vector<paddle::framework::LoDTensor>* out,
51+
const std::string& filename) {
52+
if (filename.empty()) {
53+
return DummyData(out);
54+
}
55+
56+
size_t sz = 0;
57+
std::fstream fin(filename);
58+
std::string line;
59+
out->clear();
60+
while (getline(fin, line)) {
61+
std::istringstream iss(line);
62+
std::vector<int64_t> ids;
63+
std::string field;
64+
while (getline(iss, field, ' ')) {
65+
ids.push_back(stoi(field));
66+
}
67+
if (ids.size() >= 1024) {
68+
// Synced with NLP guys, they will ignore input larger then 1024
69+
continue;
70+
}
71+
72+
paddle::framework::LoDTensor words;
73+
paddle::framework::LoD lod{{0, ids.size()}};
74+
words.set_lod(lod);
75+
int64_t* pdata = words.mutable_data<int64_t>(
76+
{static_cast<int64_t>(ids.size()), 1}, paddle::platform::CPUPlace());
77+
memcpy(pdata, ids.data(), words.numel() * sizeof(int64_t));
78+
out->emplace_back(words);
79+
sz += ids.size();
80+
}
81+
return sz;
82+
}
83+
84+
// Split input data samples into small pieces jobs as balanced as possible,
85+
// according to the number of threads.
86+
void SplitData(
87+
const std::vector<paddle::framework::LoDTensor>& datasets,
88+
std::vector<std::vector<const paddle::framework::LoDTensor*>>* jobs,
89+
const int num_threads) {
90+
size_t s = 0;
91+
jobs->resize(num_threads);
92+
while (s < datasets.size()) {
93+
for (auto it = jobs->begin(); it != jobs->end(); it++) {
94+
it->emplace_back(&datasets[s]);
95+
s++;
96+
if (s >= datasets.size()) {
97+
break;
98+
}
99+
}
100+
}
101+
}
102+
103+
void ThreadRunInfer(
104+
const int tid, paddle::framework::Executor* executor,
105+
paddle::framework::Scope* scope,
106+
const std::unique_ptr<paddle::framework::ProgramDesc>& inference_program,
107+
const std::vector<std::vector<const paddle::framework::LoDTensor*>>& jobs) {
108+
auto copy_program = std::unique_ptr<paddle::framework::ProgramDesc>(
109+
new paddle::framework::ProgramDesc(*inference_program));
110+
auto& sub_scope = scope->NewScope();
111+
112+
std::string feed_holder_name = "feed_" + paddle::string::to_string(tid);
113+
std::string fetch_holder_name = "fetch_" + paddle::string::to_string(tid);
114+
copy_program->SetFeedHolderName(feed_holder_name);
115+
copy_program->SetFetchHolderName(fetch_holder_name);
116+
117+
const std::vector<std::string>& feed_target_names =
118+
copy_program->GetFeedTargetNames();
119+
const std::vector<std::string>& fetch_target_names =
120+
copy_program->GetFetchTargetNames();
121+
122+
PADDLE_ENFORCE_EQ(fetch_target_names.size(), 1UL);
123+
std::map<std::string, paddle::framework::LoDTensor*> fetch_targets;
124+
paddle::framework::LoDTensor outtensor;
125+
fetch_targets[fetch_target_names[0]] = &outtensor;
126+
127+
std::map<std::string, const paddle::framework::LoDTensor*> feed_targets;
128+
PADDLE_ENFORCE_EQ(feed_target_names.size(), 1UL);
129+
130+
auto& inputs = jobs[tid];
131+
auto start_ms = GetCurrentMs();
132+
for (size_t i = 0; i < inputs.size(); ++i) {
133+
feed_targets[feed_target_names[0]] = inputs[i];
134+
executor->Run(*copy_program, &sub_scope, &feed_targets, &fetch_targets,
135+
true /*create_local_scope*/, true /*create_vars*/,
136+
feed_holder_name, fetch_holder_name);
137+
}
138+
auto stop_ms = GetCurrentMs();
139+
scope->DeleteScope(&sub_scope);
140+
LOG(INFO) << "Tid: " << tid << ", process " << inputs.size()
141+
<< " samples, avg time per sample: "
142+
<< (stop_ms - start_ms) / inputs.size() << " ms";
143+
}
144+
145+
TEST(inference, nlp) {
146+
if (FLAGS_model_path.empty()) {
147+
LOG(FATAL) << "Usage: ./example --model_path=path/to/your/model";
148+
}
149+
if (FLAGS_data_file.empty()) {
150+
LOG(WARNING) << "No data file provided, will use dummy data!"
151+
<< "Note: if you use nlp model, please provide data file.";
152+
}
153+
LOG(INFO) << "Model Path: " << FLAGS_model_path;
154+
LOG(INFO) << "Data File: " << FLAGS_data_file;
155+
156+
std::vector<paddle::framework::LoDTensor> datasets;
157+
size_t num_total_words = LoadData(&datasets, FLAGS_data_file);
158+
LOG(INFO) << "Number of samples (seq_len<1024): " << datasets.size();
159+
LOG(INFO) << "Total number of words: " << num_total_words;
160+
161+
const bool model_combined = false;
162+
// 0. Call `paddle::framework::InitDevices()` initialize all the devices
163+
// 1. Define place, executor, scope
164+
auto place = paddle::platform::CPUPlace();
165+
auto executor = paddle::framework::Executor(place);
166+
std::unique_ptr<paddle::framework::Scope> scope(
167+
new paddle::framework::Scope());
168+
169+
// 2. Initialize the inference_program and load parameters
170+
std::unique_ptr<paddle::framework::ProgramDesc> inference_program;
171+
inference_program =
172+
InitProgram(&executor, scope.get(), FLAGS_model_path, model_combined);
173+
if (FLAGS_use_mkldnn) {
174+
EnableMKLDNN(inference_program);
175+
}
176+
177+
#ifdef PADDLE_WITH_MKLML
178+
// only use 1 thread number per std::thread
179+
omp_set_dynamic(0);
180+
omp_set_num_threads(1);
181+
mkl_set_num_threads(1);
182+
#endif
183+
184+
double start_ms = 0, stop_ms = 0;
185+
if (FLAGS_num_threads > 1) {
186+
std::vector<std::vector<const paddle::framework::LoDTensor*>> jobs;
187+
SplitData(datasets, &jobs, FLAGS_num_threads);
188+
std::vector<std::unique_ptr<std::thread>> threads;
189+
start_ms = GetCurrentMs();
190+
for (int i = 0; i < FLAGS_num_threads; ++i) {
191+
threads.emplace_back(
192+
new std::thread(ThreadRunInfer, i, &executor, scope.get(),
193+
std::ref(inference_program), std::ref(jobs)));
194+
}
195+
for (int i = 0; i < FLAGS_num_threads; ++i) {
196+
threads[i]->join();
197+
}
198+
stop_ms = GetCurrentMs();
199+
} else {
200+
if (FLAGS_prepare_vars) {
201+
executor.CreateVariables(*inference_program, scope.get(), 0);
202+
}
203+
// always prepare context
204+
std::unique_ptr<paddle::framework::ExecutorPrepareContext> ctx;
205+
ctx = executor.Prepare(*inference_program, 0);
206+
207+
// preapre fetch
208+
const std::vector<std::string>& fetch_target_names =
209+
inference_program->GetFetchTargetNames();
210+
PADDLE_ENFORCE_EQ(fetch_target_names.size(), 1UL);
211+
std::map<std::string, paddle::framework::LoDTensor*> fetch_targets;
212+
paddle::framework::LoDTensor outtensor;
213+
fetch_targets[fetch_target_names[0]] = &outtensor;
214+
215+
// prepare feed
216+
const std::vector<std::string>& feed_target_names =
217+
inference_program->GetFeedTargetNames();
218+
PADDLE_ENFORCE_EQ(feed_target_names.size(), 1UL);
219+
std::map<std::string, const paddle::framework::LoDTensor*> feed_targets;
220+
221+
// feed data and run
222+
start_ms = GetCurrentMs();
223+
for (size_t i = 0; i < datasets.size(); ++i) {
224+
feed_targets[feed_target_names[0]] = &(datasets[i]);
225+
executor.RunPreparedContext(ctx.get(), scope.get(), &feed_targets,
226+
&fetch_targets, !FLAGS_prepare_vars);
227+
}
228+
stop_ms = GetCurrentMs();
229+
LOG(INFO) << "Tid: 0, process " << datasets.size()
230+
<< " samples, avg time per sample: "
231+
<< (stop_ms - start_ms) / datasets.size() << " ms";
232+
}
233+
LOG(INFO) << "Total inference time with " << FLAGS_num_threads
234+
<< " threads : " << (stop_ms - start_ms) / 1000.0
235+
<< " sec, QPS: " << datasets.size() / ((stop_ms - start_ms) / 1000);
236+
}

0 commit comments

Comments
 (0)