# 304: access control not specified
# 383: value copied to temporary, reference to temporary used
# 444: destructor for base class ".." is not virtual
+# 869: was never referenced (false positives)
#2282: unrecognized GCC pragma
- GMX_TEST_CXXFLAG(CXXFLAGS_WARN "-w3 -wd177 -wd304 -wd383 -wd411 -wd444 -wd981 -wd1418 -wd1572 -wd1599 -wd2259 -wd2547 -wd3280 -wd11074 -wd11076 -wd2282" GMXC_CXXFLAGS)
+ GMX_TEST_CXXFLAG(CXXFLAGS_WARN "-w3 -wd177 -wd304 -wd383 -wd411 -wd444 -wd869 -wd981 -wd1418 -wd1572 -wd1599 -wd2259 -wd2547 -wd3280 -wd11074 -wd11076 -wd2282" GMXC_CXXFLAGS)
endif()
GMX_TEST_CXXFLAG(CXXFLAGS_OPT "-ip -funroll-all-loops -alias-const -ansi-alias -no-prec-div -fimf-domain-exclusion=14 -qoverride-limits" GMXC_CXXFLAGS_RELEASE)
GMX_TEST_CXXFLAG(CXXFLAGS_DEBUG "-O0" GMXC_CXXFLAGS_DEBUG)
endif()
if (GMX_COMPILER_WARNINGS)
#809: exception specification for virtual function X is incompatible with that of overridden function
- GMX_TEST_CXXFLAG(CXXFLAGS_WARN "/W3 /wd177 /wd304 /wd383 /wd411 /wd444 /wd809 /wd981 /wd1418 /wd1572 /wd1599 /wd1786 /wd2259 /wd2547 /wd3280 /wd11074 /wd11076 /wd2282" GMXC_CXXFLAGS)
+ GMX_TEST_CXXFLAG(CXXFLAGS_WARN "/W3 /wd177 /wd304 /wd383 /wd411 /wd444 /wd809 /wd869 /wd981 /wd1418 /wd1572 /wd1599 /wd1786 /wd2259 /wd2547 /wd3280 /wd11074 /wd11076 /wd2282" GMXC_CXXFLAGS)
endif()
GMX_TEST_CXXFLAG(CXXFLAGS_OPT "/Qip" GMXC_CXXFLAGS_RELEASE)
endif()
#include "hostallocator.h"
-#include "config.h"
-
#include <cstddef>
#include <memory>
if (pinningPolicy_ == PinningPolicy::PinnedIfSupported)
{
void *p = PageAlignedAllocationPolicy::malloc(bytes);
- pin(p, bytes);
+ if (p)
+ {
+ /* For every pin, unpin has to be called or resources will
+ * leak. Doing this correctly is guaranteed because for
+ * every p!=null && pinningPolicy_ == PinnedIfSupported,
+ * the malloc and free calls handle pinning. For very
+ * standard-compliant containers, the allocator object
+ * can't be changed independently of the buffer (for move,
+ * it is propagated) and thus the allocator (and thus
+ * pinningPolicy_) can't change between malloc and
+ * free.
+ *
+ * Note that we always pin (even for size 0) so that we
+ * can always unpin without any checks. */
+ pinBuffer(p, bytes);
+ }
return p;
}
else
// Nothing to do
return;
}
- unpin(buffer);
if (pinningPolicy_ == PinningPolicy::PinnedIfSupported)
{
+ unpinBuffer(buffer);
PageAlignedAllocationPolicy::free(buffer);
}
else
}
}
-void HostAllocationPolicy::pin(void gmx_unused *p, size_t gmx_unused n) const noexcept
-{
-#if GMX_GPU == GMX_GPU_CUDA
- // I believe this if statement isn't required for calls from malloc. But it
- // is required for the unit tests. Which might not make much sense to test
- // cases which can't actually happen for an allocator.
- if (p == nullptr || n == 0 || isHostMemoryPinned(p))
- {
- return;
- }
- pinBuffer(p, n);
-#endif
-}
-
-void HostAllocationPolicy::unpin(void gmx_unused *p) const noexcept
-{
-#if GMX_GPU == GMX_GPU_CUDA
- if (isHostMemoryPinned(p))
- {
- unpinBuffer(p);
- }
-#endif
-}
-
} // namespace gmx
*
* Does not throw.
*/
- void pin(void* p, size_t n) const noexcept;
- /*! \brief Unpin the allocation, if appropriate.
- *
- * Regardless of the allocation policy, unpin the memory if
- * previously pinned, otherwise do nothing.
- *
- * Does not throw.
- */
- void unpin(void* p) const noexcept;
- /*! \brief Return the current pinning policy (which is semi-independent
- * of whether the buffer is actually pinned).
- *
- * Does not throw.
- */
PinningPolicy pinningPolicy() const { return pinningPolicy_; }
//! Don't propagate for copy
using propagate_on_container_copy_assignment = std::false_type;
{
return {};
}
+
private:
//! Pinning policy
PinningPolicy pinningPolicy_;
};
+/*! \brief Return true if two allocators are identical
+ *
+ * True if pinning policy is the same.
+ */
+template<class T1, class T2>
+bool operator==(const Allocator<T1, HostAllocationPolicy> &a,
+ const Allocator<T2, HostAllocationPolicy> &b)
+{
+ return a.pinningPolicy() == b.pinningPolicy();
+}
+
/*! \brief Helper function for changing the pinning policy of a HostVector.
*
* If the vector has contents, then a full reallocation and buffer
template <class T>
void changePinningPolicy(HostVector<T> *v, PinningPolicy pinningPolicy)
{
- if (v->get_allocator().pinningPolicy() == pinningPolicy)
- {
- return;
- }
- //Force reallocation by creating copy
- *v = HostVector<T>(*v, {pinningPolicy});
+ //Force reallocation by element-wise move (because policy is different
+ //container is forced to realloc). Does nothing if policy is the same.
+ *v = HostVector<T>(std::move(*v), {pinningPolicy});
}
} // namespace gmx
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2017, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
* top-level source directory and at http://www.gromacs.org.
#include <cstddef>
+#include <algorithm>
+
#include "gromacs/gpu_utils/cudautils.cuh"
#include "gromacs/utility/alignedallocator.h"
#include "gromacs/utility/exceptions.h"
GMX_ASSERT(isAligned(pointer, PageAlignedAllocationPolicy::alignment()),
formatString("%s Host memory needs to be page aligned.", errorMessage).c_str());
+ numBytes = std::max<size_t>(1, numBytes); //C++11 3.7.4.1 gurantees that every pointer is different thus at least 1 byte
+
ensureNoPendingCudaError(errorMessage);
cudaError_t stat = cudaHostRegister(pointer, numBytes, cudaHostRegisterDefault);
this->compareVectors(input, output);
}
+struct MoveOnly {
+ MoveOnly(real x = 0) : x(x) {}
+ MoveOnly(const MoveOnly &) = delete;
+ MoveOnly(MoveOnly &&) = default;
+ MoveOnly &operator=(const MoveOnly &) = delete;
+ MoveOnly &operator=(MoveOnly &&) = default;
+ bool operator==(const MoveOnly &o) const { return x == o.x; }
+ real x;
+};
+
//! The types used in testing.
-typedef ::testing::Types<int, real, RVec> TestTypes;
+typedef ::testing::Types<int, real, RVec, MoveOnly> TestTypes;
//! Typed test fixture
template <typename T>
};
TYPED_TEST_CASE(HostAllocatorTestNoMem, TestTypes);
+//! Typed test fixture for tests requiring a copyable type
+template <typename T>
+struct HostAllocatorTestNoMemCopyable : HostAllocatorTestNoMem<T> {};
+//! The types used in testing minus move only types
+using TestTypesCopyable = ::testing::Types<int, real, RVec>;
+TYPED_TEST_CASE(HostAllocatorTestNoMemCopyable, TestTypesCopyable);
+
// Note that in GoogleTest typed tests, the use of TestFixture:: and
// this-> is sometimes required to get access to things in the fixture
// class (or its base classes).
TYPED_TEST(HostAllocatorTest, VectorsWithDefaultHostAllocatorAlwaysWorks)
{
- typename TestFixture::VectorType input = {{1, 2, 3}}, output;
+ typename TestFixture::VectorType input(3), output;
output.resize(input.size());
}
EXPECT_TRUE(input4.get_allocator().pinningPolicy() == PinningPolicy::PinnedIfSupported);
}
-TYPED_TEST(HostAllocatorTestNoMem, CopyAssignment)
+TYPED_TEST(HostAllocatorTestNoMemCopyable, CopyAssignment)
{
typename TestFixture::VectorType input1;
typename TestFixture::VectorType input2({PinningPolicy::PinnedIfSupported});
EXPECT_TRUE (input2.get_allocator().pinningPolicy() == PinningPolicy::PinnedIfSupported);
}
-TYPED_TEST(HostAllocatorTestNoMem, CopyConstruction)
+TYPED_TEST(HostAllocatorTestNoMemCopyable, CopyConstruction)
{
typename TestFixture::VectorType input1;
typename TestFixture::VectorType input2(input1); //NOLINT(performance-unnecessary-copy-initialization)
EXPECT_TRUE (input2.get_allocator().pinningPolicy() == PinningPolicy::PinnedIfSupported);
}
+TYPED_TEST(HostAllocatorTestNoMem, Comparison)
+{
+ using AllocatorType = typename TestFixture::VectorType::allocator_type;
+ EXPECT_EQ(AllocatorType {}, AllocatorType {});
+ //Should be false for different pinning policy
+ EXPECT_NE(AllocatorType {}, AllocatorType {PinningPolicy::PinnedIfSupported});
+}
+
#if GMX_GPU == GMX_GPU_CUDA
// Policy suitable for pinning is only supported for a CUDA build
EXPECT_TRUE(input.get_allocator().pinningPolicy() == PinningPolicy::PinnedIfSupported);
EXPECT_FALSE(isPinned(input));
- // Unpin before allocation is fine, but does nothing.
- input.get_allocator().getPolicy().unpin(input.data());
- EXPECT_FALSE(isPinned(input));
-
- // Pin with no contents is fine, but does nothing.
- input.get_allocator().getPolicy().pin(input.data(), 0);
- EXPECT_FALSE(isPinned(input));
-
// Fill some contents, which will be pinned because of the policy.
this->fillInput(&input);
EXPECT_TRUE(isPinned(input));
- // Unpin after pin is fine.
- input.get_allocator().getPolicy().unpin(input.data());
- EXPECT_FALSE(isPinned(input));
-
- // Repeated unpin should be a no-op.
- input.get_allocator().getPolicy().unpin(input.data());
-
- // Pin after unpin is fine.
- const size_t size = input.size() * sizeof(typename TestFixture::VectorType::value_type);
- input.get_allocator().getPolicy().pin(input.data(), size);
- EXPECT_TRUE(isPinned(input));
-
- // Repeated pin should be a no-op, and still pinned.
- input.get_allocator().getPolicy().pin(input.data(), size);
- EXPECT_TRUE(isPinned(input));
-
// Switching policy to CannotBePinned must unpin the buffer (via
// realloc and copy).
auto oldInputData = input.data();
EXPECT_NE(oldInputData, input.data());
}
-#else
-
-TYPED_TEST(HostAllocatorTest, ManualPinningOperationsWorkEvenWithoutCuda)
-{
- typename TestFixture::VectorType input;
-
- // Since the buffer can't be pinned and isn't pinned, and the
- // calling code can't be unhappy about this, these are OK.
- input.get_allocator().getPolicy().pin(input.data(),
- input.size()*sizeof(typename TestFixture::VectorType::value_type));
- input.get_allocator().getPolicy().unpin(input.data());
-}
-
#endif
TYPED_TEST(HostAllocatorTest, StatefulAllocatorUsesMemory)
sizeof(typename TestFixture::VectorType));
}
+TEST(HostAllocatorUntypedTest, Comparison)
+{
+ //Should always be true for the same policy, indpendent of value_type
+ EXPECT_EQ(HostAllocator<float>{}, HostAllocator<double>{});
+}
+
//! Declare allocator types to test.
using AllocatorTypesToTest = ::testing::Types<HostAllocator<real>,
HostAllocator<int>,
- HostAllocator<RVec>
+ HostAllocator<RVec>,
+ HostAllocator<MoveOnly>
>;
TYPED_TEST_CASE(AllocatorTest, AllocatorTypesToTest);
* by size_type.
*/
std::size_t
- max_size() const { return (static_cast<std::size_t>(0) - static_cast<std::size_t>(1)) / sizeof(T); }
+ max_size() const { return SIZE_MAX / sizeof(T); }
/*! \brief Return true if two allocators are identical
*
* This is a member function of the left-hand-side allocator.
+ * Always true for stateless polcies. Has to be defined in the policy for stateful policies.
*/
- template<class T2>
- bool
- operator==(const Allocator<T2, AllocationPolicy> & /*unused*/) const { return std::is_same<T, T2>::value; }
+ template<class T2, class A = AllocationPolicy, typename = typename std::enable_if<std::is_empty<A>::value>::type>
+ bool operator==(const Allocator<T2, AllocationPolicy> & /*unused*/) const { return true; }
/*! \brief Return true if two allocators are different
*
* This is a member function of the left-hand-side allocator.
*/
bool
- operator!=(const Allocator &rhs) const { return !operator==(rhs); }
-
- //! Obtain allocator for copy construction
- Allocator select_on_container_copy_construction() const
- {
- return Allocator(AllocationPolicy::select_on_container_copy_construction());
- }
+ operator!=(const Allocator &rhs) const { return !(*this == rhs); }
};
} // namespace gmx
}
}
+TYPED_TEST(AllocatorTest, Move) //NOLINT(misc-definitions-in-headers)
+{
+ using value_type = typename TypeParam::value_type;
+ std::vector<value_type, TypeParam> v1(1);
+ value_type* data = v1.data();
+ EXPECT_NE(data, nullptr);
+ std::vector<value_type, TypeParam> v2(std::move(v1));
+ EXPECT_EQ(data, v2.data());
+}
+
} // namespace test
} // namespace gmx
sizeof(std::vector<value_type, TypeParam>));
}
+TEST(AllocatorUntypedTest, Comparison)
+{
+ //Should always be true for the same policy, indpendent of value_type
+ EXPECT_EQ(AlignedAllocator<float>{}, AlignedAllocator<double>{});
+ EXPECT_EQ(PageAlignedAllocator<float>{}, PageAlignedAllocator<double>{});
+}
+
} // namespace test
} // namespace gmx