Add nGraph Execution Provider (#832)

* Add nGraph Execution Provider

* feedback changes 1

* feedback2

* Feedback and upgrade nGraph

* Feedback 4

* Fix CI

* Disable new ops
This commit is contained in:
nivas-x86 2019-04-20 17:02:35 -07:00 committed by jywu-msft
parent 7e1edbb9a2
commit a4d7052aeb
48 changed files with 2104 additions and 21 deletions

View file

@ -2810,4 +2810,212 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
_____
NervanaSystems/ngraph
Copyright 2016-2019 Intel Corporation
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View file

@ -289,6 +289,15 @@
"repositoryUrl":"https://github.com/onnx/onnx-tensorrt.git"
}
}
},
{
"component":{
"type":"git",
"git":{
"commitHash":"950fbf183274ab7d2092f99bab6c809ae87c7054",
"repositoryUrl":"https://github.com/NervanaSystems/ngraph.git"
}
}
}
],
"Version":1

View file

@ -51,6 +51,7 @@ option(onnxruntime_USE_EIGEN_FOR_BLAS "Use eign for blas" ON)
option(onnxruntime_USE_MLAS "Use optimized blas library for GEMM and 2D Convolution" ON)
option(onnxruntime_USE_MKLDNN "Build with MKL-DNN support" OFF)
option(onnxruntime_USE_MKLML "Build MKL-DNN with MKL-ML binary dependency" OFF)
option(onnxruntime_USE_NGRAPH "Build with nGraph support" OFF)
option(onnxruntime_USE_OPENBLAS "Use openblas" OFF)
option(onnxruntime_DEV_MODE "Enable developer warnings and treat most of them as error." OFF)
option(onnxruntime_USE_JEMALLOC "Use jecmalloc" OFF)
@ -475,6 +476,22 @@ if (onnxruntime_USE_MKLDNN)
link_directories(${MKLDNN_LIB_DIR})
endif()
if (onnxruntime_USE_NGRAPH)
if (Win32)
message(FATAL_ERROR "nGraph is not currently supported on Windows.")
endif()
#if (onnxruntime_USE_OPENMP)
# message(FATAL_ERROR "Please set onnxruntime_USE_OPENMP=OFF for nGraph execution provider.")
#endif()
if (NOT onnxruntime_USE_FULL_PROTOBUF)
message(FATAL_ERROR "Please set onnxruntime_USE_FULL_PROTOBUF=ON for nGraph execution provider.")
endif()
add_definitions(-DUSE_NGRAPH=1)
include(ngraph)
list(APPEND onnxruntime_EXTERNAL_LIBRARIES ngraph)
list(APPEND onnxruntime_EXTERNAL_DEPENDENCIES ngraph)
endif()
if (onnxruntime_USE_OPENBLAS)
add_definitions(-DUSE_OPENBLAS=1)
if (WIN32)

57
cmake/external/ngraph.cmake vendored Normal file
View file

