Improve HostDeviceVector exception safety (#4301)

* make the assignments of HostDeviceVector exception safe.
* storing a dummy GPUDistribution instance in HDV for CPU based code.
* change testxgboost binary location to build directory.
This commit is contained in:
sriramch 2019-03-31 07:48:58 -07:00 committed by Jiaming Yuan
parent 680a1b36f3
commit 2f7087eba1
8 changed files with 36 additions and 30 deletions

View File

@ -340,7 +340,7 @@ if(GOOGLE_TEST)
$<TARGET_OBJECTS:objxgboost>) $<TARGET_OBJECTS:objxgboost>)
endif () endif ()
set_output_directory(testxgboost ${PROJECT_SOURCE_DIR}) set_output_directory(testxgboost ${CMAKE_BINARY_DIR})
target_include_directories(testxgboost target_include_directories(testxgboost
PRIVATE ${GTEST_INCLUDE_DIRS}) PRIVATE ${GTEST_INCLUDE_DIRS})
target_link_libraries(testxgboost ${GTEST_LIBRARIES} ${LINK_LIBRARIES}) target_link_libraries(testxgboost ${GTEST_LIBRARIES} ${LINK_LIBRARIES})

View File

@ -166,7 +166,7 @@ environment variable:
.. code-block:: bash .. code-block:: bash
ASAN_OPTIONS=protect_shadow_gap=0 ../testxgboost ASAN_OPTIONS=protect_shadow_gap=0 ${BUILD_DIR}/testxgboost
For details, please consult `official documentation <https://github.com/google/sanitizers/wiki>`_ for sanitizers. For details, please consult `official documentation <https://github.com/google/sanitizers/wiki>`_ for sanitizers.

View File

@ -15,41 +15,42 @@ namespace xgboost {
template <typename T> template <typename T>
struct HostDeviceVectorImpl { struct HostDeviceVectorImpl {
explicit HostDeviceVectorImpl(size_t size, T v) : data_h_(size, v), distribution_() {} explicit HostDeviceVectorImpl(size_t size, T v) : data_h_(size, v) {}
HostDeviceVectorImpl(std::initializer_list<T> init) : data_h_(init), distribution_() {} HostDeviceVectorImpl(std::initializer_list<T> init) : data_h_(init) {}
explicit HostDeviceVectorImpl(std::vector<T> init) : data_h_(std::move(init)), distribution_() {} explicit HostDeviceVectorImpl(std::vector<T> init) : data_h_(std::move(init)) {}
void Swap(HostDeviceVectorImpl &other) {
data_h_.swap(other.data_h_);
}
std::vector<T>& Vec() { return data_h_; } std::vector<T>& Vec() { return data_h_; }
GPUDistribution& Dist() { return distribution_; }
private: private:
std::vector<T> data_h_; std::vector<T> data_h_;
GPUDistribution distribution_;
}; };
template <typename T> template <typename T>
HostDeviceVector<T>::HostDeviceVector(size_t size, T v, GPUDistribution distribution) HostDeviceVector<T>::HostDeviceVector(size_t size, T v, const GPUDistribution &)
: impl_(nullptr) { : impl_(nullptr) {
impl_ = new HostDeviceVectorImpl<T>(size, v); impl_ = new HostDeviceVectorImpl<T>(size, v);
} }
template <typename T> template <typename T>
HostDeviceVector<T>::HostDeviceVector(std::initializer_list<T> init, GPUDistribution distribution) HostDeviceVector<T>::HostDeviceVector(std::initializer_list<T> init, const GPUDistribution &)
: impl_(nullptr) { : impl_(nullptr) {
impl_ = new HostDeviceVectorImpl<T>(init); impl_ = new HostDeviceVectorImpl<T>(init);
} }
template <typename T> template <typename T>
HostDeviceVector<T>::HostDeviceVector(const std::vector<T>& init, GPUDistribution distribution) HostDeviceVector<T>::HostDeviceVector(const std::vector<T>& init, const GPUDistribution &)
: impl_(nullptr) { : impl_(nullptr) {
impl_ = new HostDeviceVectorImpl<T>(init); impl_ = new HostDeviceVectorImpl<T>(init);
} }
template <typename T> template <typename T>
HostDeviceVector<T>::~HostDeviceVector() { HostDeviceVector<T>::~HostDeviceVector() {
HostDeviceVectorImpl<T>* tmp = impl_; delete impl_;
impl_ = nullptr; impl_ = nullptr;
delete tmp;
} }
template <typename T> template <typename T>
@ -63,8 +64,10 @@ HostDeviceVector<T>& HostDeviceVector<T>::operator=(const HostDeviceVector<T>& o
if (this == &other) { if (this == &other) {
return *this; return *this;
} }
delete impl_;
impl_ = new HostDeviceVectorImpl<T>(*other.impl_); HostDeviceVectorImpl<T> newInstance(*other.impl_);
newInstance.Swap(*impl_);
return *this; return *this;
} }
@ -76,7 +79,8 @@ GPUSet HostDeviceVector<T>::Devices() const { return GPUSet::Empty(); }
template <typename T> template <typename T>
const GPUDistribution& HostDeviceVector<T>::Distribution() const { const GPUDistribution& HostDeviceVector<T>::Distribution() const {
return impl_->Dist(); static GPUDistribution dummyInstance;
return dummyInstance;
} }
template <typename T> template <typename T>

