diff --git a/CMakeLists.txt b/CMakeLists.txt index f58be352813b0e83c2ec54021666d0a9e62f930a..dc2930f7f75223b916937474be5bdb8516ddff1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,8 @@ include(external/snappy) include(external/gtest) include(generic) include(flags) +include(external/python) +include(external/pybind11) if (NOT CLIENT_ONLY) include(external/cudnn) diff --git a/cmake/external/pybind11.cmake b/cmake/external/pybind11.cmake new file mode 100644 index 0000000000000000000000000000000000000000..a2dbf4cdb7c000c713124993a1643f1b086ff263 --- /dev/null +++ b/cmake/external/pybind11.cmake @@ -0,0 +1,42 @@ +# Copyright (c) 2016 PaddlePaddle Authors. 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. + +include(ExternalProject) + +set(PYBIND_SOURCE_DIR ${THIRD_PARTY_PATH}/pybind) + +include_directories(${PYBIND_SOURCE_DIR}/src/extern_pybind/include) + +ExternalProject_Add( + extern_pybind + ${EXTERNAL_PROJECT_LOG_ARGS} + GIT_REPOSITORY "https://github.com/pybind/pybind11.git" + GIT_TAG "v2.2.4" + PREFIX ${PYBIND_SOURCE_DIR} + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) + +if(${CMAKE_VERSION} VERSION_LESS "3.3.0") + set(dummyfile ${CMAKE_CURRENT_BINARY_DIR}/pybind_dummy.c) + file(WRITE ${dummyfile} "const char * dummy_pybind = \"${dummyfile}\";") + add_library(pybind STATIC ${dummyfile}) +else() + add_library(pybind INTERFACE) +endif() + +add_dependencies(pybind extern_pybind) diff --git a/cmake/external/python.cmake b/cmake/external/python.cmake new file mode 100644 index 0000000000000000000000000000000000000000..7679cd5f0058362bdaf262b0f79e522ff80fbf76 --- /dev/null +++ b/cmake/external/python.cmake @@ -0,0 +1,65 @@ +# Copyright (c) 2016 PaddlePaddle Authors. 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. + +FIND_PACKAGE(PythonInterp ${PY_VERSION} REQUIRED) +FIND_PACKAGE(PythonLibs ${PY_VERSION} REQUIRED) + +if(WIN32) + execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" +"from distutils import sysconfig as s;import sys;import struct; +print(sys.prefix); +print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE) + + if(NOT _PYTHON_SUCCESS MATCHES 0) + set(PYTHONLIBS_FOUND FALSE) + return() + endif() + + # Convert the process output into a list + string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) + string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) + list(GET _PYTHON_VALUES 0 PYTHON_PREFIX) + list(GET _PYTHON_VALUES 1 PYTHON_LIBRARY_SUFFIX) + + # Make sure all directory separators are '/' + string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX}) + + set(PYTHON_LIBRARY + "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + + # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the + # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. + if(NOT EXISTS "${PYTHON_LIBRARY}") + get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) + set(PYTHON_LIBRARY + "${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + endif() + + # raise an error if the python libs are still not found. + if(NOT EXISTS "${PYTHON_LIBRARY}") + message(FATAL_ERROR "Python libraries not found") + endif() + SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") +endif(WIN32) + +# Fixme: Maybe find a static library. Get SHARED/STATIC by FIND_PACKAGE. +ADD_LIBRARY(python SHARED IMPORTED GLOBAL) +SET_PROPERTY(TARGET python PROPERTY IMPORTED_LOCATION ${PYTHON_LIBRARIES}) + +SET(py_env "") +INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIR}) diff --git a/elastic-ctr/client/CMakeLists.txt b/elastic-ctr/client/CMakeLists.txt index 3d93fc5eed99f393623291ebddfc6bb9b237e0bf..9066f4cfb87df4213c80dcfd0d7603c9a2f01c20 100644 --- a/elastic-ctr/client/CMakeLists.txt +++ b/elastic-ctr/client/CMakeLists.txt @@ -4,13 +4,17 @@ file(GLOB sdk_cpp_srcs ${CMAKE_SOURCE_DIR}/sdk-cpp/src/*.cpp) list(APPEND elasticctr_srcs ${elastic_ctr_cpp_srcs}) list(APPEND elasticctr_srcs ${sdk_cpp_srcs}) list(APPEND elasticctr_srcs - ${CMAKE_CURRENT_LIST_DIR}/api/elastic_ctr_api.cpp) + ${CMAKE_CURRENT_LIST_DIR}/api/elastic_ctr_api.cpp + ${CMAKE_CURRENT_LIST_DIR}/api/pybind.cpp) add_library(elasticctr SHARED ${elasticctr_srcs}) -target_link_libraries(elasticctr brpc configure protobuf leveldb) +add_dependencies(elasticctr pybind) +target_link_libraries(elasticctr brpc configure protobuf leveldb -lcrypto + -lssl -lz) add_executable(elastic_ctr_demo ${CMAKE_CURRENT_LIST_DIR}/demo/demo.cpp) -target_link_libraries(elastic_ctr_demo elasticctr -lpthread -lcrypto -lm -lrt -lssl -ldl -lz) +target_link_libraries(elastic_ctr_demo elasticctr python -lpthread -lcrypto -lm -lrt + -lssl -ldl -lz) # install install(TARGETS elastic_ctr_demo diff --git a/elastic-ctr/client/api/elastic_ctr_api.cpp b/elastic-ctr/client/api/elastic_ctr_api.cpp index c568c32e250a8f1a4eff7dcb9a9ae8c53871fe86..9596ae14ee17fdc4f8db850520191a1bb8abe67c 100644 --- a/elastic-ctr/client/api/elastic_ctr_api.cpp +++ b/elastic-ctr/client/api/elastic_ctr_api.cpp @@ -93,14 +93,14 @@ void ThreadResource::validate_request(const std::set &slot_names) { } } -void ElasticCTRPredictorApi::read_slot_conf(const char *path, - const char *slot_conf_file) { +int ElasticCTRPredictorApi::read_slot_conf(const char *path, + const char *slot_conf_file) { struct stat stat_buf; char name[VARIABLE_NAME_LEN]; snprintf(name, VARIABLE_NAME_LEN, "%s/%s", path, slot_conf_file); if (stat(name, &stat_buf) != 0) { LOG(ERROR) << "Error stating file" << name; - return; + return -1; } std::ifstream fs(name); @@ -113,13 +113,22 @@ void ElasticCTRPredictorApi::read_slot_conf(const char *path, LOG(INFO) << "slot: " << x.c_str(); } #endif + + return 0; } int ElasticCTRPredictorApi::init(const char *path, const char *slot_conf_file, const char *serving_conf_file) { - api_.create(path, serving_conf_file); - read_slot_conf(path, slot_conf_file); + int ret = api_.create(path, serving_conf_file); + if (ret != 0) { + return ret; + } + + ret = read_slot_conf(path, slot_conf_file); + if (ret != 0) { + return ret; + } // Thread-local storage if (pthread_key_create(&tls_bspec_key_, thread_resource_delete) != 0) { @@ -231,7 +240,8 @@ void ElasticCTRPredictorApi::validate_request() { thread_resource->validate_request(slot_names_); } -int ElasticCTRPredictorApi::inference() { +int ElasticCTRPredictorApi::inference( + std::vector> &results_vec) { ThreadResource *thread_resource = reinterpret_cast(pthread_getspecific(tls_bspec_key_)); if (thread_resource == NULL) { @@ -257,33 +267,19 @@ int ElasticCTRPredictorApi::inference() { return ret; } - return 0; -} - -std::vector ElasticCTRPredictorApi::get_results() { - std::vector prediction_vec; - - ThreadResource *thread_resource = - reinterpret_cast(pthread_getspecific(tls_bspec_key_)); - if (thread_resource == NULL) { - if (thread_resource == NULL) { - LOG(ERROR) << "ERROR: thread local resource is null"; - return prediction_vec; - } - } - Response *response = thread_resource->get_response(); for (int i = 0; i < response->predictions_size(); ++i) { const ResInstance &res_instance = response->predictions(i); - Prediction prediction; - prediction.prob0 = res_instance.prob0(); - prediction.prob1 = res_instance.prob1(); - prediction_vec.push_back(prediction); + std::vector res; + res.push_back(res_instance.prob0()); + res.push_back(res_instance.prob1()); + results_vec.push_back(res); } - return prediction_vec; + return 0; } + } // namespace elastic_ctr } // namespace paddle_serving } // namespace baidu diff --git a/elastic-ctr/client/api/elastic_ctr_api.h b/elastic-ctr/client/api/elastic_ctr_api.h index d9db6b7b8e2a37b55b8e1ea32127e05eae49596c..d2bfabe7b1380647b3137398f0ac572271dbc8a3 100644 --- a/elastic-ctr/client/api/elastic_ctr_api.h +++ b/elastic-ctr/client/api/elastic_ctr_api.h @@ -91,11 +91,10 @@ class ElasticCTRPredictorApi { int add_slot(ReqInstance *instance, const std::string slot_name, int64_t value); - int inference(); - std::vector get_results(); + int inference(std::vector> &results_vec); // NOLINT private: - static void read_slot_conf(const char *path, const char *slot_conf_file); + static int read_slot_conf(const char *path, const char *slot_conf_file); void validate_request(); private: diff --git a/elastic-ctr/client/api/pybind.cpp b/elastic-ctr/client/api/pybind.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0f71eafda71f2eb6e4faad5985bec5ed7d58acd --- /dev/null +++ b/elastic-ctr/client/api/pybind.cpp @@ -0,0 +1,61 @@ +/* Copyright (c) 2019 PaddlePaddle Authors. 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. */ +#include +#include +#include +#include +#include +#include // NOLINT // for call_once +#include +#include +#include +#include +#include +#include "elastic-ctr/client/api/elastic_ctr_api.h" +#include "pybind11/pybind11.h" +#include "pybind11/stl.h" + +namespace baidu { +namespace paddle_serving { +namespace elastic_ctr { + +namespace py = pybind11; +using baidu::paddle_serving::predictor::elastic_ctr::ReqInstance; + +PYBIND11_MODULE(elasticctr, m) { + m.doc() = "C++ core of ElasticCTRPredictorApi"; + + py::class_(m, "ReqInstance", py::buffer_protocol()) + .def(py::init<>()); + + py::class_( + m, "ElasticCTRPredictorApi", py::buffer_protocol()) + .def(py::init<>()) + .def("init", &ElasticCTRPredictorApi::init) + .def("thrd_initialize", &ElasticCTRPredictorApi::thrd_initialize) + .def("thrd_clear", &ElasticCTRPredictorApi::thrd_clear) + .def("thrd_finalize", &ElasticCTRPredictorApi::thrd_finalize) + .def("destroy", &ElasticCTRPredictorApi::destroy) + .def("add_instance", &ElasticCTRPredictorApi::add_instance) + .def("add_slot", &ElasticCTRPredictorApi::add_slot) + .def("inference", + [](ElasticCTRPredictorApi &self) -> std::vector> { + std::vector> results_vec; + self.inference(results_vec); + return results_vec; + }); +} +} // namespace elastic_ctr +} // namespace paddle_serving +} // namespace baidu diff --git a/elastic-ctr/client/api/python/elasticctr/__init__.py b/elastic-ctr/client/api/python/elasticctr/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1dd9525a47a3c9c07ab419f09fede465c6a9ac9a --- /dev/null +++ b/elastic-ctr/client/api/python/elasticctr/__init__.py @@ -0,0 +1,24 @@ +# Copyright (c) 2019 PaddlePaddle Authors. 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. + + +def __bootstrap__(): + """ + Returns: + None + """ + import sys + import os + import platform + from . import elasticctr diff --git a/elastic-ctr/client/api/python/elasticctr/elasticctr.py b/elastic-ctr/client/api/python/elasticctr/elasticctr.py new file mode 100644 index 0000000000000000000000000000000000000000..5e6b44f3bed4dced4c4bed831a68d3e42390867c --- /dev/null +++ b/elastic-ctr/client/api/python/elasticctr/elasticctr.py @@ -0,0 +1,49 @@ +# Copyright (c) 2019 PaddlePaddle Authors. 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. + +import elasticctr + + +class ElasticCTRPredictorApi(object): + """ + A ElasticCTRPredictorApi Wrapper class + """ + + def __init__(self): + self.api = elasticctr.ElasticCTRPredictorApi + print "Init OK" + + def init(conf_dir, slot_conf_file, serving_conf_file): + self.api.init(conf_dir, slot_conf_file, serving_conf_file) + + def thrd_initialize(): + self.api.thrd_initialize() + + def thrd_clear(): + self.api.thrd_clear() + + def destroy(): + self.api.destroy() + + def add_instance(): + return self.api.add_instance() + + def add_slot(instance, slot_name, value): + return self.api.add_slot(instance, slot_name, value) + + def inference(): + self.api.inferene() + + def get_results(): + return self.api.get_results() diff --git a/elastic-ctr/client/demo/demo.cpp b/elastic-ctr/client/demo/demo.cpp index 1524e1c9751acb6d2a521639632b7760158793eb..47cb9c8125f564f7dc62dec677da5a9397b2b4a7 100644 --- a/elastic-ctr/client/demo/demo.cpp +++ b/elastic-ctr/client/demo/demo.cpp @@ -158,17 +158,16 @@ int main(int argc, char** argv) { ++index; } - Response res; - if (api.inference() != 0) { + std::vector> results_vec; + if (api.inference(results_vec) != 0) { LOG(ERROR) << "failed call predictor"; return -1; } - std::vector ret = api.get_results(); #if 1 - for (std::size_t i = 0; i < ret.size(); ++i) { - LOG(INFO) << "sample " << i << ": [" << ret[i].prob0 << ", " - << ret[i].prob1 << "]"; + for (std::size_t i = 0; i < results_vec.size(); ++i) { + LOG(INFO) << "sample " << i << ": [" << results_vec[i].at(0) << ", " + << results_vec[i].at(1) << "]"; } #endif } // end while diff --git a/elastic-ctr/client/demo/demo.py b/elastic-ctr/client/demo/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..ca2e8788353853b8d9041e1950e6d55423583703 --- /dev/null +++ b/elastic-ctr/client/demo/demo.py @@ -0,0 +1,50 @@ +# Copyright (c) 2019 PaddlePaddle Authors. 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. + +import os +import os.path +import sys + +path = os.path.abspath(os.path.curdir) + '/../api' +print path +sys.path.append(path) + +import lib +from lib import elasticctr + +api = elasticctr.ElasticCTRPredictorApi() +ret = api.init("./conf", "slot.conf", "predictors.prototxt") + +if ret != 0: + print "api.init fail" + sys.exit(-1) + +api.thrd_initialize() + +api.thrd_clear() + +instance = api.add_instance() +api.add_slot(instance, "0", 1234) +api.add_slot(instance, "1", 1234) +api.add_slot(instance, "2", 1234) +api.add_slot(instance, "3", 1234) +api.add_slot(instance, "4", 1234) +api.add_slot(instance, "5", 1234) +api.add_slot(instance, "6", 1234) + +ret = api.inference() + +print ret + +api.destroy() diff --git a/elastic-ctr/serving/conf/model_toolkit.prototxt b/elastic-ctr/serving/conf/model_toolkit.prototxt index b1f6b5114b8006d9e749bc9b8fdd02206303a02a..17ebe03b9e103321fe073fbe04bad12ab946d931 100644 --- a/elastic-ctr/serving/conf/model_toolkit.prototxt +++ b/elastic-ctr/serving/conf/model_toolkit.prototxt @@ -1,5 +1,5 @@ engines { - name: "ctr_prediction" + name: "elastic_ctr_prediction" type: "FLUID_CPU_ANALYSIS_DIR" reloadable_meta: "./data/model/paddle/fluid_time_file" reloadable_type: "timestamp_ne"