Skip to content

Commit c246cbe

Browse files
goldsboroughPenghuiCheng
authored andcommitted
Integrate custom op tests with CI (pytorch#10611)
Summary: This PR is stacked on pytorch#10610, and only adds changes in one file `.jenkins/pytorch/test.sh`, where we now build the custom op tests and run them. I'd also like to take this PR to discuss whether the [`TorchConfig.cmake`](https://github.com/pytorch/pytorch/blob/master/cmake/TorchConfig.cmake.in) I made is robust enough (we will also see in the CI) orionr Yangqing dzhulgakov what do you think? Also ezyang for CI changes Pull Request resolved: pytorch#10611 Differential Revision: D9597627 Pulled By: goldsborough fbshipit-source-id: f5af8164c076894f448cef7e5b356a6b3159f8b3
1 parent 985c91d commit c246cbe

File tree

13 files changed

+150
-45
lines changed

13 files changed

+150
-45
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ docs/cpp/html/
3030
docs/cpp/api/
3131
test/.coverage
3232
test/cpp/api/mnist
33+
test/custom_operator/model.pt
3334
test/data/gpu_tensors.pt
3435
test/data/legacy_modules.t7
3536
test/data/legacy_serialized.pt
@@ -66,6 +67,7 @@ torch/lib/protoc
6667
torch/lib/tmp_install
6768
torch/lib/torch_shm_manager
6869
torch/lib/python*
70+
torch/share/
6971
torch/version.py
7072

7173
# IPython notebook checkpoints

.jenkins/pytorch/build.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,14 @@ if [[ "$BUILD_TEST_LIBTORCH" == "1" ]]; then
138138
pushd ../cpp-build/caffe2
139139
WERROR=1 VERBOSE=1 DEBUG=1 python $BUILD_LIBTORCH_PY
140140
popd
141+
142+
# Build custom operator tests.
143+
CUSTOM_OP_BUILD="$PWD/../custom-op-build"
144+
CUSTOM_OP_TEST="$PWD/test/custom_operator"
145+
SITE_PACKAGES="$(python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')"
146+
mkdir "$CUSTOM_OP_BUILD"
147+
pushd "$CUSTOM_OP_BUILD"
148+
CMAKE_PREFIX_PATH="$SITE_PACKAGES/torch" cmake "$CUSTOM_OP_TEST"
149+
make VERBOSE=1
150+
popd
141151
fi

.jenkins/pytorch/macos-test.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,35 @@ test_cpp_api() {
7878
"$CPP_BUILD"/caffe2/bin/test_api
7979
}
8080

81+
test_custom_script_ops() {
82+
echo "Testing custom script operators"
83+
pushd test/custom_operator
84+
# Build the custom operator library.
85+
rm -rf build && mkdir build
86+
pushd build
87+
SITE_PACKAGES="$(python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')"
88+
CMAKE_PREFIX_PATH="$SITE_PACKAGES/torch" cmake ..
89+
make VERBOSE=1
90+
popd
91+
92+
# Run tests Python-side and export a script module.
93+
python test_custom_ops.py -v
94+
python model.py --export-script-module=model.pt
95+
# Run tests C++-side and load the exported script module.
96+
build/test_custom_ops ./model.pt
97+
popd
98+
}
99+
100+
81101
if [ -z "${JOB_BASE_NAME}" ] || [[ "${JOB_BASE_NAME}" == *-test ]]; then
82102
test_python_all
83103
test_cpp_api
104+
test_custom_script_ops
84105
else
85106
if [[ "${JOB_BASE_NAME}" == *-test1 ]]; then
86107
test_python_all
87108
elif [[ "${JOB_BASE_NAME}" == *-test2 ]]; then
88109
test_cpp_api
110+
test_custom_script_ops
89111
fi
90112
fi

.jenkins/pytorch/test.sh

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ test_aten() {
9797
# put the dynamic libraries somewhere were the dynamic linker can find them.
9898
# This is a bit of a hack.
9999
if [[ "$BUILD_ENVIRONMENT" == *ppc64le* ]]; then
100-
SUDO=sudo
100+
SUDO=sudo
101101
fi
102102

103103
${SUDO} ln -s "$TORCH_LIB_PATH"/libcaffe2* build/bin
@@ -140,12 +140,28 @@ test_libtorch() {
140140
fi
141141
}
142142

143+
test_custom_script_ops() {
144+
if [[ "$BUILD_TEST_LIBTORCH" == "1" ]]; then
145+
echo "Testing custom script operators"
146+
CUSTOM_OP_BUILD="$PWD/../custom-op-build"
147+
pushd test/custom_operator
148+
cp -r "$CUSTOM_OP_BUILD" build
149+
# Run tests Python-side and export a script module.
150+
python test_custom_ops.py -v
151+
python model.py --export-script-module=model.pt
152+
# Run tests C++-side and load the exported script module.
153+
build/test_custom_ops ./model.pt
154+
popd
155+
fi
156+
}
157+
143158
if [ -z "${JOB_BASE_NAME}" ] || [[ "${JOB_BASE_NAME}" == *-test ]]; then
144159
test_python_nn
145160
test_python_all_except_nn
146161
test_aten
147162
test_torchvision
148163
test_libtorch
164+
test_custom_script_ops
149165
else
150166
if [[ "${JOB_BASE_NAME}" == *-test1 ]]; then
151167
test_python_nn
@@ -154,5 +170,6 @@ else
154170
test_aten
155171
test_torchvision
156172
test_libtorch
173+
test_custom_script_ops
157174
fi
158175
fi

cmake/TorchConfig.cmake.in

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,48 @@
1717
#
1818
# torch_add_custom_op_library(<name> <source_files>)
1919

20-
SET(TORCH_ROOT "${CMAKE_CURRENT_LIST_DIR}/../")
20+
if ($ENV{TORCH_INSTALL_PREFIX})
21+
set(TORCH_INSTALL_PREFIX $ENV{TORCH_INSTALL_PREFIX})
22+
else()
23+
# Assume we are in <install-prefix>/share/cmake/Torch/TorchConfig.cmake
24+
get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
25+
get_filename_component(TORCH_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
26+
endif()
2127

22-
set(TORCH_INCLUDE_DIRS
23-
"${TORCH_ROOT}"
24-
"${TORCH_ROOT}/aten/src"
25-
"${CMAKE_CURRENT_LIST_DIR}/aten/src"
26-
"${CMAKE_CURRENT_LIST_DIR}/caffe2/aten/src"
27-
"${CMAKE_CURRENT_LIST_DIR}/caffe2/aten/src/TH"
28-
)
28+
# Include directories.
29+
set(TORCH_INCLUDE_DIRS "${TORCH_INSTALL_PREFIX}/lib/include")
2930

30-
find_library(TORCH_LIBRARY torch PATHS "${CMAKE_CURRENT_LIST_DIR}/lib" NO_DEFAULT_PATH)
31-
find_library(CAFFE2_LIBRARY caffe2 PATHS "${CMAKE_CURRENT_LIST_DIR}/lib" NO_DEFAULT_PATH)
31+
# Library dependencies.
32+
find_package(Caffe2 REQUIRED)
33+
find_library(TORCH_LIBRARY torch PATHS "${TORCH_INSTALL_PREFIX}/lib")
3234

35+
set(TORCH_LIBRARIES ${TORCH_LIBRARY} ${Caffe2_MAIN_LIBS})
3336
if (@USE_CUDA@)
34-
find_package(CUDA REQUIRED)
35-
find_library(CAFFE2_CUDA_LIBRARY caffe2_gpu PATHS "${CMAKE_CURRENT_LIST_DIR}/lib" NO_DEFAULT_PATH)
36-
set(TORCH_CUDA_LIBRARIES -L${CUDA_TOOLKIT_ROOT_DIR}/lib64 cuda nvrtc cudart nvToolsExt)
37-
list(APPEND TORCH_INCLUDE_DIRS ${CUDA_TOOLKIT_INCLUDE})
37+
if(MSVC)
38+
set(NVTOOLEXT_HOME "C:/Program Files/NVIDIA Corporation/NvToolsExt")
39+
if ($ENV{NVTOOLEXT_HOME})
40+
set(NVTOOLEXT_HOME $ENV{NVTOOLEXT_HOME})
41+
endif()
42+
set(TORCH_CUDA_LIBRARIES
43+
${NVTOOLEXT_HOME}/lib/x64/nvToolsExt64_1.lib
44+
${CUDA_LIBRARIES})
45+
list(APPEND TORCH_INCLUDE_DIRS "${NVTOOLEXT_HOME}/include")
46+
elseif(APPLE)
47+
set(TORCH_CUDA_LIBRARIES
48+
${CUDA_TOOLKIT_ROOT_DIR}/lib/libcudart.dylib
49+
${CUDA_TOOLKIT_ROOT_DIR}/lib/libnvrtc.dylib
50+
${CUDA_TOOLKIT_ROOT_DIR}/lib/libnvToolsExt.dylib
51+
${CUDA_LIBRARIES})
52+
else()
53+
set(TORCH_CUDA_LIBRARIES
54+
${CUDA_CUDA_LIB}
55+
${CUDA_NVRTC_LIB}
56+
${CUDA_TOOLKIT_ROOT_DIR}/lib64/libnvToolsExt.so
57+
${CUDA_LIBRARIES})
58+
endif()
59+
list(APPEND TORCH_LIBRARIES ${TORCH_CUDA_LIBRARIES})
3860
endif()
3961

40-
set(TORCH_LIBRARIES
41-
${TORCH_LIBRARY}
42-
${CAFFE2_LIBRARY}
43-
${CAFFE2_CUDA_LIBRARY}
44-
${TORCH_CUDA_LIBRARIES})
45-
4662
# Creates a shared library <name> with the correct include directories
4763
# and linker flags set to include Torch header files and link with Torch
4864
# libraries. Also sets the C++ standard version to C++11. All options
@@ -51,5 +67,5 @@ function(torch_add_custom_op_library name source_files)
5167
add_library(${name} SHARED ${source_files})
5268
target_include_directories(${name} PUBLIC "${TORCH_INCLUDE_DIRS}")
5369
target_link_libraries(${name} "${TORCH_LIBRARIES}")
54-
target_compile_options(${name} PUBLIC -std=c++11)
70+
set_property(TARGET ${name} PROPERTY CXX_STANDARD 11)
5571
endfunction(torch_add_custom_op_library)

setup.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,10 @@ def check_file(f):
443443
# we need to find a better way to do this.
444444
# More information can be found in conversation thread of PR #5772
445445

446-
self.copy_tree('torch/csrc', 'torch/lib/include/torch/csrc/')
446+
self.copy_tree('torch/lib/tmp_install/share', 'torch/share')
447447
self.copy_tree('third_party/pybind11/include/pybind11/',
448448
'torch/lib/include/pybind11')
449449
self.copy_file('torch/csrc/torch.h', 'torch/lib/include/torch/torch.h')
450-
self.copy_file('torch/op.h', 'torch/lib/include/torch/op.h')
451450

452451

453452
build_dep_cmds = {}
@@ -1136,27 +1135,38 @@ def make_relative_rpath(path):
11361135
'lib/*.h',
11371136
'lib/include/ATen/*.h',
11381137
'lib/include/ATen/core/*.h',
1139-
'lib/include/ATen/detail/*.h',
1140-
'lib/include/ATen/cuda/*.h',
11411138
'lib/include/ATen/cuda/*.cuh',
1139+
'lib/include/ATen/cuda/*.h',
1140+
'lib/include/ATen/cuda/detail/*.cuh',
11421141
'lib/include/ATen/cuda/detail/*.h',
11431142
'lib/include/ATen/cudnn/*.h',
1144-
'lib/include/ATen/cuda/detail/*.cuh',
1143+
'lib/include/ATen/detail/*.h',
1144+
'lib/include/caffe2/utils/*.h',
1145+
'lib/include/torch/*.h',
1146+
'lib/include/torch/csrc/*.h',
1147+
'lib/include/torch/csrc/api/include/torch/detail/ordered_dict.h',
1148+
'lib/include/torch/csrc/autograd/*.h',
1149+
'lib/include/torch/csrc/autograd/generated/*.h',
1150+
'lib/include/torch/csrc/cuda/*.h',
1151+
'lib/include/torch/csrc/jit/*.h',
1152+
'lib/include/torch/csrc/jit/generated/*.h',
1153+
'lib/include/torch/csrc/jit/passes/*.h',
1154+
'lib/include/torch/csrc/jit/script/*.h',
1155+
'lib/include/torch/csrc/utils/*.h',
11451156
'lib/include/pybind11/*.h',
11461157
'lib/include/pybind11/detail/*.h',
11471158
'lib/include/TH/*.h*',
11481159
'lib/include/TH/generic/*.h*',
1149-
'lib/include/THC/*.h*',
11501160
'lib/include/THC/*.cuh',
1161+
'lib/include/THC/*.h*',
11511162
'lib/include/THC/generic/*.h',
11521163
'lib/include/THCUNN/*.cuh',
11531164
'lib/include/THNN/*.h',
1154-
'lib/include/torch/csrc/*.h',
1155-
'lib/include/torch/csrc/autograd/*.h',
1156-
'lib/include/torch/csrc/jit/*.h',
1157-
'lib/include/torch/csrc/utils/*.h',
1158-
'lib/include/torch/csrc/cuda/*.h',
1159-
'lib/include/torch/torch.h',
1165+
'share/cmake/ATen/*.cmake',
1166+
'share/cmake/Caffe2/*.cmake',
1167+
'share/cmake/Caffe2/public/*.cmake',
1168+
'share/cmake/Gloo/*.cmake',
1169+
'share/cmake/Torch/*.cmake',
11601170
],
11611171
'caffe2': [
11621172
rel_site_packages + '/caffe2/**/*.py'

test/custom_operator/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ project(custom_ops)
44

55
find_package(Torch REQUIRED)
66

7+
# This convenience function will create a shared library target, configure
8+
# the right include directories and link against the right libraries. It is
9+
# exactly equivalent to the following lines:
10+
#
11+
# add_library(custom_ops SHARED op.cpp)
12+
# target_include_directories(custom_ops PUBLIC "${TORCH_INCLUDE_DIRS}")
13+
# target_link_libraries(custom_ops "${TORCH_LIBRARIES}")
14+
# set_property(TARGET custom_ops PROPERTY CXX_STANDARD 11)
15+
#
716
torch_add_custom_op_library(custom_ops op.cpp)
817

918
add_executable(test_custom_ops test_custom_ops.cpp)

test/custom_operator/model.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import argparse
22
import os.path
3+
import sys
34

45
import torch
56

67

8+
SHARED_LIBRARY_EXTENSIONS = {'linux': 'so', 'darwin': 'dylib', 'win32': 'dll'}
9+
10+
11+
def get_custom_op_library_path():
12+
extension = SHARED_LIBRARY_EXTENSIONS[sys.platform]
13+
path = os.path.abspath('build/libcustom_ops.{}'.format(extension))
14+
assert os.path.exists(path), path
15+
return path
16+
17+
718
class Model(torch.jit.ScriptModule):
819
def __init__(self):
920
super(Model, self).__init__()
@@ -20,7 +31,7 @@ def main():
2031
parser.add_argument("--export-script-module-to", required=True)
2132
options = parser.parse_args()
2233

23-
torch.ops.load_library(os.path.abspath('build/libcustom_ops.so'))
34+
torch.ops.load_library(get_custom_op_library_path())
2435

2536
model = Model()
2637
model.save(options.export_script_module_to)

test/custom_operator/op.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <cstddef>
44
#include <vector>
55

6-
std::vector<at::Tensor> custom_op(
6+
TORCH_API std::vector<at::Tensor> custom_op(
77
at::Tensor tensor,
88
double scalar,
99
int64_t repeat);

test/custom_operator/test_custom_ops.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ void get_operator_from_registry_and_execute() {
2222
std::vector<at::Tensor> output;
2323
torch::jit::pop(stack, output);
2424

25+
const auto manual = custom_op(torch::ones(5), 2.0, 3);
26+
2527
assert(output.size() == 3);
26-
for (const auto& tensor : output) {
27-
assert(tensor.allclose(torch::ones(5) * 2));
28+
for (size_t i = 0; i < output.size(); ++i) {
29+
assert(output[i].allclose(torch::ones(5) * 2));
30+
assert(output[i].allclose(manual[i]));
2831
}
2932
}
3033

@@ -71,10 +74,9 @@ void test_argument_checking_for_serialized_modules(
7174
module->forward({});
7275
assert(false);
7376
} catch (const at::Error& error) {
74-
std::cout << error.what_without_backtrace() << std::endl;
7577
assert(
7678
std::string(error.what_without_backtrace())
77-
.find("custom::op() is missing value for argument 'tensor'") == 0);
79+
.find("forward() is missing value for argument 'input'") == 0);
7880
}
7981
}
8082

test/custom_operator/test_custom_ops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
import torch
77

8-
from model import Model
8+
from model import Model, get_custom_op_library_path
99

1010

1111
class TestCustomOperators(unittest.TestCase):
1212
def setUp(self):
13-
self.library_path = os.path.abspath('build/libcustom_ops.so')
13+
self.library_path = get_custom_op_library_path()
1414
torch.ops.load_library(self.library_path)
1515

1616
def test_custom_library_is_loaded(self):

torch/CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ if(NOT TORCH_INSTALL_BIN_DIR)
2626
endif()
2727

2828
if(NOT TORCH_INSTALL_INCLUDE_DIR)
29-
set(TORCH_INSTALL_INCLUDE_DIR include/libtorch)
29+
set(TORCH_INSTALL_INCLUDE_DIR include)
3030
endif()
3131

3232
if(NOT TORCH_INSTALL_LIB_DIR)
@@ -287,8 +287,7 @@ if (MSVC)
287287
target_link_libraries(torch onnx onnx_library)
288288
endif()
289289

290-
target_link_libraries(torch
291-
caffe2_library)
290+
target_link_libraries(torch caffe2_library)
292291

293292
find_package(OpenMP)
294293
if(OPENMP_FOUND)
@@ -395,6 +394,8 @@ endif()
395394
install(DIRECTORY "${TORCH_SRC_DIR}/csrc"
396395
DESTINATION ${TORCH_INSTALL_INCLUDE_DIR}/torch
397396
FILES_MATCHING PATTERN "*.h")
397+
install(FILES "${TORCH_SRC_DIR}/op.h"
398+
DESTINATION ${TORCH_INSTALL_INCLUDE_DIR}/torch)
398399

399400
install(TARGETS torch
400401
RUNTIME DESTINATION "${TORCH_INSTALL_BIN_DIR}"
@@ -473,3 +474,7 @@ configure_file(
473474
${TORCH_ROOT}/cmake/TorchConfig.cmake.in
474475
${PROJECT_BINARY_DIR}/TorchConfig.cmake
475476
@ONLY)
477+
install(FILES
478+
${PROJECT_BINARY_DIR}/TorchConfigVersion.cmake
479+
${PROJECT_BINARY_DIR}/TorchConfig.cmake
480+
DESTINATION share/cmake/Torch)

torch/op.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <torch/csrc/autograd/generated/variable_factories.h>
44
#include <torch/csrc/jit/custom_operator.h>
55
#include <torch/csrc/jit/import.h>
6+
#include <torch/csrc/WindowsTorchApiMacro.h>
67

78
#include <ATen/ATen.h>
89

0 commit comments

Comments
 (0)