diff --git a/inference/C/README.md b/inference/C/README.md new file mode 100644 index 0000000..243575c --- /dev/null +++ b/inference/C/README.md @@ -0,0 +1,14 @@ +# Inference demo + +This is an inference demo program based on the Paddle C API. But this demo is based on the c++ code, so need to use g++ or clang++ to compile. +The demo can be run from the command line and used to test the inference performance of various models. + +## Android +To compile and run this demo in the Android environment, follow these steps: + +1. Refer to [this document](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/howto/cross_compiling/cross_compiling_for_android_cn.md) to compile the paddle of android version. +2. Compile this inference.cc to an executable program for the Android environment. +3. Run the demo program by logging into the Android environment via adb and specifying the paddle model from the command line. +``` +./inference --merged_model ./model/mobilenet.paddle --input_size 150528 +``` diff --git a/inference/C/inference.cc b/inference/C/inference.cc new file mode 100644 index 0000000..01084b7 --- /dev/null +++ b/inference/C/inference.cc @@ -0,0 +1,171 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +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. */ + +#include +#include +#include +#include +#include + +inline paddle_error& operator |=(paddle_error& a, paddle_error b) { + return a = + static_cast(static_cast(a) | static_cast(b)); +} + +class Timer { +public: + Timer(std::string name, int iter = 1) : name_(name), iter_(iter) { + clock_gettime(CLOCK_MONOTONIC, &tp_start); + } + ~Timer() { + struct timespec tp_end; + clock_gettime(CLOCK_MONOTONIC, &tp_end); + float time = ((tp_end.tv_nsec - tp_start.tv_nsec)/1000000.0f); + time += (tp_end.tv_sec - tp_start.tv_sec)*1000; + time /= iter_; + std::cout << "Time of " << name_ << " " << time << " ms." << std::endl; + } + +private: + std::string name_; + int iter_; + struct timespec tp_start; +}; + +void read_file(const char* file, void** buf, long* size) { + FILE* fp = fopen(file, "r"); + if (fp) { + if (fseek(fp, 0L, SEEK_END) == 0) { + *size = ftell(fp); + fseek(fp, 0L, SEEK_SET); + *buf = malloc(*size); + fread(*buf, 1, *size, fp); + } + } + fclose(fp); +} + +int main(int argc, char* argv[]) { + // parse command line arguments + std::string predict_config; + std::string predict_model; + std::string merged_model; + int input_size; + for (int i = 1; i < argc; ++i) { + if (std::string(argv[i]) == "--predict_config") { + predict_config = std::string(argv[++i]); + } else if (std::string(argv[i]) == "--predict_model") { + predict_model = std::string(argv[++i]); + } else if (std::string(argv[i]) == "--merged_model") { + merged_model = std::string(argv[++i]); + } else if (std::string(argv[i]) == "--input_size") { + input_size = atoi(argv[++i]); + } + } + + { + Timer time("init paddle"); + char* argv[] = {"--use_gpu=False"}; + if (paddle_init(1, (char**)argv) != kPD_NO_ERROR) { + std::cout << "paddle init error!" << std::endl; + } + } + + // Create a gradient machine for inference. + paddle_gradient_machine machine; + paddle_error error = kPD_NO_ERROR; + if (!merged_model.empty()) { + Timer time("create from merged model file"); + long size = 0; + void* buf = NULL; + read_file(merged_model.c_str(), &buf, &size); + paddle_gradient_machine_create_for_inference_with_parameters( + &machine, buf, size); + free(buf); + } else { + // Reading config binary file. It is generated by `convert_protobin.sh` + if (predict_config.empty()) return -1; + long size = 0; + void* buf = NULL; + { + Timer time("read model config"); + read_file(predict_config.c_str(), &buf, &size); + } + + error |= + paddle_gradient_machine_create_for_inference(&machine, buf, (int)size); + + if (predict_model.empty()) { + error |= paddle_gradient_machine_randomize_param(machine); + } else { + Timer time("load model parameter"); + error |= paddle_gradient_machine_load_parameter_from_disk( + machine, predict_model.c_str()); + } + free(buf); + } + + if (error != kPD_NO_ERROR) { + std::cout << "paddle create inference machine error!" << std::endl; + } + + // Create input matrix. + paddle_arguments in_args = paddle_arguments_create_none(); + error |= paddle_arguments_resize(in_args, 1); + paddle_matrix mat = paddle_matrix_create(/* sample_num */ 1, + /* size */ input_size, + /* useGPU */ false); + srand(time(0)); + paddle_real* array; + // Get First row. + error |= paddle_matrix_get_row(mat, 0, &array); + + for (int i = 0; i < input_size; ++i) { + array[i] = rand() / ((float)RAND_MAX); + } + + error |= paddle_arguments_set_value(in_args, 0, mat); + + paddle_arguments out_args = paddle_arguments_create_none(); + + if (error != kPD_NO_ERROR) { + std::cout << "paddle init input data!" << std::endl; + } + + error |= paddle_gradient_machine_forward(machine, + in_args, + out_args, + /* isTrain */ false); + + { + Timer time("forward time", 20); + for (int i = 0; i < 20; i++) { + error |= paddle_gradient_machine_forward(machine, + in_args, + out_args, + /* isTrain */ false); + } + } + + if (error != kPD_NO_ERROR) { + std::cout << "paddle forward error!" << std::endl; + } + + paddle_arguments_destroy(out_args); + paddle_matrix_destroy(mat); + paddle_arguments_destroy(in_args); + paddle_gradient_machine_destroy(machine); + + return 0; +}