View File

@ -171,7 +171,7 @@ struct HostDeviceVectorImpl {
HostDeviceVectorImpl<T>* vec_; HostDeviceVectorImpl<T>* vec_;
}; };
HostDeviceVectorImpl(size_t size, T v, GPUDistribution distribution) HostDeviceVectorImpl(size_t size, T v, const GPUDistribution &distribution)
: distribution_(distribution), perm_h_(distribution.IsEmpty()), size_d_(0) { : distribution_(distribution), perm_h_(distribution.IsEmpty()), size_d_(0) {
if (!distribution_.IsEmpty()) { if (!distribution_.IsEmpty()) {
size_d_ = size; size_d_ = size;
@ -194,7 +194,7 @@ struct HostDeviceVectorImpl {
// Initializer can be std::vector<T> or std::initializer_list<T> // Initializer can be std::vector<T> or std::initializer_list<T>
template <class Initializer> template <class Initializer>
HostDeviceVectorImpl(const Initializer& init, GPUDistribution distribution) HostDeviceVectorImpl(const Initializer& init, const GPUDistribution &distribution)
: distribution_(distribution), perm_h_(distribution.IsEmpty()), size_d_(0) { : distribution_(distribution), perm_h_(distribution.IsEmpty()), size_d_(0) {
if (!distribution_.IsEmpty()) { if (!distribution_.IsEmpty()) {
size_d_ = init.size(); size_d_ = init.size();
@ -435,19 +435,19 @@ struct HostDeviceVectorImpl {
template <typename T> template <typename T>
HostDeviceVector<T>::HostDeviceVector HostDeviceVector<T>::HostDeviceVector
(size_t size, T v, GPUDistribution distribution) : impl_(nullptr) { (size_t size, T v, const GPUDistribution &distribution) : impl_(nullptr) {
impl_ = new HostDeviceVectorImpl<T>(size, v, distribution); impl_ = new HostDeviceVectorImpl<T>(size, v, distribution);
} }
template <typename T> template <typename T>
HostDeviceVector<T>::HostDeviceVector HostDeviceVector<T>::HostDeviceVector
(std::initializer_list<T> init, GPUDistribution distribution) : impl_(nullptr) { (std::initializer_list<T> init, const GPUDistribution &distribution) : impl_(nullptr) {
impl_ = new HostDeviceVectorImpl<T>(init, distribution); impl_ = new HostDeviceVectorImpl<T>(init, distribution);
} }
template <typename T> template <typename T>
HostDeviceVector<T>::HostDeviceVector HostDeviceVector<T>::HostDeviceVector
(const std::vector<T>& init, GPUDistribution distribution) : impl_(nullptr) { (const std::vector<T>& init, const GPUDistribution &distribution) : impl_(nullptr) {
impl_ = new HostDeviceVectorImpl<T>(init, distribution); impl_ = new HostDeviceVectorImpl<T>(init, distribution);
} }
@ -461,8 +461,10 @@ template <typename T>
HostDeviceVector<T>& HostDeviceVector<T>::operator= HostDeviceVector<T>& HostDeviceVector<T>::operator=
(const HostDeviceVector<T>& other) { (const HostDeviceVector<T>& other) {
if (this == &other) { return *this; } if (this == &other) { return *this; }
std::unique_ptr<HostDeviceVectorImpl<T>> newImpl(new HostDeviceVectorImpl<T>(*other.impl_));
delete impl_; delete impl_;
impl_ = new HostDeviceVectorImpl<T>(*other.impl_); impl_ = newImpl.release();
return *this; return *this;
} }

View File

@ -93,7 +93,7 @@ class GPUDistribution {
private: private:
GPUDistribution(GPUSet devices, int granularity, int overlap, GPUDistribution(GPUSet devices, int granularity, int overlap,
std::vector<size_t> offsets) std::vector<size_t> &&offsets)
: devices_(devices), granularity_(granularity), overlap_(overlap), : devices_(devices), granularity_(granularity), overlap_(overlap),
offsets_(std::move(offsets)) {} offsets_(std::move(offsets)) {}
@ -109,7 +109,7 @@ class GPUDistribution {
} }
static GPUDistribution Explicit(GPUSet devices, std::vector<size_t> offsets) { static GPUDistribution Explicit(GPUSet devices, std::vector<size_t> offsets) {
return GPUDistribution(devices, 1, 0, offsets); return GPUDistribution(devices, 1, 0, std::move(offsets));
} }
friend bool operator==(const GPUDistribution& a, const GPUDistribution& b) { friend bool operator==(const GPUDistribution& a, const GPUDistribution& b) {
@ -195,11 +195,11 @@ template <typename T>
class HostDeviceVector { class HostDeviceVector {
public: public:
explicit HostDeviceVector(size_t size = 0, T v = T(), explicit HostDeviceVector(size_t size = 0, T v = T(),
GPUDistribution distribution = GPUDistribution()); const GPUDistribution &distribution = GPUDistribution());
HostDeviceVector(std::initializer_list<T> init, HostDeviceVector(std::initializer_list<T> init,
GPUDistribution distribution = GPUDistribution()); const GPUDistribution &distribution = GPUDistribution());
explicit HostDeviceVector(const std::vector<T>& init, explicit HostDeviceVector(const std::vector<T>& init,
GPUDistribution distribution = GPUDistribution()); const GPUDistribution &distribution = GPUDistribution());
~HostDeviceVector(); ~HostDeviceVector();
HostDeviceVector(const HostDeviceVector<T>&); HostDeviceVector(const HostDeviceVector<T>&);
HostDeviceVector<T>& operator=(const HostDeviceVector<T>&); HostDeviceVector<T>& operator=(const HostDeviceVector<T>&);

View File

@ -5,4 +5,4 @@ cd python-package
python setup.py install --user python setup.py install --user
cd .. cd ..
pytest -v -s --fulltrace -m "(not mgpu) and (not slow)" tests/python-gpu pytest -v -s --fulltrace -m "(not mgpu) and (not slow)" tests/python-gpu
./testxgboost --gtest_filter=-*.MGPU_* ./build/testxgboost --gtest_filter=-*.MGPU_*

View File

@ -5,7 +5,7 @@ cd python-package
python setup.py install --user python setup.py install --user
cd .. cd ..
pytest -v -s --fulltrace -m "(not slow) and mgpu" tests/python-gpu pytest -v -s --fulltrace -m "(not slow) and mgpu" tests/python-gpu
./testxgboost --gtest_filter=*.MGPU_* ./build/testxgboost --gtest_filter=*.MGPU_*
cd tests/distributed cd tests/distributed
./runtests-gpu.sh ./runtests-gpu.sh

View File

@ -123,8 +123,8 @@ if [ ${TASK} == "cmake_test" ]; then
PLUGINS="-DPLUGIN_LZ4=ON -DPLUGIN_DENSE_PARSER=ON" PLUGINS="-DPLUGIN_LZ4=ON -DPLUGIN_DENSE_PARSER=ON"
cmake .. -DGOOGLE_TEST=ON -DGTEST_ROOT=$PWD/../gtest/ ${PLUGINS} cmake .. -DGOOGLE_TEST=ON -DGTEST_ROOT=$PWD/../gtest/ ${PLUGINS}
make make
cd ..
./testxgboost ./testxgboost
cd ..
rm -rf build rm -rf build
fi fi
@ -170,10 +170,10 @@ if [ ${TASK} == "sanitizer_test" ]; then
-DCMAKE_CXX_FLAGS="-fuse-ld=gold" \ -DCMAKE_CXX_FLAGS="-fuse-ld=gold" \
-DCMAKE_C_FLAGS="-fuse-ld=gold" -DCMAKE_C_FLAGS="-fuse-ld=gold"
make make
cd ..
export ASAN_SYMBOLIZER_PATH=$(which llvm-symbolizer) export ASAN_SYMBOLIZER_PATH=$(which llvm-symbolizer)
ASAN_OPTIONS=symbolize=1 ./testxgboost ASAN_OPTIONS=symbolize=1 ./testxgboost
cd ..
rm -rf build rm -rf build
exit 0 exit 0
fi fi