@ -0,0 +1,57 @@
# Copyright(C) 2019 Intel Corporation
# Licensed under the MIT License
include (ExternalProject)
set(ngraph_ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/ngraph)
set(ngraph_INSTALL_DIR ${ngraph_ROOT_DIR})
set(ngraph_INCLUDE_DIRS ${ngraph_INSTALL_DIR}/include)
set(ngraph_LIBRARIES ${ngraph_INSTALL_DIR}/lib)
set(ngraph_SRC ${CMAKE_CURRENT_BINARY_DIR}/ngraph/src/project_ngraph)
set(prebuilt_ONNX_SOURCE_DIR "${PROJECT_SOURCE_DIR}/external/onnx")
set(prebuilt_ONNX_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/onnx")
set(ngraph_URL "https://github.com/NervanaSystems/ngraph.git")
set(ngraph_TAG "v0.18.1")
# Libraries for python package.
set(NGRAPH_SHARED_LIB libngraph.so)
set(NGRAPH_CODEGEN_SHARED_LIB libcodegen.so)
set(NGRAPH_CPU_BACKEND_SHARED_LIB libcpu_backend.so)
set(NGRAPH_IOMP5MD_SHARED_LIB libiomp5.so)
set(NGRAPH_MKLDNN_SHARED_LIB libmkldnn.so)
set(NGRAPH_MKLML_SHARED_LIB libmklml_intel.so)
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(NGRAPH_TBB_SHARED_LIB libtbb_debug.so)
set(NGRAPH_TBB_SHARED_LIB_2 libtbb_debug.so.2)
else()
set(NGRAPH_TBB_SHARED_LIB libtbb.so)
set(NGRAPH_TBB_SHARED_LIB_2 libtbb.so.2)
endif()
ExternalProject_Add(project_ngraph
PREFIX ngraph
GIT_REPOSITORY ${ngraph_URL}
GIT_TAG ${ngraph_TAG}
# Here we use onnx and protobuf built by onnxruntime to avoid linking with incompatible libraries. This might change in future.
PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/patches/ngraph/ngraph_onnx.cmake ${ngraph_SRC}/cmake/external_onnx.cmake
# TODO: Use cmake.file+copy as above.
COMMAND patch -p1 < ${PROJECT_SOURCE_DIR}/patches/ngraph/ngraph_protobuf.patch
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DNGRAPH_USE_PREBUILT_LLVM=TRUE
-DNGRAPH_USE_SYSTEM_PROTOBUF=FALSE
-DNGRAPH_ONNX_IMPORT_ENABLE=TRUE
-DNGRAPH_INTERPRETER_ENABLE=FALSE
-DNGRAPH_ONNXIFI_ENABLE=FALSE
-DNGRAPH_UNIT_TEST_ENABLE=FALSE
-DNGRAPH_TOOLS_ENABLE=FALSE
-DCMAKE_INSTALL_PREFIX=${ngraph_INSTALL_DIR}
-Dprebuilt_ONNX_BINARY_DIR=${prebuilt_ONNX_BINARY_DIR}
-Dprebuilt_ONNX_SOURCE_DIR=${prebuilt_ONNX_SOURCE_DIR}
DEPENDS onnx
)
add_library(ngraph SHARED IMPORTED)
set_property(TARGET ngraph PROPERTY IMPORTED_LOCATION ${ngraph_LIBRARIES}/${NGRAPH_SHARED_LIB})
add_dependencies(ngraph project_ngraph)
include_directories(${ngraph_INCLUDE_DIRS})

View file

@ -59,6 +59,7 @@ target_link_libraries(onnxruntime PRIVATE
${onnxruntime_libs}
${PROVIDERS_CUDA}
${PROVIDERS_MKLDNN}
${PROVIDERS_NGRAPH}
${PROVIDERS_TENSORRT}
onnxruntime_optimizer
onnxruntime_providers

View file

@ -22,6 +22,10 @@ if(onnxruntime_USE_MKLDNN)
set(PROVIDERS_MKLDNN onnxruntime_providers_mkldnn)
list(APPEND ONNXRUNTIME_PROVIDER_NAMES mkldnn)
endif()
if(onnxruntime_USE_NGRAPH)
set(PROVIDERS_NGRAPH onnxruntime_providers_ngraph)
list(APPEND ONNXRUNTIME_PROVIDER_NAMES ngraph)
endif()
if(onnxruntime_USE_CUDA)
set(PROVIDERS_CUDA onnxruntime_providers_cuda)
list(APPEND ONNXRUNTIME_PROVIDER_NAMES cuda)
@ -176,6 +180,26 @@ if (onnxruntime_USE_TENSORRT)
endif()
endif()
if (onnxruntime_USE_NGRAPH)
include_directories("${CMAKE_CURRENT_BINARY_DIR}/onnx")
file(GLOB_RECURSE onnxruntime_providers_ngraph_cc_srcs
"${ONNXRUNTIME_ROOT}/core/providers/ngraph/*.h"
"${ONNXRUNTIME_ROOT}/core/providers/ngraph/*.cc"
)
source_group(TREE ${ONNXRUNTIME_ROOT}/core FILES ${onnxruntime_providers_ngraph_cc_srcs})
add_library(onnxruntime_providers_ngraph ${onnxruntime_providers_ngraph_cc_srcs})
onnxruntime_add_include_to_target(onnxruntime_providers_ngraph onnxruntime_common onnxruntime_framework gsl onnx onnx_proto protobuf::libprotobuf)
add_dependencies(onnxruntime_providers_ngraph ngraph onnx ${onnxruntime_EXTERNAL_DEPENDENCIES})
set_target_properties(onnxruntime_providers_ngraph PROPERTIES FOLDER "ONNXRuntime")
target_include_directories(onnxruntime_providers_ngraph PRIVATE ${ONNXRUNTIME_ROOT} ${ngraph_INCLUDE_DIRS})
set_target_properties(onnxruntime_providers_ngraph PROPERTIES LINKER_LANGUAGE CXX)
target_compile_options(onnxruntime_providers_ngraph PRIVATE "SHELL:-Wformat" "SHELL:-Wformat-security" "SHELL:-fstack-protector-strong" "SHELL:-D_FORTIFY_SOURCE=2")
target_link_options(onnxruntime_providers_ngraph PRIVATE "LINKER:-z, noexecstack " "LINKER:-z relro" "LINKER:-z now" "LINKER:-pie")
endif()
if (onnxruntime_ENABLE_MICROSOFT_INTERNAL)
include(onnxruntime_providers_internal.cmake)
endif()

View file

@ -69,6 +69,7 @@ set(onnxruntime_pybind11_state_libs
${PROVIDERS_CUDA}
${PROVIDERS_MKLDNN}
${PROVIDERS_TENSORRT}
${PROVIDERS_NGRAPH}
onnxruntime_optimizer
onnxruntime_providers
onnxruntime_util
@ -188,6 +189,23 @@ if (onnxruntime_USE_MKLDNN)
$<TARGET_FILE_DIR:${test_data_target}>/onnxruntime/capi/
)
endif()
if (onnxruntime_USE_NGRAPH)
add_custom_command(
TARGET onnxruntime_pybind11_state POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${ngraph_LIBRARIES}/${NGRAPH_SHARED_LIB}
${ngraph_LIBRARIES}/${NGRAPH_CODEGEN_SHARED_LIB}
${ngraph_LIBRARIES}/${NGRAPH_CPU_BACKEND_SHARED_LIB}
${ngraph_LIBRARIES}/${NGRAPH_IOMP5MD_SHARED_LIB}
${ngraph_LIBRARIES}/${NGRAPH_MKLDNN_SHARED_LIB}
${ngraph_LIBRARIES}/${NGRAPH_MKLML_SHARED_LIB}
${ngraph_LIBRARIES}/${NGRAPH_TBB_SHARED_LIB}
${ngraph_LIBRARIES}/${NGRAPH_TBB_SHARED_LIB_2}
$<TARGET_FILE_DIR:${test_data_target}>/onnxruntime/capi/
)
endif()
if (onnxruntime_USE_TVM)
add_custom_command(
TARGET onnxruntime_pybind11_state POST_BUILD

View file

@ -124,6 +124,13 @@ file(GLOB_RECURSE onnxruntime_test_providers_cpu_src
)
list(APPEND onnxruntime_test_providers_src ${onnxruntime_test_providers_cpu_src})
if (onnxruntime_USE_NGRAPH)
file(GLOB_RECURSE onnxruntime_test_providers_ngraph_src
"${TEST_SRC_DIR}/providers/ngraph/*"
)
list(APPEND onnxruntime_test_providers_src ${onnxruntime_test_providers_ngraph_src})
endif()
# tests from lowest level library up.
# the order of libraries should be maintained, with higher libraries being added first in the list
@ -172,6 +179,10 @@ if(onnxruntime_USE_MKLDNN)
list(APPEND onnxruntime_test_providers_dependencies onnxruntime_providers_mkldnn)
endif()
if(onnxruntime_USE_NGRAPH)
list(APPEND onnxruntime_test_providers_dependencies onnxruntime_providers_ngraph)
endif()
file(GLOB_RECURSE onnxruntime_test_tvm_src
"${ONNXRUNTIME_ROOT}/test/tvm/*.h"
"${ONNXRUNTIME_ROOT}/test/tvm/*.cc"
@ -187,6 +198,7 @@ set(ONNXRUNTIME_TEST_LIBS
${PROVIDERS_CUDA}
${PROVIDERS_MKLDNN}
${PROVIDERS_TENSORRT}
${PROVIDERS_NGRAPH}
onnxruntime_optimizer
onnxruntime_providers
onnxruntime_util

View file

@ -0,0 +1,28 @@
set(BINARY_DIR "${prebuilt_ONNX_BINARY_DIR}")
set(ONNX_INCLUDE_DIR ${BINARY_DIR})
set(ONNX_SOURCE_INCLUDE_DIR "${prebuilt_ONNX_SOURCE_DIR}/onnx")
include_directories("${ONNX_SOURCE_INCLUDE_DIR}")
set(ONNX_PROTO_INCLUDE_DIR ${ONNX_INCLUDE_DIR})
set(ONNX_LIBRARY ${BINARY_DIR}/libonnx.a)
set(ONNX_PROTO_LIBRARY ${BINARY_DIR}/libonnx_proto.a)
set(ONNX_LIBRARIES ${ONNX_LIBRARY} ${ONNX_PROTO_LIBRARY})
if (NOT TARGET onnx::libonnx)
add_library(onnx::libonnx UNKNOWN IMPORTED)
set_target_properties(onnx::libonnx PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${ONNX_INCLUDE_DIR}
IMPORTED_LOCATION ${ONNX_LIBRARY}
INCLUDE_DIRECTORIES "${ONNX_SOURCE_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS ONNX_ML=1)
endif()
if (NOT TARGET onnx::libonnx_proto)
add_library(onnx::libonnx_proto UNKNOWN IMPORTED)
set_target_properties(onnx::libonnx_proto PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${ONNX_PROTO_INCLUDE_DIR}
IMPORTED_LOCATION ${ONNX_PROTO_LIBRARY}
INCLUDE_DIRECTORIES "${ONNX_SOURCE_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS ONNX_ML=1)
endif()
add_library(ext_onnx UNKNOWN IMPORTED)
add_dependencies(ext_onnx onnx::libonnx)

View file

@ -0,0 +1,13 @@
diff --git a/cmake/external_protobuf.cmake b/cmake/external_protobuf.cmake
index 47977b3..1a66e1c 100644
--- a/cmake/external_protobuf.cmake
+++ b/cmake/external_protobuf.cmake
@@ -23,7 +23,7 @@ include(ExternalProject)
# This version of PROTOBUF is required by Microsoft ONNX Runtime.
set(NGRAPH_PROTOBUF_GIT_REPO_URL "https://github.com/protocolbuffers/protobuf")
-set(NGRAPH_PROTOBUF_GIT_TAG "v3.5.2")
+set(NGRAPH_PROTOBUF_GIT_TAG "v3.6.1")
ExternalProject_Add(
ext_protobuf

View file

@ -0,0 +1,20 @@
#-------------------------------------------------------------------------
# Copyright(C) 2019 Intel Corporation.
# Licensed under the MIT License.
#--------------------------------------------------------------------------
FROM ubuntu:16.04
RUN apt-get update && \
apt-get install -y sudo \
build-essential curl \
libcurl4-openssl-dev \
libssl-dev wget \
python 3.6 python3-pip \
python3-dev git
RUN pip3 install --upgrade pip
RUN pip3 install numpy onnx
RUN pip3 install onnxruntime-ngraph
WORKDIR /code

View file

@ -6,7 +6,14 @@ enum DType {
TFloat32 = 0,
TInt32 = 1,
TDouble = 2,
TInt64 = 3
TInt64 = 3,
TBool = 4,
TUint8 = 5,
TInt8 = 6,
TUint16 = 7,
TInt16 = 8,
TUint32 = 9,
TUint64 = 10
//TODO: more types
};

View file

@ -18,9 +18,11 @@ constexpr const char* kOnnxDomain = "";
constexpr const char* kOnnxDomainAlias = "ai.onnx";
constexpr const char* kMLDomain = "ai.onnx.ml";
constexpr const char* kMSDomain = "com.microsoft";
constexpr const char* kNGraphDomain = "com.intel.ai";
constexpr const char* kCpuExecutionProvider = "CPUExecutionProvider";
constexpr const char* kCudaExecutionProvider = "CUDAExecutionProvider";
constexpr const char* kMklDnnExecutionProvider = "MKLDNNExecutionProvider";
constexpr const char* kNGraphExecutionProvider = "NGRAPHExecutionProvider";
constexpr const char* kNupharExecutionProvider = "NupharExecutionProvider";
constexpr const char* kBrainSliceExecutionProvider = "BrainSliceExecutionProvider";
constexpr const char* kTensorrtExecutionProvider = "TensorrtExecutionProvider";

View file

@ -102,6 +102,9 @@ class GraphViewer {
return graph_->DomainToVersionMap();
}
/** Check if this is a Subgraph */
bool IsSubgraph() const;
private:
ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(GraphViewer);

View file

@ -0,0 +1,17 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#include "core/session/onnxruntime_c_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \param use_arena zero: false. non-zero: true.
*/
ORT_API_STATUS(OrtSessionOptionsAppendExecutionProvider_NGraph, _In_ OrtSessionOptions* options, _In_ const char* ng_backend_type);
#ifdef __cplusplus
}
#endif

View file

@ -22,6 +22,20 @@ DType ORT_type_to_c_type(MLDataType type) {
return DType::TDouble;
else if (type == DataTypeImpl::GetType<int32_t>())
return DType::TInt32;
else if (type == DataTypeImpl::GetType<bool>())
return DType::TBool;
else if (type == DataTypeImpl::GetType<uint8_t>())
return DType::TUint8;
else if (type == DataTypeImpl::GetType<int8_t>())
return DType::TInt8;
else if (type == DataTypeImpl::GetType<uint16_t>())
return DType::TUint16;
else if (type == DataTypeImpl::GetType<int16_t>())
return DType::TInt16;
else if (type == DataTypeImpl::GetType<uint32_t>())
return DType::TUint32;
else if (type == DataTypeImpl::GetType<uint64_t>())
return DType::TUint64;
else if (type == DataTypeImpl::GetType<int64_t>())
return DType::TInt64;
else

View file

@ -104,4 +104,9 @@ const InitializedTensorSet& GraphViewer::GetAllInitializedTensors() const noexce
const NodeArg* GraphViewer::GetNodeArg(const std::string& name) const {
return graph_->GetNodeArg(name);
}
bool GraphViewer::IsSubgraph() const {
return graph_->IsSubgraph();
}
} // namespace onnxruntime

View file

@ -58,6 +58,7 @@ common::Status MemcpyTransformer::ApplyImpl(Graph& graph, bool& modified, int gr
for (auto& provider : provider_types_) {
if (provider != onnxruntime::kCpuExecutionProvider &&
provider != onnxruntime::kMklDnnExecutionProvider &&
provider != onnxruntime::kNGraphExecutionProvider &&
provider != onnxruntime::kNupharExecutionProvider &&
provider != onnxruntime::kTensorrtExecutionProvider) {
TransformerMemcpyImpl copy_impl(graph, provider);

View file

@ -0,0 +1,203 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#include <fstream>
#include <iostream>
#include <string>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <ngraph/frontend/onnx_import/onnx.hpp>
#pragma GCC diagnostic pop
#include "ngraph_custom_op.h"
#include "core/common/logging/logging.h"
namespace onnxruntime {
namespace ngraph_ep {
static DType GetDataType(const ngraph::element::Type& ng_type) {
switch (ng_type.get_type_enum()) {
case ngraph::element::Type_t::f32:
return DType::TFloat32;
case ngraph::element::Type_t::f64:
return DType::TDouble;
case ngraph::element::Type_t::boolean:
return DType::TBool;
case ngraph::element::Type_t::u8:
return DType::TUint8;
case ngraph::element::Type_t::i8:
return DType::TInt8;
case ngraph::element::Type_t::u16:
return DType::TUint16;
case ngraph::element::Type_t::i16:
return DType::TInt16;
case ngraph::element::Type_t::u32:
return DType::TUint32;
case ngraph::element::Type_t::i32:
return DType::TInt32;
case ngraph::element::Type_t::u64:
return DType::TUint64;
case ngraph::element::Type_t::i64:
return DType::TInt64;
default:
throw "Unsupported DataType";
}
}
NGRAPHCustomOp::NGRAPHCustomOp(const ComputeContext* context, const ONNX_NAMESPACE::ModelProto& model_proto,
const std::shared_ptr<ngraph::runtime::Backend>& ng_backend)
: ng_backend_{ng_backend},
model_proto_{model_proto} {
allocate_func_ = context->allocate_func;
release_func_ = context->release_func;
allocator_ = context->allocator_handle;
name_ = context->node_name;
if (std::getenv("ONNXRUNTIME_NGRAPH_DUMP_OPS") != nullptr) {
std::fstream dump(name_ + ".onnx", std::ios::out | std::ios::trunc | std::ios::binary);
model_proto_.SerializeToOstream(&dump);
}
}
NGRAPHCustomOp::~NGRAPHCustomOp() {
for (const auto& compiled_exe : ng_exe_map_) {
ng_backend_->remove_compiled_function(compiled_exe.second);
}
}
//This method gets called in critical path of execution: Optimize
void NGRAPHCustomOp::Initialize(const ONNXRunTimeTensor* input_tensors, const size_t& num_inputs) const {
LOGS_DEFAULT(INFO) << "nGraph compiling customOp: " << name_;
//Key for ng_exe_map
std::string uniq_input_shape;
//Optimizing for general case of 4D tensors
uniq_input_shape.reserve(4 * sizeof(int64_t) * num_inputs + num_inputs);
for (size_t i = 0; i < num_inputs; i++) {
const auto& ndim = input_tensors[i].ndim;
uniq_input_shape.append(reinterpret_cast<const char*>(&ndim), sizeof(ndim));
uniq_input_shape.append(reinterpret_cast<const char*>(input_tensors[i].shape), ndim * sizeof(int64_t));
}
auto it = ng_exe_map_.insert({uniq_input_shape, nullptr}); //TODO: Limit the size of map with configurable size.
//ng_exe with current shape already exists
if (!it.second) {
ng_curr_exe_ = it.first->second;
return;
} else {
auto graph_proto = model_proto_.mutable_graph();
// Clear previous shapes if any and set new input shapes
for (size_t i = 0; i < num_inputs; i++) {
auto g_in_shape = graph_proto->mutable_input(i)->mutable_type()->mutable_tensor_type()->mutable_shape();
g_in_shape->clear_dim();
for (size_t dim = 0; dim < input_tensors[i].ndim; dim++) {
g_in_shape->add_dim()->set_dim_value(input_tensors[i].shape[dim]);
}
}
std::istringstream model_stream{model_proto_.SerializeAsString()};
std::shared_ptr<ngraph::Function> ng_function;
try {
ng_function = ngraph::onnx_import::import_onnx_model(model_stream);
} catch (const std::exception& exp) {
LOGS_DEFAULT(FATAL) << "[" << name_ << "] "
<< "Exception while converting onnx to nGraph: " << std::string(exp.what());
throw;
} catch (...) {
LOGS_DEFAULT(FATAL) << "[" << name_ << "] "
<< "Unknown exception while converting onnx to nGraph";
throw;
}
for (auto& result : ng_function->get_results()) {
result->set_needs_default_layout(true);
}
// Finally compile nGraph with backend.
try {
ng_curr_exe_ = ng_backend_->compile(ng_function);
} catch (const std::exception& exp) {
LOGS_DEFAULT(FATAL) << "Exception while compiling nGraph Op: " << name_ << std::string(exp.what());
} catch (...) {
LOGS_DEFAULT(FATAL) << "Unknown exception while compiling nGraph Op: " << name_;
}
it.first->second = ng_curr_exe_;
}
} // namespace ngraph_ep
//This method gets called in critical path of execution: Optimize
Status NGRAPHCustomOp::Compute(const ONNXRunTimeTensor* input_tensors, const size_t num_inputs, ONNXRunTimeTensor* const output_tensors, const size_t num_outputs) const {
ORT_UNUSED_PARAMETER(num_outputs);
//TODO: Minimize locked region
std::lock_guard<std::mutex> lock(compute_lock_);
// Initialize nGraph function if it is not already initialized.
Initialize(input_tensors, num_inputs);
ORT_ENFORCE(ng_curr_exe_ != nullptr);
std::vector<std::shared_ptr<ngraph::runtime::Tensor>> ng_inputs;
std::vector<std::shared_ptr<ngraph::runtime::Tensor>> ng_outputs;
// Write ONNXR input data to nGraph input tensors.
try {
auto& in_tensor = input_tensors;
for (const auto& ng_param : ng_curr_exe_->get_parameters()) {
ng_inputs.emplace_back(ng_backend_->create_tensor(ng_param->get_output_element_type(0), ng_param->get_output_shape(0), (in_tensor++)->data));
}
} catch (const std::exception& exp) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Exception while copying input data to nGraph: " + std::string(exp.what()));
} catch (...) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Unknown exception while copying input data to nGraph");
}
// Initialize output tensors
try {
//TODO: Optimize
auto onxr_output = output_tensors;
for (auto& ng_result : ng_curr_exe_->get_results()) {
const auto& dtype = ng_result->get_element_type();
const auto& shape = ng_result->get_shape();
onxr_output->dtype = GetDataType(dtype);
onxr_output->ndim = shape.size();
onxr_output->shape = new int64_t[onxr_output->ndim];
size_t num_elements = 1;
for (size_t dim = 0; dim < shape.size(); dim++) {
num_elements *= shape[dim];
onxr_output->shape[dim] = shape[dim];
}
onxr_output->data = (*(allocate_func_))(allocator_, 64, num_elements * sizeof(onxr_output->dtype));
ng_outputs.emplace_back(ng_backend_->create_tensor(dtype, shape, onxr_output->data));
++onxr_output;
}
} catch (const std::exception& exp) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Exception while creating nGraph output Tensor: " + std::string(exp.what()));
} catch (...) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Unknown exception while creating nGraph output Tensor");
}
// Run the graph through nGraph.
try {
if (!ng_curr_exe_->call(ng_outputs, ng_inputs))
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Error while executing nGraph computation");
} catch (const std::exception& exp) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Exception while executing nGraph computation: " + std::string(exp.what()));
} catch (...) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Unknown exception while executing nGraph computation");
}
return Status::OK();
}
} // namespace ngraph_ep
} // namespace onnxruntime

View file

@ -0,0 +1,54 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#pragma once
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <ngraph/ngraph.hpp>
#pragma GCC diagnostic pop
#include "core/framework/func_api.h"
#include "core/graph/onnx_protobuf.h"
namespace onnxruntime {
namespace ngraph_ep {
class NGRAPHCustomOp {
public:
NGRAPHCustomOp(const ComputeContext* context, const ONNX_NAMESPACE::ModelProto& model_proto, const std::shared_ptr<ngraph::runtime::Backend>& ng_backend);
Status Compute(const ONNXRunTimeTensor* input_tensors, const size_t num_inputs, ONNXRunTimeTensor* const output_tensors, const size_t num_outputs) const;
~NGRAPHCustomOp();
private:
void Initialize(const ONNXRunTimeTensor* input_tensors, const size_t& num_inputs) const;
std::shared_ptr<ngraph::runtime::Backend> ng_backend_;
mutable std::shared_ptr<ngraph::runtime::Executable> ng_curr_exe_ = nullptr;
AllocateFunc allocate_func_ = nullptr;
DestroyFunc release_func_ = nullptr;
AllocatorHandle allocator_ = nullptr;
std::string name_;
/*
nGraph::Executable objects are specific to input shapes.
Here we keep of a cache of nGraph::Executable objects with key as input shapes. TODO: Configure size of this cache.
Logically, key = [i0.rank,[i0.dims],i1.rank,[i1.dims] ... iN.rank,[iN.dims]] raw bytes enclosed inside a string.
Example: input0.shape(1,2,3) input1.shape(4,5)
key = [3,1,2,3,2,4,5]
*/
mutable std::unordered_map<std::string, std::shared_ptr<ngraph::runtime::Executable>> ng_exe_map_;
mutable std::mutex compute_lock_;
mutable ONNX_NAMESPACE::ModelProto model_proto_;
};
} // namespace ngraph_ep
} // namespace onnxruntime

View file

@ -0,0 +1,550 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#include "core/common/common.h"
#include "core/common/logging/logging.h"
#include "core/framework/compute_capability.h"
#include "core/framework/allocatormgr.h"
#include "core/framework/kernel_registry.h"
#include "core/graph/graph_viewer.h"
#include "core/graph/model.h"
#include "ngraph_execution_provider.h"
#include "ngraph_custom_op.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <ngraph/ngraph.hpp>
#include <ngraph/frontend/onnx_import/onnx.hpp>
#pragma GCC diagnostic pop
#define MEMCPY_S(dest, src, destsz, srcsz) memcpy(dest, src, MIN(destsz, srcsz))
namespace onnxruntime {
constexpr const char* NGRAPH = "nGraph";
NGRAPHExecutionProvider::NGRAPHExecutionProvider(const NGRAPHExecutionProviderInfo& info)
: IExecutionProvider{onnxruntime::kNGraphExecutionProvider} {
DeviceAllocatorRegistrationInfo default_allocator_info({OrtMemTypeDefault,
[](int) { return std::make_unique<CPUAllocator>(std::make_unique<OrtAllocatorInfo>(NGRAPH, OrtAllocatorType::OrtDeviceAllocator, 0, OrtMemTypeDefault)); },
std::numeric_limits<size_t>::max()});
InsertAllocator(CreateAllocator(default_allocator_info));
DeviceAllocatorRegistrationInfo cpu_allocator_info({OrtMemTypeCPUOutput,
[](int) { return std::make_unique<CPUAllocator>(std::make_unique<OrtAllocatorInfo>(NGRAPH, OrtAllocatorType::OrtDeviceAllocator, 0, OrtMemTypeCPUOutput)); },
std::numeric_limits<size_t>::max()});
InsertAllocator(CreateAllocator(cpu_allocator_info));
ORT_ENFORCE(info.ng_backend_type == "CPU", "nGraph Execution Provider for onnxruntime currently is only supported for CPU backend.");
try {
ng_backend_ = ngraph::runtime::Backend::create(info.ng_backend_type);
} catch (const std::exception& exp) {
LOGS_DEFAULT(FATAL) << "Exception while creating nGraph " << info.ng_backend_type << " Backend: " << std::string(exp.what());
} catch (...) {
LOGS_DEFAULT(FATAL) << "Unknown exception while while creating nGraph " << info.ng_backend_type << " Backend";
throw;
}
}
std::shared_ptr<KernelRegistry> NGRAPHExecutionProvider::GetKernelRegistry() const {
return std::make_shared<KernelRegistry>();
}
/**
* Checks if a tensor represented by srcLocation can be copied into the dstLocation tensor
* @param src_location result of Location().name call on the source tensor
* @param dst_location result of Location().name call on the destination tensor
* @return true if src and dest locations combination allows copying
*/
bool TensorCopyPossible(const std::string& src_location, const std::string& dst_location) {
// contains allowed combinations of source and destination locations for tensors copying purposes
// the first element of a pair denotes a source, the second - destination
static const std::map<std::string, std::string> allowed_copy_directions = {
{NGRAPH, CPU}, {NGRAPH, NGRAPH}, {CPU, NGRAPH}};
// copying of tensors is allowed only if the params match any of the allowed combinations
return std::any_of(allowed_copy_directions.begin(),
allowed_copy_directions.end(), [&](const auto& copy_direction) {
return src_location == copy_direction.first && dst_location == copy_direction.second;
});
}
Status NGRAPHExecutionProvider::CopyTensor(const Tensor& src, Tensor& dst) const {
const size_t src_bytes = src.DataType()->Size() * src.Shape().Size();
const size_t dst_bytes = dst.DataType()->Size() * dst.Shape().Size();
if (src_bytes != dst_bytes) {
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL,
"nGraph: Source and Destination data sizes are not equal - cannot copy tensors");
}
if (!TensorCopyPossible(src.Location().name, dst.Location().name)) {
ORT_NOT_IMPLEMENTED("Copying tensors between '", src.Location().name, "' and '", dst.Location().name,
"' is not implemented in NGRAPHExecutionProvider");
}
MEMCPY_S(dst.MutableDataRaw(), src.DataRaw(), dst_bytes, src_bytes);
return Status::OK();
}
// Returns true only if op is in a mode that is not currently supported
static bool IsUnsupportedOpMode(const Node* node, const onnxruntime::GraphViewer& graph_viewer) {
const auto& optype = node->OpType();
const auto& initializers = graph_viewer.GetAllInitializedTensors();
if (optype == "Reshape") {
//nGraph Reshape op currently requires shape info available in advance.
const auto& shape_arg = node->InputDefs()[1];
return initializers.find(shape_arg->Name()) == initializers.end();
} else if (optype == "MaxPool") {
//MaxPool "indices" output is not currently supported.
if (node->OutputDefs().size() > 1) {
return true;
}
// ceil_mode and dilations attrs are not supported in nGraph
const auto& attributes = node->GetAttributes();
if (attributes.find("ceil_mode") != attributes.end()) {
return true;
} else if (attributes.find("dilations") != attributes.end()) {
return true;
}
} else if (optype == "OneHot") {
//nGraph OneHot op currently requires depth info available in advance.
const auto& depth_arg = node->InputDefs()[1];
return initializers.find(depth_arg->Name()) == initializers.end();
} else if (optype == "TopK") {
//TopK opset 10 is currently not supported.
//K as input is currently not suppported.
return node->InputDefs().size() > 1;
} else if (optype == "LSTM") {
const auto& attributes = node->GetAttributes();
const auto direction_attr = attributes.find("direction");
if (direction_attr != attributes.end()) {
return direction_attr->second.s() == "reverse";
}
} else if (optype == "MatMul") {
//All matmuls except float have computation missmatch
const bool A_is_float = node->InputDefs()[0]->Type()->find("float") != std::string::npos;
const bool B_is_float = node->InputDefs()[1]->Type()->find("float") != std::string::npos;
return (A_is_float && B_is_float) ? false : true;
} else if (optype == "Pad") {
//3D pad with negative padding have computation missmatch
const auto& attributes = node->GetAttributes();
const auto pad_attr = attributes.find("pads");
if (pad_attr != attributes.end() && (pad_attr->second.ints().size() > 4 || pad_attr->second.ints().size() == 3)) {
for (const auto& val : pad_attr->second.ints()) {
if (val < 0)
return true;
}
}
} else if (optype == "Cast") {
//support of casting to bool in nGraph is in progress
const auto& attributes = node->GetAttributes();
const auto to_attr = attributes.find("to");
return to_attr->second.i() == ONNX_NAMESPACE::TensorProto::BOOL;
} else if (optype == "Slice") {
//Slice in opset 10 is currently not supported.
//unsupported inputs: starts, ends, axes, steps
if (node->InputDefs().size() > 1) {
return true;
}
//nGraph does not properly handle the situation where any value of the "starts" attribute
//is higher than a corresponding value in the "ends"
const auto& attributes = node->GetAttributes();
if (attributes.count("starts") == 0 || attributes.count("ends") == 0) {
return true;
}
const auto& starts = attributes.find("starts")->second.ints();
const auto& ends = attributes.find("ends")->second.ints();
for (int i = 0; i < starts.size(); ++i) {
if (starts.Get(i) > ends.Get(i)) {
return true;
}
}
} else if (optype == "AveragePool") {
// ceil_mode attribute is not supported in nGraph
const auto& attributes = node->GetAttributes();
if (attributes.find("ceil_mode") != attributes.end()) {
return true;
}
}
//Op doesn't fall into known any of unsupported modes.
return false;
}
static bool IsTypeSupported(const NodeArg* node_arg) {
const auto* type_proto = node_arg->TypeAsProto();
if (!type_proto) {
return false;
}
switch (type_proto->tensor_type().elem_type()) {
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_BOOL:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_FLOAT:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_DOUBLE:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_INT8:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_INT16:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_INT32:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_INT64:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_UINT8:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_UINT16:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_UINT32:
case ONNX_NAMESPACE::TensorProto_DataType::TensorProto_DataType_UINT64:
return true;
default:
return false;
}
}
static bool IsNodeSupported(const std::map<std::string, std::set<std::string>>& op_map,
const onnxruntime::GraphViewer& graph_viewer,
const NodeIndex node_idx) {
const auto& node = graph_viewer.GetNode(node_idx);
const auto& optype = node->OpType();
const auto& domain = node->Domain();
/*
1. Check input and output data types are supported.
2. Check Op is supported
2a. Check if Op is of known unsupported modes (edge cases). If yes return false right away.
2b. If above is not true, check if the op is available in nGraph.
*/
//Check 1
bool are_types_supported = true;
node->ForEachDef([&are_types_supported](const onnxruntime::NodeArg& node_arg, bool /*is_input*/) {
are_types_supported &= IsTypeSupported(&node_arg);
});
if (!are_types_supported) {
return false;
}
//Check 2a
if (domain == kOnnxDomain && IsUnsupportedOpMode(node, graph_viewer)) {
return false;
}
//Check 2b
const auto opset = op_map.find(domain);
if (opset == op_map.end() || opset->second.find(optype) == opset->second.end()) {
return false;
} else {
return true;
}
}
static void AppendClusterToSubGraph(const std::vector<NodeIndex>& nodes,
const onnxruntime::GraphViewer& graph_viewer,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::unordered_set<std::string>& ng_required_initializers,
std::vector<std::unique_ptr<ComputeCapability>>& result) {
static size_t op_counter = 0;
// Create ng_required_initializers attribute of NGraphCustomOp
ONNX_NAMESPACE::AttributeProto initializers;
initializers.set_name("initializers");
initializers.set_type(ONNX_NAMESPACE::AttributeProto_AttributeType::AttributeProto_AttributeType_TENSORS);
for (const auto& init : ng_required_initializers) {
auto tensor = initializers.add_tensors();
*tensor = *(graph_viewer.GetAllInitializedTensors().at(init));
}
auto meta_def = std::make_unique<IndexedSubGraph::MetaDef>();
meta_def->attributes["initializers"] = initializers;
meta_def->name = "NGRAPHCustomOp_" + std::to_string(++op_counter);
meta_def->domain = kNGraphDomain;
meta_def->since_version = 1;
meta_def->status = ONNX_NAMESPACE::EXPERIMENTAL;
meta_def->inputs = inputs;
meta_def->outputs = outputs;
std::unique_ptr<IndexedSubGraph> sub_graph = std::make_unique<IndexedSubGraph>();
sub_graph->nodes = nodes;
sub_graph->SetMetaDef(meta_def);
result.push_back(std::make_unique<ComputeCapability>(std::move(sub_graph)));
}
static int GetOnnxOpSet(const GraphViewer& graph_viewer) {
const auto& dm_to_ver = graph_viewer.DomainToVersionMap();
return dm_to_ver.at(kOnnxDomain);
}
static std::map<std::string, std::set<std::string>> GetNgSupportedOps(const int onnx_opset) {
std::map<std::string, std::set<std::string>> ng_supported_ops;
ng_supported_ops.emplace(kOnnxDomain, ngraph::onnx_import::get_supported_operators(onnx_opset, kOnnxDomain));
const std::set<std::string> ng_disabled_ops = {"DequantizeLinear", "QLinearConv", "QuantizeLinear"}; //Place-holder for ops not supported.
for (const auto& disabled_op : ng_disabled_ops) {
ng_supported_ops.at(kOnnxDomain).erase(disabled_op);
}
return ng_supported_ops;
}
static std::vector<NodeIndex> GetUnsupportedNodeIndices(const GraphViewer& graph_viewer, /*out*/ std::unordered_set<std::string>& ng_required_initializers) {
const auto ng_supported_ops = GetNgSupportedOps(GetOnnxOpSet(graph_viewer));
std::vector<NodeIndex> unsupported_nodes_idx;
for (const auto& node_idx : graph_viewer.GetNodesInTopologicalOrder()) {
if (IsNodeSupported(ng_supported_ops, graph_viewer, node_idx)) {
// Collect inputs that are initializers
graph_viewer.GetNode(node_idx)->ForEachDef([&ng_required_initializers, &graph_viewer](const onnxruntime::NodeArg& node_arg, bool is_input) {
if(is_input && graph_viewer.GetAllInitializedTensors().count(node_arg.Name())) {
ng_required_initializers.insert(node_arg.Name());
} }, true);
} else {
unsupported_nodes_idx.push_back(node_idx);
}
}
return unsupported_nodes_idx;
}
/* Returns a vector clusters(or node_idx). For each unsupported node, the graph is split into 3 parts.
supported_cluster + (UNsupported_node + rest_of_the_graph). This functions returns vector of all supported_clusters by nGraph
*/
static std::vector<std::vector<NodeIndex>> GetPartitionedClusters(const std::vector<NodeIndex>& topological_order, const std::vector<NodeIndex>& unsupported_nodes) {
std::vector<std::vector<NodeIndex>> ng_clusters;
auto prev = topological_order.begin();
for (const auto& unsup_node : unsupported_nodes) {
auto it = std::find(prev, topological_order.end(), unsup_node);
// Create a cluster vector[supported_node_idx, unsupported_node_idx) and append it to return list.
std::vector<NodeIndex> this_cluster{prev, it};
if (!this_cluster.empty()) {
ng_clusters.push_back(std::move(this_cluster));
}
// Point prev to node idx past this unsuported node.
prev = ++it;
}
//Tail
std::vector<NodeIndex> this_cluster{prev, topological_order.end()};
if (!this_cluster.empty()) {
ng_clusters.push_back(std::move(this_cluster));
}
return ng_clusters;
}
static void GetInputsOutputsOfCluster(const GraphViewer& graph_viewer,
const std::vector<NodeIndex>& cluster,
const std::unordered_set<std::string>& ng_required_initializers,
/*out*/ std::vector<std::string>& cluster_inputs,
/*out*/ std::vector<std::string>& cluster_outputs) {
std::unordered_set<std::string> input_args;
std::unordered_set<std::string> output_args;
std::unordered_set<std::string> external_output_args;
for (const auto& node_idx : cluster) {
const auto& node = graph_viewer.GetNode(node_idx);
// Collect all inputs and outputs
node->ForEachDef(
[&input_args, &output_args](const NodeArg& node_arg, bool is_input) {
is_input ? input_args.insert(node_arg.Name()) : output_args.insert(node_arg.Name());
},
true);
// Check if output of this node is used by nodes outside this_cluster. If yes add this to cluster outputs
for (auto it = node->OutputNodesBegin(); it != node->OutputNodesEnd(); ++it) {
const auto& ext_node = graph_viewer.GetNode((*it).Index());
if (std::find(cluster.begin(), cluster.end(), ext_node->Index()) == cluster.end()) {
// Node is external to this_cluster. Search through its inputs to find the output that is generated by this_cluster.
std::set<std::string> ext_node_inputs;
ext_node->ForEachDef(
[&ext_node_inputs](const onnxruntime::NodeArg& arg, bool is_input) {
if (is_input) {
ext_node_inputs.insert(arg.Name());
}
},
true);
for (const auto& out_def : node->OutputDefs()) {
if (ext_node_inputs.find(out_def->Name()) != ext_node_inputs.end()) {
external_output_args.insert(out_def->Name());
}
}
}
}
}
std::vector<std::string> cluster_initializers;
//Extract initializers used by this_cluster.
std::unordered_set<std::string> original_graph_inputs;
for (const auto& node_arg : graph_viewer.GetInputsIncludingInitializers()) {
original_graph_inputs.insert(node_arg->Name());
}
const auto& initializers = graph_viewer.GetAllInitializedTensors();
for (const auto& in_arg : input_args) {
if ((initializers.count(in_arg) && !original_graph_inputs.count(in_arg)) ||
ng_required_initializers.count(in_arg)) {
cluster_initializers.push_back(in_arg);
} else if (!output_args.count(in_arg)) {
cluster_inputs.push_back(in_arg);
}
}
std::copy(external_output_args.begin(), external_output_args.end(), std::back_inserter(cluster_outputs));
for (const auto& node_arg : graph_viewer.GetOutputs()) {
const auto& name = node_arg->Name();
if (output_args.count(name) && !external_output_args.count(name)) {
cluster_outputs.push_back(name);
}
}
}
std::vector<std::unique_ptr<ComputeCapability>>
NGRAPHExecutionProvider::GetCapability(const onnxruntime::GraphViewer& graph_viewer,
const std::vector<const KernelRegistry*>& kernel_registries) const {
ORT_UNUSED_PARAMETER(kernel_registries);
std::vector<std::unique_ptr<ComputeCapability>> result;
//TODO:(nivas) Handle If and Loop operators
if (graph_viewer.IsSubgraph()) {
return result;
}
// Need access to model_path_
for (const auto& tensor : graph_viewer.GetAllInitializedTensors()) {
if (tensor.second->has_data_location() && tensor.second->data_location() == ONNX_NAMESPACE::TensorProto_DataLocation_EXTERNAL) {
LOGS_DEFAULT(WARNING) << "nGraph EP: Initializers with external data location are not currently supported";
return result;
}
}
/* This is a list of initializers that nGraph considers as constants. Example weights, reshape shape etc.
TODO: Support overridable initializers */
std::unordered_set<std::string> ng_required_initializers;
const auto unsupported_nodes = GetUnsupportedNodeIndices(graph_viewer, ng_required_initializers);
//If all ops are supported, no partitioning is required. Short-circuit and avoid splitting.
if (unsupported_nodes.empty()) {
std::vector<std::string> inputs;
std::vector<std::string> outputs;
//Fill inputs with names
std::for_each(graph_viewer.GetInputs().begin(), graph_viewer.GetInputs().end(),
[&inputs](const NodeArg* node_arg) { inputs.push_back(node_arg->Name()); });
/* In scenarios, when there are no inputs or all inputs being initializers,
ConstantFolding optimization in onnxruntime pre-computes the value.*/
if (inputs.empty()) {
return result;
}
//Fill outputs with names
std::for_each(graph_viewer.GetOutputs().begin(), graph_viewer.GetOutputs().end(),
[&outputs](const NodeArg* node_arg) { outputs.push_back(node_arg->Name()); });
// Remove initializers from inputs if they are in ng_required_initializers
inputs.erase(std::remove_if(inputs.begin(), inputs.end(), [&ng_required_initializers](const std::string& name) -> bool {
return ng_required_initializers.count(name);
}),
inputs.end());
// Create and add this graph to result.
AppendClusterToSubGraph(graph_viewer.GetNodesInTopologicalOrder(), graph_viewer, inputs, outputs, ng_required_initializers, result);
} else { // unsupported_nodes_idx.empty()
const auto ng_clusters = GetPartitionedClusters(graph_viewer.GetNodesInTopologicalOrder(), unsupported_nodes);
for (const auto& this_cluster : ng_clusters) {
std::vector<std::string> cluster_inputs, cluster_outputs;
GetInputsOutputsOfCluster(graph_viewer, this_cluster, ng_required_initializers, cluster_inputs, cluster_outputs);
if (!cluster_inputs.empty()) {
AppendClusterToSubGraph(this_cluster, graph_viewer, cluster_inputs, cluster_outputs, ng_required_initializers, result);
}
}
}
return result;
}
static ONNX_NAMESPACE::ModelProto GetModelProtoFromFusedNode(const onnxruntime::Node* fused_node) {
const auto& attributes = fused_node->GetAttributes();
const auto& initializers = attributes.at("initializers").tensors();
ONNX_NAMESPACE::ModelProto model_proto;
auto graph_proto = model_proto.mutable_graph();
const auto& fused_graph = fused_node->GetFunctionBody()->Body();
for (const auto& node : fused_graph.Nodes()) {
node.ToProto(*(graph_proto->add_node()));
}
for (const auto& input : fused_node->InputDefs()) {
auto valueInfoProto = graph_proto->add_input();
*valueInfoProto = input->ToProto();
}
for (const auto& output : fused_node->OutputDefs()) {
auto valueInfoProto = graph_proto->add_output();
*valueInfoProto = output->ToProto();
}
for (const auto& initializer : initializers) {
graph_proto->add_initializer()->CopyFrom(initializer);
}
auto opset = model_proto.add_opset_import();
opset->set_domain(kOnnxDomain);
opset->set_version(fused_graph.DomainToVersionMap().at(kOnnxDomain));
model_proto.set_ir_version(ONNX_NAMESPACE::Version::IR_VERSION);
return model_proto;
}
Status NGRAPHExecutionProvider::Compile(const std::vector<onnxruntime::Node*>& fused_nodes,
std::vector<NodeComputeInfo>& node_compute_funcs) {
for (const auto& fused_node : fused_nodes) {
auto model_proto = GetModelProtoFromFusedNode(fused_node);
NodeComputeInfo compute_info;
// Local copy of backend since, class members cannot be captured.
auto ngraph_backend = ng_backend_;
compute_info.create_state_func = [model_proto, ngraph_backend](ComputeContext* context, FunctionState* state) {
auto* p = new onnxruntime::ngraph_ep::NGRAPHCustomOp(context, model_proto, ngraph_backend);
*state = p;
return 0;
};
compute_info.release_state_func = [](FunctionState state) {
if (state)
delete reinterpret_cast<onnxruntime::ngraph_ep::NGRAPHCustomOp*>(state);
};
compute_info.compute_func = [](FunctionState state, ONNXRunTimeTensor* input_tensors, size_t num_inputs, ONNXRunTimeTensor* output_tensors, size_t num_outputs) {
onnxruntime::ngraph_ep::NGRAPHCustomOp* ng_custom_op = reinterpret_cast<onnxruntime::ngraph_ep::NGRAPHCustomOp*>(state);
const Status compute_status = ng_custom_op->Compute(input_tensors, num_inputs, output_tensors, num_outputs);
return compute_status == Status::OK() ? 0 : 1;
};
node_compute_funcs.push_back(compute_info);
}
return Status::OK();
}
} // namespace onnxruntime

View file

@ -0,0 +1,44 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#pragma once
#include "core/framework/execution_provider.h"
namespace ngraph {
namespace runtime {
class Backend;
}
} // namespace ngraph
namespace onnxruntime {
// Information needed to construct nGraph execution providers.
struct NGRAPHExecutionProviderInfo {
const std::string ng_backend_type;
};
// Logical device representation.
class NGRAPHExecutionProvider : public IExecutionProvider {
public:
explicit NGRAPHExecutionProvider(const NGRAPHExecutionProviderInfo& info);
~NGRAPHExecutionProvider() = default;
Status CopyTensor(const Tensor& src, Tensor& dst) const override;
std::vector<std::unique_ptr<ComputeCapability>>
GetCapability(const onnxruntime::GraphViewer& graph_viewer,
const std::vector<const KernelRegistry*>& kernel_registries) const override;
Status Compile(const std::vector<onnxruntime::Node*>& fused_nodes,
std::vector<NodeComputeInfo>& node_compute_funcs) override;
const void* GetExecutionHandle() const noexcept override { return nullptr; }
std::shared_ptr<KernelRegistry> GetKernelRegistry() const override;
private:
std::shared_ptr<ngraph::runtime::Backend> ng_backend_;
};
} // namespace onnxruntime

View file

@ -0,0 +1,12 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#pragma once
namespace onnxruntime {
namespace ngraph_ep {
template <typename T>
KernelCreateInfo BuildKernel();
}
} // namespace onnxruntime

View file

@ -0,0 +1,34 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#include "core/providers/ngraph/ngraph_provider_factory.h"
#include <atomic>
#include "ngraph_execution_provider.h"
#include "core/session/abi_session_options_impl.h"
using namespace onnxruntime;
namespace onnxruntime {
struct NGraphProviderFactory : IExecutionProviderFactory {
NGraphProviderFactory(std::string&& ng_backend_type) : ng_backend_type_(std::move(ng_backend_type)) {}
~NGraphProviderFactory() = default;
std::unique_ptr<IExecutionProvider> CreateProvider() override {
NGRAPHExecutionProviderInfo info{ng_backend_type_};
return std::make_unique<NGRAPHExecutionProvider>(info);
}
private:
const std::string ng_backend_type_;
};
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_NGraph(const char* ng_backend_type) {
return std::make_shared<onnxruntime::NGraphProviderFactory>(std::string{ng_backend_type});
}
} // namespace onnxruntime
ORT_API_STATUS_IMPL(OrtSessionOptionsAppendExecutionProvider_NGraph, _In_ OrtSessionOptions* options, _In_ const char* ng_backend_type) {
options->provider_factories.push_back(onnxruntime::CreateExecutionProviderFactory_NGraph(ng_backend_type));
return nullptr;
}

View file

@ -0,0 +1 @@
OrtSessionOptionsAppendExecutionProvider_NGraph

View file

@ -34,13 +34,20 @@
#define BACKEND_MKLML ""
#endif
#if USE_NGRAPH
#define BACKEND_NGRAPH "-NGRAPH"
#include "core/providers/ngraph/ngraph_execution_provider.h"
#else
#define BACKEND_NGRAPH ""
#endif
#if USE_OPENBLAS
#define BACKEND_OPENBLAS "-OPENBLAS"
#else
#define BACKEND_OPENBLAS ""
#endif
#define BACKEND_DEVICE BACKEND_PROC BACKEND_MKLDNN BACKEND_MKLML BACKEND_OPENBLAS
#define BACKEND_DEVICE BACKEND_PROC BACKEND_MKLDNN BACKEND_MKLML BACKEND_NGRAPH BACKEND_OPENBLAS
#include "core/session/onnxruntime_cxx_api.h"
#include "core/providers/providers.h"
#include "core/providers/cpu/cpu_execution_provider.h"
@ -55,6 +62,9 @@
#ifdef USE_MKLDNN
#include "core/providers/mkldnn/mkldnn_provider_factory.h"
#endif
#ifdef USE_NGRAPH
#include "core/providers/ngraph/ngraph_provider_factory.h"
#endif
#ifdef USE_NUPHAR
#include "core/providers/nuphar/nuphar_provider_factory.h"
#endif
@ -67,6 +77,7 @@ std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_CPU(in
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_CUDA(int device_id);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Tensorrt();
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Mkldnn(int use_arena);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_NGraph(const char* ng_backend_type);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Nuphar(int device_id, const char*);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_BrainSlice(uint32_t ip, int, int, bool, const char*, const char*, const char*);
} // namespace onnxruntime
@ -218,6 +229,11 @@ void InitializeSession(InferenceSession* sess) {
RegisterExecutionProvider(sess, *onnxruntime::CreateExecutionProviderFactory_Mkldnn(enable_cpu_mem_arena ? 1 : 0));
}
#endif
#if USE_NGRAPH
{
RegisterExecutionProvider(sess, *onnxruntime::CreateExecutionProviderFactory_NGraph("CPU"));
}
#endif
#if 0 //USE_NUPHAR
{

View file

@ -32,7 +32,7 @@ void usage() {
"\t-r [repeat]: Specifies the number of times to repeat\n"
"\t-v: verbose\n"
"\t-n [test_case_name]: Specifies a single test case to run.\n"
"\t-e [EXECUTION_PROVIDER]: EXECUTION_PROVIDER could be 'cpu', 'cuda', 'mkldnn' or 'tensorrt'. Default: 'cpu'.\n"
"\t-e [EXECUTION_PROVIDER]: EXECUTION_PROVIDER could be 'cpu', 'cuda', 'mkldnn', 'tensorrt' or 'ngraph'. Default: 'cpu'.\n"
"\t-x: Use parallel executor, default (without -x): sequential executor.\n"
"\t-h: help\n");
}
@ -80,6 +80,7 @@ int real_main(int argc, char* argv[], OrtEnv** p_env) {
int p_models = GetNumCpuCores();
bool enable_cuda = false;
bool enable_mkl = false;
bool enable_ngraph = false;
bool enable_nuphar = false;
bool enable_tensorrt = false;
OrtLoggingLevel logging_level = ORT_LOGGING_LEVEL_WARNING;
@ -129,6 +130,8 @@ int real_main(int argc, char* argv[], OrtEnv** p_env) {
enable_cuda = true;
} else if (!CompareCString(optarg, ORT_TSTR("mkldnn"))) {
enable_mkl = true;
} else if (!CompareCString(optarg, ORT_TSTR("ngraph"))) {
enable_ngraph = true;
} else if (!CompareCString(optarg, ORT_TSTR("nuphar"))) {
enable_nuphar = true;
} else if (!CompareCString(optarg, ORT_TSTR("tensorrt"))) {
@ -223,6 +226,14 @@ int real_main(int argc, char* argv[], OrtEnv** p_env) {
return -1;
#endif
}
if (enable_ngraph) { //TODO: Re-order the priority?
#ifdef USE_NGRAPH
ORT_THROW_ON_ERROR(OrtSessionOptionsAppendExecutionProvider_NGraph(sf, "CPU"));
#else
fprintf(stderr, "nGraph is not supported in this build");
return -1;
#endif
}
std::unordered_set<std::string> cuda_flaky_tests = {
"fp16_inception_v1", "fp16_shufflenet", "fp16_tiny_yolov2"};

View file

@ -28,9 +28,8 @@ namespace perftest {
"Options:\n"
"\t-m [test_mode]: Specifies the test mode. Value coulde be 'duration' or 'times'.\n"
"\t\tProvide 'duration' to run the test for a fix duration, and 'times' to repeated for a certain times. "
"Default:'duration'.\n"
"\t-c [parallel runs]: Specifies the (max) number of runs to invoke simultaneously. Default:1.\n"
"\t-e [cpu|cuda|mkldnn|tensorrt]: Specifies the provider 'cpu','cuda','mkldnn' or 'tensorrt'. Default:'cpu'.\n"
"\t-e [cpu|cuda|mkldnn|tensorrt|ngraph]: Specifies the provider 'cpu','cuda','mkldnn','tensorrt' or 'ngraph'. Default:'cpu'.\n"
"\t-b [tf|ort]: backend to use. Default:ort\n"
"\t-r [repeated_times]: Specifies the repeated times if running in 'times' test mode.Default:1000.\n"
"\t-t [seconds_to_run]: Specifies the seconds to run for 'duration' mode. Default:600.\n"
@ -68,6 +67,8 @@ namespace perftest {
test_config.machine_config.provider_type_name = onnxruntime::kCudaExecutionProvider;
} else if (!CompareCString(optarg, ORT_TSTR("mkldnn"))) {
test_config.machine_config.provider_type_name = onnxruntime::kMklDnnExecutionProvider;
} else if (!CompareCString(optarg, ORT_TSTR("ngraph"))) {
test_config.machine_config.provider_type_name = onnxruntime::kNGraphExecutionProvider;
} else if (!CompareCString(optarg, ORT_TSTR("brainslice"))) {
test_config.machine_config.provider_type_name = onnxruntime::kBrainSliceExecutionProvider;
} else if (!CompareCString(optarg, ORT_TSTR("tensorrt"))) {

View file

@ -36,6 +36,12 @@ OnnxRuntimeTestSession::OnnxRuntimeTestSession(OrtEnv* env, const PerformanceTes
ORT_THROW_ON_ERROR(OrtSessionOptionsAppendExecutionProvider_Mkldnn(sf, enable_cpu_mem_arena ? 1 : 0));
#else
ORT_THROW("MKL-DNN is not supported in this build\n");
#endif
} else if (provider_name == onnxruntime::kNGraphExecutionProvider) {
#ifdef USE_NGRAPH
ORT_THROW_ON_ERROR(OrtSessionOptionsAppendExecutionProvider_NGraph(sf, "CPU"));
#else
ORT_THROW("nGraph is not supported in this build");
#endif
} else if (provider_name == onnxruntime::kCudaExecutionProvider) {
#ifdef USE_CUDA

View file

@ -0,0 +1,358 @@
// Copyright(C) 2019 Intel Corporation
// Licensed under the MIT License
#include <random>
#include "core/providers/ngraph/ngraph_execution_provider.h"
#include "test/providers/provider_test_utils.h"
#include "default_providers.h"
#include "gtest/gtest.h"
#include "core/session/inference_session.h"
#include "test/framework/test_utils.h"
#include "test/test_environment.h"
#include "core/util/math.h"
using namespace ONNX_NAMESPACE;
namespace onnxruntime {
namespace test {
//This is a dummy op that just increments the tensor values by one.
class UnSupportedOp final : public OpKernel {
public:
UnSupportedOp(const OpKernelInfo& info) : OpKernel(info) {}
Status Compute(OpKernelContext* p_context) const {
const auto* X = p_context->Input<Tensor>(0);
ORT_ENFORCE(X);
auto X_Data = X->Data<float>();
auto& shape = X->Shape().GetDims();
auto* Y = p_context->Output(0, shape);
auto* Y_Data = Y->MutableData<float>();
size_t size = 1;
for (size_t i = 0; i < shape.size(); i++) {
size *= shape[i];
}
for (size_t i = 0; i < size; i++) {
Y_Data[i] = X_Data[i] + 1;
}
return Status::OK();
}
};
KernelDefBuilder UnSupportedOpDef() {
KernelDefBuilder def;
def.SetName("UnSupportedOp")
.SetDomain(onnxruntime::kOnnxDomain)
.SinceVersion(7)
.Provider(onnxruntime::kCpuExecutionProvider)
.TypeConstraint("T", DataTypeImpl::GetTensorType<float>());
return def;
}
ONNX_NAMESPACE::OpSchema GetUnSupportedOpSchema() {
ONNX_NAMESPACE::OpSchema schema("UnSupportedOp", "unknown", 0);
schema.Input(0, "A", "Data.", "T");
schema.Output(0, "C", "Result, has same dimensions and type as A", "T");
schema.TypeConstraint("T", {"tensor(float)"}, "Constrain input and output types to float tensors.");
schema.SinceVersion(7);
return schema;
}
void add_feeds(NameMLValMap& feeds, std::string name, std::vector<int64_t> dims, std::vector<float> value) {
MLValue ml_value;
CreateMLValue<float>(TestCPUExecutionProvider()->GetAllocator(0, OrtMemTypeDefault), dims, value, &ml_value);
feeds.insert(std::make_pair(name, ml_value));
}
//TODO:(nivas) Refractor to use existing code
void RunTest(const std::string& model_path, const NameMLValMap& feeds, const std::vector<std::string>& output_names, const std::vector<std::vector<int64_t>>& expected_shapes, const std::vector<std::vector<float>>& expected_values) {
SessionOptions so;
InferenceSession session_object(so, &DefaultLoggingManager());
EXPECT_TRUE(session_object.RegisterExecutionProvider(DefaultNGraphExecutionProvider()).IsOK());
std::shared_ptr<CustomRegistry> registry = std::make_shared<CustomRegistry>();
EXPECT_TRUE(session_object.RegisterCustomRegistry(registry).IsOK());
auto unsupported_schema = GetUnSupportedOpSchema();
std::vector<OpSchema> schemas = {unsupported_schema};
EXPECT_TRUE(registry->RegisterOpSet(schemas, onnxruntime::kOnnxDomain, 7, 8).IsOK());
auto def = UnSupportedOpDef();
KernelCreateFn kernel_create_fn = [](const OpKernelInfo& info) -> OpKernel* { return new UnSupportedOp(info); };
EXPECT_TRUE(registry->RegisterCustomKernel(def, kernel_create_fn).IsOK());
auto status = session_object.Load(model_path);
EXPECT_TRUE(status.IsOK()) << status.ErrorMessage();
if (!status.IsOK()) {
LOGS_DEFAULT(ERROR) << "Load failed with status: " << status.ErrorMessage();
return;
}
status = session_object.Initialize();
//TODO : Count number of nodes to ensure fusion and also verify that, fused node is assigned to nGraph_EP.
// Currently there is no api to get above info from here.
EXPECT_TRUE(status.IsOK()) << status.ErrorMessage();
RunOptions run_options{};
run_options.run_tag = "nGraph EP test tag";
run_options.run_log_verbosity_level = 1;
std::vector<MLValue> fetches;
status = session_object.Run(run_options, feeds, output_names, &fetches);
if (!status.IsOK()) {
LOGS_DEFAULT(ERROR) << "Run failed with status: " << status.ErrorMessage();
return;
}
for (size_t idx = 0; idx < expected_values.size(); ++idx) {
auto& got_tensor = fetches[idx].Get<Tensor>();
auto* got = got_tensor.Data<float>();
auto& expected = expected_values[idx];
TensorShape expected_shape(expected_shapes[idx]);
ASSERT_EQ(got_tensor.Shape(), expected_shape);
for (size_t i = 0; i < expected.size(); i++) {
ASSERT_EQ(got[i], expected[i]);
}
}
}
/*
Basic test: To ensure fusion. TODO: Count number of nodes after model initializaton.
(A) (A)
\ /
Add
(B) |
\ |
Mul
|
|
(Z)
*/
TEST(NGraphExecutionProviderTest, Basic_Test) {
NameMLValMap feeds;
add_feeds(feeds, "A", {4}, {1.0f, 2.0f, 3.0f, 4.0f});
add_feeds(feeds, "B", {4}, {2.0f, 2.0f, 2.0f, 2.0f});
std::vector<std::vector<float>> expected_values = {
{4.0f, 8.0f, 12.0f, 16.0f}};
std::vector<std::vector<int64_t>> expected_shapes = {
{4}};
RunTest("testdata/ngraph/Basic_Test.onnx", feeds, {"Z"}, expected_shapes, expected_values);
}
/*
(A) (A)
\ /
Add
(B) |
\ |
Mul
|
|
UnSupportedOp
|
|
(Z)
Simple test-case for a graph with UnSupportedOp
*/
TEST(NGraphExecutionProviderTest, Graph_with_UnSupportedOp) {
NameMLValMap feeds;
add_feeds(feeds, "A", {4}, {1.0f, 2.0f, 3.0f, 4.0f});
add_feeds(feeds, "B", {4}, {2.0f, 2.0f, 2.0f, 2.0f});
std::vector<std::vector<float>> expected_values = {
{5.0f, 9.0f, 13.0f, 17.0f}};
std::vector<std::vector<int64_t>> expected_shapes = {
{4}};
RunTest("testdata/ngraph/Graph_with_UnSupportedOp.onnx", feeds, {"Z"}, expected_shapes, expected_values);
}
/*
(A) (A)
\ /
Add
(B) |
\ |
Mul
|
|
UnSupportedOp
| (C)
| /
Add
|
|
(Z)
Same as above, here are we have two sub-graphs that are run by nGraph execution provider
*/
TEST(NGraphExecutionProviderTest, Two_Subgraphs) {
NameMLValMap feeds;
add_feeds(feeds, "A", {4}, {1.0f, 2.0f, 3.0f, 4.0f});
add_feeds(feeds, "B", {4}, {2.0f, 2.0f, 2.0f, 2.0f});
add_feeds(feeds, "C", {4}, {1.0f, 1.0f, 1.0f, 1.0f});
std::vector<std::vector<float>> expected_values = {
{6.0f, 10.0f, 14.0f, 18.0f}};
std::vector<std::vector<int64_t>> expected_shapes = {
{4}};
RunTest("testdata/ngraph/Two_Subgraphs.onnx", feeds, {"Z"}, expected_shapes, expected_values);
}
/*
(A) (A)
\ /
Add
(B) |
\ |
Mul ----------------\
| |
| |
UnSupportedOp |
| (C) |
| / |
Add |
| |
| |
(Z) (Y)
Output of the sub-graph is also graph output
*/
TEST(NGraphExecutionProviderTest, ClusterOut_isAlso_GraphOut) {
NameMLValMap feeds;
add_feeds(feeds, "A", {4}, {1.0f, 2.0f, 3.0f, 4.0f});
add_feeds(feeds, "B", {4}, {2.0f, 2.0f, 2.0f, 2.0f});
add_feeds(feeds, "C", {4}, {1.0f, 1.0f, 1.0f, 1.0f});
std::vector<std::vector<float>> expected_values = {
{2.0f, 4.0f, 6.0f, 8.0f},
{6.0f, 10.0f, 14.0f, 18.0f}};
std::vector<std::vector<int64_t>> expected_shapes = {
{4},
{4}};
RunTest("testdata/ngraph/ClusterOut_isAlso_GraphOut.onnx", feeds, {"Y", "Z"}, expected_shapes, expected_values);
}
/*
(A) (A)
\ /
Add
(B) | \
\ | \
Mul \
| \
| \
UnSupportedOp\
(C) | \
\ | \
Add \
| \
| \
[Z] [Y]
Sub-graph in-out is also graph output
*/
TEST(NGraphExecutionProviderTest, InOut_isAlso_GraphOut) {
NameMLValMap feeds;
add_feeds(feeds, "A", {4}, {1.0f, 2.0f, 3.0f, 4.0f});
add_feeds(feeds, "B", {4}, {2.0f, 2.0f, 2.0f, 2.0f});
add_feeds(feeds, "C", {4}, {1.0f, 1.0f, 1.0f, 1.0f});
std::vector<std::vector<float>> expected_values = {
{4.0f, 8.0f, 12.0f, 16.0f},
{6.0f, 10.0f, 14.0f, 18.0f}};
std::vector<std::vector<int64_t>> expected_shapes = {
{4},
{4}};
RunTest("testdata/ngraph/InOut_isAlso_GraphOut.onnx", feeds, {"Y", "Z"}, expected_shapes, expected_values);
}
/*
(A) (A)
\ /
Add
|
|
Dropout
| \ Mask (Unused output)
|
UnSupportedOp
(C) |
\ |
Add
|
|
[Z]
Test-case to esnure ep working in case of unused or optional input that is NOT graph output.
*/
TEST(NGraphExecutionProviderTest, Op_with_Optional_or_Unused_Outputs) {
NameMLValMap feeds;
add_feeds(feeds, "A", {4}, {1.0f, 2.0f, 3.0f, 4.0f});
add_feeds(feeds, "C", {4}, {1.0f, 1.0f, 1.0f, 1.0f});
std::vector<std::vector<float>> expected_values = {
{4.0f, 6.0f, 8.0f, 10.0f}};
std::vector<std::vector<int64_t>> expected_shapes = {
{4}};
RunTest("testdata/ngraph/Op_with_Optional_or_Unused_Outputs.onnx", feeds, {"Z"}, expected_shapes, expected_values);
}
/*
(A) (A)
\ /
Add(1)
_______|_______
| |
| Add(2)
| |
UnSupportedOp(4) |
| Add(3)
| |
|_____________|
|
|
Add(5)
|
|
[Z]
This test is to ensure, we do not have cyclic dependent sub-graphs.
Example: Sub-Graph-1{1,2,3,5} is invalid because the output of this cluster is input to UnSupportedOp whose output is again input to the same cluster.
*/
TEST(NGraphExecutionProviderTest, Independent_SubGraphs) {
NameMLValMap feeds;
add_feeds(feeds, "A", {4}, {1.0f, 2.0f, 3.0f, 4.0f});
std::vector<std::vector<float>> expected_values = {
{7.0f, 11.0f, 15.0f, 19.0f}};
std::vector<std::vector<int64_t>> expected_shapes = {
{4}};
RunTest("testdata/ngraph/Independent_SubGraphs.onnx", feeds, {"Z"}, expected_shapes, expected_values);
}
} // namespace test
} // namespace onnxruntime

View file

@ -315,7 +315,10 @@ void OpTester::ExecuteModel(Model& model,
}
} else {
if (expect_result == ExpectResult::kExpectFailure) {
EXPECT_THAT(status.ErrorMessage(), testing::HasSubstr(expected_failure_string));
// Disable expected_failure_string checks for MKL-DNN and nGraph EP's
if (provider_type != kMklDnnExecutionProvider && provider_type != kNGraphExecutionProvider) {
EXPECT_THAT(status.ErrorMessage(), testing::HasSubstr(expected_failure_string));
}
} else {
LOGS_DEFAULT(ERROR) << "Run failed with status: " << status.ErrorMessage();
EXPECT_TRUE(status.IsOK()) << status.ErrorMessage();
@ -411,6 +414,7 @@ void OpTester::Run(ExpectResult expect_result,
kCpuExecutionProvider,
kCudaExecutionProvider,
kMklDnnExecutionProvider,
kNGraphExecutionProvider,
kNupharExecutionProvider,
kBrainSliceExecutionProvider,
kTensorrtExecutionProvider,
@ -448,6 +452,8 @@ void OpTester::Run(ExpectResult expect_result,
execution_provider = DefaultCudaExecutionProvider();
else if (provider_type == onnxruntime::kMklDnnExecutionProvider)
execution_provider = DefaultMkldnnExecutionProvider();
else if (provider_type == onnxruntime::kNGraphExecutionProvider)
execution_provider = DefaultNGraphExecutionProvider();
else if (provider_type == onnxruntime::kNupharExecutionProvider)
execution_provider = DefaultNupharExecutionProvider();
else if (provider_type == onnxruntime::kBrainSliceExecutionProvider)
@ -470,10 +476,13 @@ void OpTester::Run(ExpectResult expect_result,
if (provider_type == onnxruntime::kTensorrtExecutionProvider)
continue;
auto reg = execution_provider->GetKernelRegistry();
const KernelCreateInfo* kci = reg->TryFindKernel(node, execution_provider->Type());
if (!kci) {
valid = false;
break;
// nGraph EP doesn't have kernels registered
if (!reg->IsEmpty()) {
const KernelCreateInfo* kci = reg->TryFindKernel(node, execution_provider->Type());
if (!kci) {
valid = false;
break;
}
}
}

View file

@ -0,0 +1,19 @@
nGraph EP test model:‰

A
A node1_outnode_1"Add

node1_out
BZnode_2"MulnGraph EP test graphZ
A

Z
B

b
Z

B

View file

@ -0,0 +1,32 @@
nGraph EP test model:ò

A
AYnode_1"Add

Y
B node2_outnode_2"Mul
-
node2_out node3_outnode_3" UnSupportedOp

node3_out
CZnode_4"AddnGraph EP test graphZ
A

Z
B

Z
C

b
Y

b
Z

B

View file

@ -0,0 +1,21 @@
nGraph EP test model:¸

A
A node1_outnode_1"Add
&
node1_out
B node2_outnode_2"Mul
%
node2_outZnode_3" UnSupportedOpnGraph EP test graphZ
A

Z
B

b
Z

B

View file

@ -0,0 +1,32 @@
nGraph EP test model:ò

A
A node1_outnode_1"Add

node1_out
BYnode_2"Mul
%
Y node3_outnode_3" UnSupportedOp

node3_out
CZnode_4"AddnGraph EP test graphZ
A

Z
B

Z
C

b
Y

b
Z

B

Binary file not shown.

View file

@ -0,0 +1,23 @@
nGraph EP test model:Ô

A
A node1_outnode_1"Add
"
node1_outYXnode_2"Dropout
%
Y node3_outnode_3" UnSupportedOp

node3_out
CZnode_4"AddnGraph EP test graphZ
A

Z
C

b
Z

B

View file

@ -0,0 +1,28 @@
nGraph EP test model:ñ

A
A node1_outnode_1"Add
&
node1_out
B node2_outnode_2"Mul
-
node2_out node3_outnode_3" UnSupportedOp

node3_out
CZnode_4"AddnGraph EP test graphZ
A

Z
B

Z
C

b
Z

B

View file

@ -0,0 +1,107 @@
import numpy as np
import onnx
from onnx import helper, numpy_helper
def save_graph(graph_def, name) :
model_def = helper.make_model(graph_def, producer_name="nGraph EP test model")
model_def.opset_import[0].version = 7
model_def.ir_version = 3
onnx.save_model(model_def, name)
A = helper.make_tensor_value_info('A', onnx.TensorProto.FLOAT, [4])
B = helper.make_tensor_value_info('B', onnx.TensorProto.FLOAT, [4])
C = helper.make_tensor_value_info('C', onnx.TensorProto.FLOAT, [4])
Y = helper.make_tensor_value_info('Y', onnx.TensorProto.FLOAT, [4])
Z = helper.make_tensor_value_info('Z', onnx.TensorProto.FLOAT, [4])
graph_def = helper.make_graph(
[helper.make_node('Add',['A','A'],['node1_out'], "node_1"),
helper.make_node('Mul',['node1_out','B'],['Z'], "node_2")],
"nGraph EP test graph",
[A,B],
[Z]
)
save_graph(graph_def, "Basic_Test.onnx")
graph_def = helper.make_graph(
[helper.make_node('Add',['A','A'],['node1_out'], "node_1"),
helper.make_node('Mul',['node1_out','B'],['node2_out'], "node_2"),
helper.make_node('UnSupportedOp',['node2_out'],['Z'], "node_3")],
"nGraph EP test graph",
[A,B],
[Z]
)
save_graph(graph_def, "Graph_with_UnSupportedOp.onnx")
graph_def = helper.make_graph(
[helper.make_node('Add',['A','A'],['node1_out'], "node_1"),
helper.make_node('Mul',['node1_out','B'],['node2_out'], "node_2"),
helper.make_node('UnSupportedOp',['node2_out'],['node3_out'], "node_3"),
helper.make_node('Add',['node3_out','C'],['Z'], "node_4")],
"nGraph EP test graph",
[A,B,C],
[Z]
)
save_graph(graph_def, "Two_Subgraphs.onnx")
graph_def = helper.make_graph(
[helper.make_node('Add',['A','A'],['Y'], "node_1"),
helper.make_node('Mul',['Y','B'],['node2_out'], "node_2"),
helper.make_node('UnSupportedOp',['node2_out'],['node3_out'], "node_3"),
helper.make_node('Add',['node3_out','C'],['Z'], "node_4")],
"nGraph EP test graph",
[A,B,C],
[Y,Z]
)
save_graph(graph_def, "ClusterOut_isAlso_GraphOut.onnx")
graph_def = helper.make_graph(
[helper.make_node('Add',['A','A'],['node1_out'], "node_1"),
helper.make_node('Mul',['node1_out','B'],['Y'], "node_2"),
helper.make_node('UnSupportedOp',['Y'],['node3_out'], "node_3"),
helper.make_node('Add',['node3_out','C'],['Z'], "node_4")],
"nGraph EP test graph",
[A,B,C],
[Y,Z]
)
save_graph(graph_def, "InOut_isAlso_GraphOut.onnx")
graph_def = helper.make_graph(
[helper.make_node('Add',['A','A'],['node1_out'], "node_1"),
helper.make_node('Dropout',['node1_out'],['Y','X'], "node_2"),
helper.make_node('UnSupportedOp',['Y'],['node3_out'], "node_3"),
helper.make_node('Add',['node3_out','C'],['Z'], "node_4")],
"nGraph EP test graph",
[A,C],
[Z]
)
save_graph(graph_def, "Op_with_Optional_or_Unused_Outputs.onnx")
one_in = helper.make_tensor_value_info('one', onnx.TensorProto.FLOAT, [1])
one_data = np.array([1],dtype=np.float32)
one_tensor = numpy_helper.from_array(one_data, "one")
graph_def = helper.make_graph(
[helper.make_node('Add',['A', "A"],['node1_out'], "node_1"),
helper.make_node('UnSupportedOp',['node1_out'],['branch_a_out'], "node_2"),
helper.make_node('Add',["node1_out","one"],["branch_b_out1"], "node_3"),
helper.make_node('Add',["branch_b_out1","one"],["branch_b_out"],"node_4"),
helper.make_node('Add',["branch_a_out","branch_b_out"],["Z"], "node_5")],
"nGraph EP test graph",
[A, one_in],
[Z],
[one_tensor]
)
save_graph(graph_def, "Independent_SubGraphs.onnx")

View file

@ -11,6 +11,7 @@ namespace onnxruntime {
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_CPU(int use_arena);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_CUDA(int device_id);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Mkldnn(int use_arena);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_NGraph(const char* ng_backend_type);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Nuphar(int device_id, const char*);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_BrainSlice(uint32_t ip, int, int, bool, const char*, const char*, const char*);
std::shared_ptr<IExecutionProviderFactory> CreateExecutionProviderFactory_Tensorrt();
@ -46,6 +47,14 @@ std::unique_ptr<IExecutionProvider> DefaultMkldnnExecutionProvider(bool enable_a
#endif
}
std::unique_ptr<IExecutionProvider> DefaultNGraphExecutionProvider() {
#ifdef USE_NGRAPH
return CreateExecutionProviderFactory_NGraph("CPU")->CreateProvider();
#else
return nullptr;
#endif
}
std::unique_ptr<IExecutionProvider> DefaultNupharExecutionProvider() {
#ifdef USE_NUPHAR
return CreateExecutionProviderFactory_Nuphar(0, "")->CreateProvider();

View file

@ -10,6 +10,7 @@ namespace test {
std::unique_ptr<IExecutionProvider> DefaultCpuExecutionProvider(bool enable_arena = true);
std::unique_ptr<IExecutionProvider> DefaultCudaExecutionProvider();
std::unique_ptr<IExecutionProvider> DefaultMkldnnExecutionProvider(bool enable_arena = true);
std::unique_ptr<IExecutionProvider> DefaultNGraphExecutionProvider();
std::unique_ptr<IExecutionProvider> DefaultNupharExecutionProvider();
std::unique_ptr<IExecutionProvider> DefaultBrainSliceExecutionProvider();
std::unique_ptr<IExecutionProvider> DefaultTensorrtExecutionProvider();

View file

@ -10,6 +10,9 @@
#ifdef USE_MKLDNN
#include "core/providers/mkldnn/mkldnn_provider_factory.h"
#endif
#ifdef USE_NGRAPH
#include "core/providers/ngraph/ngraph_provider_factory.h"
#endif
#ifdef USE_NUPHAR
#include "core/providers/nuphar/nuphar_provider_factory.h"
#endif

View file

@ -25,6 +25,9 @@ elif '--use_cuda' in sys.argv:
package_name = 'ort-gpu-nightly'
nightly_build = True
sys.argv.remove('--nightly_build')
elif '--use_ngraph' in sys.argv:
package_name = 'onnxruntime-ngraph'
sys.argv.remove('--use_ngraph')
if '--nightly_build' in sys.argv:
package_name = 'ort-nightly'
@ -43,6 +46,8 @@ except ImportError:
# Additional binaries
if platform.system() == 'Linux':
libs = ['onnxruntime_pybind11_state.so', 'libmkldnn.so.0', 'libmklml_intel.so', 'libiomp5.so']
# nGraph Libs
libs.extend(['libngraph.so', 'libcodegen.so', 'libcpu_backend.so', 'libmkldnn.so', 'libtbb_debug.so', 'libtbb_debug.so.2', 'libtbb.so', 'libtbb.so.2'])
elif platform.system() == "Darwin":
libs = ['onnxruntime_pybind11_state.so', 'libmkldnn.0.dylib'] # TODO add libmklml and libiomp5 later.
else:

View file

@ -116,6 +116,7 @@ Use the individual flags to only run the specified stages.
parser.add_argument("--use_openblas", action='store_true', help="Build with OpenBLAS.")
parser.add_argument("--use_mkldnn", action='store_true', help="Build with MKLDNN.")
parser.add_argument("--use_mklml", action='store_true', help="Build with MKLML.")
parser.add_argument("--use_ngraph", action='store_true', help="Build with nGraph.")
parser.add_argument("--use_nsync", action='store_true', help="Build with NSYNC.")
parser.add_argument("--use_preinstalled_eigen", action='store_true', help="Use pre-installed eigen.")
parser.add_argument("--eigen_path", help="Path to pre-installed eigen.")
@ -311,6 +312,7 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home
"-Donnxruntime_USE_OPENBLAS=" + ("ON" if args.use_openblas else "OFF"),
"-Donnxruntime_USE_MKLDNN=" + ("ON" if args.use_mkldnn else "OFF"),
"-Donnxruntime_USE_MKLML=" + ("ON" if args.use_mklml else "OFF"),
"-Donnxruntime_USE_NGRAPH=" + ("ON" if args.use_ngraph else "OFF"),
"-Donnxruntime_USE_OPENMP=" + ("ON" if args.use_openmp else "OFF"),
"-Donnxruntime_USE_TVM=" + ("ON" if args.use_tvm else "OFF"),
"-Donnxruntime_USE_LLVM=" + ("ON" if args.use_llvm else "OFF"),
@ -323,8 +325,8 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home
# By default - we currently support only cross compiling for ARM/ARM64 (no native compilation supported through this script)
"-Donnxruntime_CROSS_COMPILING=" + ("ON" if args.arm64 or args.arm else "OFF"),
"-Donnxruntime_BUILD_x86=" + ("ON" if args.x86 else "OFF"),
# TensorRT provider currently only supports full_protobuf option.
"-Donnxruntime_USE_FULL_PROTOBUF=" + ("ON" if args.use_full_protobuf or args.use_tensorrt else "OFF"),
# nGraph and TensorRT providers currently only supports full_protobuf option.
"-Donnxruntime_USE_FULL_PROTOBUF=" + ("ON" if args.use_full_protobuf or args.use_ngraph or args.use_tensorrt else "OFF"),
"-Donnxruntime_DISABLE_CONTRIB_OPS=" + ("ON" if args.disable_contrib_ops else "OFF"),
"-Donnxruntime_MSVC_STATIC_RUNTIME=" + ("ON" if args.enable_msvc_static_runtime else "OFF"),
]
@ -490,7 +492,7 @@ def setup_tensorrt_vars(args):
return tensorrt_home
def run_onnxruntime_tests(args, source_dir, ctest_path, build_dir, configs, enable_python_tests, enable_tvm = False, enable_tensorrt = False):
def run_onnxruntime_tests(args, source_dir, ctest_path, build_dir, configs, enable_python_tests, enable_tvm = False, enable_tensorrt = False, enable_ngraph = False):
for config in configs:
log.info("Running tests for %s configuration", config)
cwd = get_config_build_dir(build_dir, config)
@ -505,7 +507,8 @@ def run_onnxruntime_tests(args, source_dir, ctest_path, build_dir, configs, enab
if enable_python_tests:
# Disable python tests for TensorRT because many tests are not supported yet
if enable_tensorrt:
if enable_tensorrt or enable_ngraph:
print("NOT RUNNING")
return
if is_windows():
cwd = os.path.join(cwd, config)
@ -566,7 +569,7 @@ def run_onnx_tests(build_dir, configs, onnx_test_data_dir, provider, enable_para
else:
run_subprocess([exe] + cmd, cwd=cwd)
def build_python_wheel(source_dir, build_dir, configs, use_cuda, use_tensorrt, nightly_build = False):
def build_python_wheel(source_dir, build_dir, configs, use_cuda, use_ngraph, use_tensorrt, nightly_build = False):
for config in configs:
cwd = get_config_build_dir(build_dir, config)
@ -577,6 +580,8 @@ def build_python_wheel(source_dir, build_dir, configs, use_cuda, use_tensorrt, n
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel', '--use_tensorrt', '--nightly_build'], cwd=cwd)
elif use_cuda:
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel', '--use_cuda', '--nightly_build'], cwd=cwd)
elif use_ngraph:
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel', '--use_ngraph', '--nightly-build'], cwd=cwd)
else:
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel', '--nightly_build'], cwd=cwd)
else:
@ -584,6 +589,8 @@ def build_python_wheel(source_dir, build_dir, configs, use_cuda, use_tensorrt, n
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel', '--use_tensorrt'], cwd=cwd)
elif use_cuda:
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel', '--use_cuda'], cwd=cwd)
elif use_ngraph:
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel', '--use_ngraph'], cwd=cwd)
else:
run_subprocess([sys.executable, os.path.join(source_dir, 'setup.py'), 'bdist_wheel'], cwd=cwd)
if is_ubuntu_1604():
@ -739,7 +746,7 @@ def main():
if args.test :
run_onnxruntime_tests(args, source_dir, ctest_path, build_dir, configs,
args.enable_pybind if not args.skip_onnx_tests else False,
args.use_tvm, args.use_tensorrt)
args.use_tvm, args.use_tensorrt, args.use_ngraph)
# run the onnx model tests if requested explicitly.
if args.enable_onnx_tests and not args.skip_onnx_tests:
# directory from ONNX submodule with ONNX test data
@ -754,6 +761,8 @@ def main():
run_onnx_tests(build_dir, configs, onnx_test_data_dir, 'cuda', False, 2)
elif args.x86 or platform.system() == 'Darwin':
run_onnx_tests(build_dir, configs, onnx_test_data_dir, None, False, 1)
elif args.use_ngraph:
run_onnx_tests(build_dir, configs, onnx_test_data_dir, 'ngraph', True, 1)
# TODO: parallel executor test fails on MacOS
else:
run_onnx_tests(build_dir, configs, onnx_test_data_dir, None, True, 0)
@ -764,8 +773,8 @@ def main():
if args.build:
if args.build_wheel:
nightly_build = bool(os.getenv('NIGHTLY_BUILD') == '1')
build_python_wheel(source_dir, build_dir, configs, args.use_cuda, args.use_tensorrt, nightly_build)
build_python_wheel(source_dir, build_dir, configs, args.use_cuda, args.use_ngraph, args.use_tensorrt, nightly_build)
if args.gen_doc:
generate_documentation(source_dir, build_dir, configs)

View file

@ -0,0 +1,30 @@
jobs:
- job: Linux_nGraph_CI_Dev
pool: Linux-CPU
steps:
- template: templates/set-test-data-variables-step.yml
- task: CmdLine@2
displayName: 'Download azcopy'
inputs:
script: |
curl -so azcopy.tar.gz -L 'https://aka.ms/downloadazcopy-v10-linux'
tar -zxvf azcopy.tar.gz --strip 1
workingDirectory: $(Build.BinariesDirectory)
- task: PythonScript@0
displayName: 'Download test data'
inputs:
scriptPath: '$(Build.SourcesDirectory)/tools/ci_build/github/download_test_data.py'
arguments: --test_data_url $(TestDataUrl)
pythonInterpreter: '/usr/bin/python3'
workingDirectory: $(Build.BinariesDirectory)
- script: 'tools/ci_build/github/linux/run_dockerbuild.sh -o ubuntu16.04 -d ngraph -r $(Build.BinariesDirectory) -x "--use_ngraph"'
displayName: 'Command Line Script'
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: 'Component Detection'
condition: and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
- template: templates/clean-agent-build-directory-step.yml

View file

@ -14,6 +14,7 @@ apt-get update && apt-get install -y software-properties-common
add-apt-repository ppa:deadsnakes/ppa
apt-get update && apt-get install -y --no-install-recommends \
autotools-dev \
automake \
build-essential \
git apt-transport-https \
ca-certificates \
@ -34,6 +35,8 @@ apt-get update && apt-get install -y --no-install-recommends \
libssl1.0.0 \
libkrb5-3 \
libicu55 \
libtinfo-dev \
libtool \
aria2 \
bzip2 \
unzip \

View file

@ -39,6 +39,12 @@ else
--use_openmp \
--cuda_home /usr/local/cuda \
--cudnn_home /usr/local/cuda --build_shared_lib $BUILD_EXTR_PAR
elif [ $BUILD_DEVICE = "ngraph" ]; then
python3 $SCRIPT_DIR/../../build.py --build_dir /build \
--config Debug Release --build_shared_lib \
--skip_submodule_sync --enable_onnx_tests \
--build_wheel \
--parallel --use_openmp $BUILD_EXTR_PAR
else
python3 $SCRIPT_DIR/../../build.py --build_dir /build \
--config Debug Release --build_shared_lib \

View file

@ -63,7 +63,7 @@ if [ -z "$NIGHTLY_BUILD" ]; then
set NIGHTLY_BUILD=0
fi
if [ $BUILD_DEVICE = "cpu" ]; then
if [ $BUILD_DEVICE = "cpu" ] || [ $BUILD_DEVICE = "ngraph" ]; then
docker rm -f "onnxruntime-$BUILD_DEVICE" || true
docker run -h $HOSTNAME \
--name "onnxruntime-$BUILD_DEVICE" \
@ -89,7 +89,7 @@ else
/bin/bash /onnxruntime_src/tools/ci_build/github/linux/run_build.sh \
-d $BUILD_DEVICE -x "$BUILD_EXTR_PAR" -o $BUILD_OS &
fi
wait -n
wait $!
EXIT_CODE=$?