*
* Copyright (c) 1991-2000, University of Groningen, The Netherlands.
* Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2017, 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.
Simd4Real real_mask_S0, real_mask_S1;
int of, i;
- work = new(internal::alignedMalloc(sizeof(pme_spline_work)))pme_spline_work;
+ work = new(gmx::AlignedAllocationPolicy::malloc(sizeof(pme_spline_work)))pme_spline_work;
zero_S = setZero();
* Implements AlignedAllocator.
*
* \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
* \ingroup module_utility
*/
#include "gmxpre.h"
#include <cstdlib>
+#include <memory>
+
#if HAVE_MM_MALLOC_H
# include <mm_malloc.h>
#elif HAVE_MALLOC_H
# include <xmmintrin.h>
#endif
-#include "gromacs/utility/gmxassert.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "gromacs/compat/make_unique.h"
+#include "gromacs/utility/gmxassert.h"
namespace gmx
{
-namespace internal
+namespace
{
/*! \brief Allocate aligned memory in a fully portable way
}
}
-
-
-void *
-alignedMalloc(std::size_t bytes)
+//! Implement malloc of \c bytes of memory, aligned to \c alignment.
+static void *mallocImpl(std::size_t bytes, std::size_t alignment)
{
- // For now we always use 128-byte alignment:
- // 1) IBM Power already has cache lines of 128-bytes, and needs it.
- // 2) x86 has 64 byte cache lines, but since a future AVX-1024 (rumored?)
- // will need 1024/8=128 byte SIMD alignment, it is safer to use that
- // already now.
- // 3) The old Pentium4 used 256-byte cache prefetching (but 64-byte lines).
- // However, it's not worth worrying about performance for P4...
- // 4) ARM & Sparc have 64 byte lines, but will be just fine with
- // 128-byte alignment (nobody knows what the future brings)
- //
- // So, for now we're semi-lazy and just align to 128 bytes!
- //
- // TODO LINCS code is copying this assumption independently (for now)
- std::size_t alignment = 128;
-
void * p;
- // Pad memory at the end with another alignment bytes to avoid false sharing
- bytes += alignment;
-
#if HAVE__MM_MALLOC
p = _mm_malloc( bytes, alignment );
#elif HAVE_POSIX_MEMALIGN
#elif HAVE__ALIGNED_MALLOC
p = _aligned_malloc(bytes, alignment);
#else
- p = alignedMallocGeneric(bytes, alignment);
+ p = internal::alignedMallocGeneric(bytes, alignment);
#endif
return p;
}
-void
-alignedFree(void *p)
+//! Free aligned memory allocated with mallocImpl().
+static void freeImpl(void *p)
{
if (p)
{
#elif HAVE__ALIGNED_MALLOC
_aligned_free(p);
#else
- alignedFreeGeneric(p);
+ internal::alignedFreeGeneric(p);
#endif
}
}
-} // namespace internal
+} // namespace
+
+// === AlignedAllocationPolicy
+
+std::size_t AlignedAllocationPolicy::alignment()
+{
+ // For now we always use 128-byte alignment:
+ // 1) IBM Power already has cache lines of 128-bytes, and needs it.
+ // 2) x86 has 64 byte cache lines, but since a future AVX-1024 (rumored?)
+ // will need 1024/8=128 byte SIMD alignment, it is safer to use that
+ // already now.
+ // 3) The old Pentium4 used 256-byte cache prefetching (but 64-byte lines).
+ // However, it's not worth worrying about performance for P4...
+ // 4) ARM & Sparc have 64 byte lines, but will be just fine with
+ // 128-byte alignment (nobody knows what the future brings)
+ //
+ // So, for now we're semi-lazy and just align to 128 bytes!
+ //
+ // TODO LINCS code is copying this assumption independently (for now)
+ return 128;
+}
+
+void *
+AlignedAllocationPolicy::malloc(std::size_t bytes)
+{
+ // Pad memory at the end with another alignment bytes to avoid false sharing
+ auto size = alignment();
+ bytes += size;
+
+ return mallocImpl(bytes, size);
+}
+
+void
+AlignedAllocationPolicy::free(void *p)
+{
+ freeImpl(p);
+}
+
+// === PageAlignedAllocationPolicy
+
+//! Return a page size, from a sysconf query if available, or a default guess (4096 bytes).
+static std::size_t getPageSize()
+{
+ long pageSize;
+ /* Note that sysconf returns -1 on its error conditions, which we
+ don't really need to check, nor can really handle at
+ initialization time. */
+#if defined(_SC_PAGESIZE)
+ pageSize = sysconf(_SC_PAGESIZE);
+#elif defined(_SC_PAGE_SIZE)
+ pageSize = sysconf(_SC_PAGE_SIZE);
+#else
+ pageSize = -1;
+#endif
+ return ((pageSize == -1) ? 4096 // A useful guess e.g. on Windows.
+ : static_cast<std::size_t>(pageSize));
+}
+
+/* Implements the "construct on first use" idiom to avoid the static
+ * initialization order fiasco where a possible static page-aligned
+ * container would be initialized before the alignment variable was.
+ *
+ * Note that thread-safety of the initialization is guaranteed by the
+ * C++11 language standard.
+ *
+ * The size_t has no destructor, so there is no deinitialization
+ * issue. See https://isocpp.org/wiki/faq/ctors for discussion of
+ * alternatives and trade-offs. */
+std::size_t PageAlignedAllocationPolicy::alignment()
+{
+ static size_t thePageSize = getPageSize();
+ return thePageSize;
+}
+
+void *
+PageAlignedAllocationPolicy::malloc(std::size_t bytes)
+{
+ return mallocImpl(bytes, alignment());
+}
+
+void
+PageAlignedAllocationPolicy::free(void *p)
+{
+ freeImpl(p);
+}
} // namespace gmx
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2015,2016,2017, by the GROMACS development team, led by
+ * Copyright (c) 2017, 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.
* the research papers on the package. Check out http://www.gromacs.org.
*/
/*! \libinternal \file
- * \brief
- * Declares gmx::AlignedAllocator that is used to make standard library
- * containers compatible with SIMD contents that require aligned load/store.
+ * \brief Declares allocation policy classes and allocators that are
+ * used to make library containers compatible with alignment
+ * requirements of particular hardware, e.g. memory operations for
+ * SIMD or accelerators.
*
* \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
* \inlibraryapi
* \ingroup module_utility
*/
#include <cstddef>
-#include <memory>
-#include <new>
-
-#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/allocator.h"
namespace gmx
{
-namespace internal
-{
-
-/*! \brief Allocate aligned memory
- *
- * \param bytes Amount of memory (bytes) to allocate. It is valid to ask for
- * 0 bytes, which will return a non-null pointer that is properly
- * aligned and padded (but that you should not use).
- *
- * \return Valid pointer if the allocation worked, otherwise nullptr.
- *
- * The memory will always be aligned to 128 bytes, which is our
- * estimate of the longest cache lines on architectures currently in use.
- * It will also be padded by the same amount at the end of the
- * area, to help avoid false cache sharing.
- *
- * \note Memory allocated with this routine must be released with
- * gmx::internal::alignedFree(), and absolutely not the system free().
- */
-void *
-alignedMalloc(std::size_t bytes);
-
-/*! \brief Free aligned memory
- *
- * \param p Memory pointer previously returned from gmx::internal::alignedMalloc()
- *
- * \note This routine should only be called with pointers obtained from
- * gmx::internal::alignedMalloc(), and absolutely not any pointers obtained
- * the system malloc().
+/*! \libinternal \brief Policy class for configuring gmx::Allocator, to manage
+ * allocations of aligned memory for SIMD code.
*/
-void
-alignedFree(void *p);
-
-}
-
-/*! \libinternal \brief Aligned memory allocator.
- *
- * \tparam T Type of objects to allocate
- *
- * This class can be used for the optional allocator template parameter
- * in standard library containers, which is necessary e.g. to use SIMD
- * aligned load and store operations in those containers. The memory will always
- * be aligned to 128 bytes, which is our estimate of the longest cache lines on
- * architectures currently in use. It will also be padded by the same amount at
- * the end of the area, to help avoid false cache sharing.
- *
- * \throws std::bad_alloc Instead of a GROMACS exception object we throw the
- * standard one on allocation failures to make it as compatible as possible with
- * the errors expected by code using the standard library containers.
- *
- * \inlibraryapi
- * \ingroup module_utility
- */
-template <class T>
-class AlignedAllocator
+class AlignedAllocationPolicy
{
public:
- // The standard library specification for a custom allocator
- // requires these typedefs, with this capitalization/underscoring.
- typedef T value_type; //!< Type of allocated elements
- typedef T &reference; //!< Reference to allocated elements
- typedef const T &const_reference; //!< Constant reference to allocated elements
- typedef T * pointer; //!< Pointer to allocated elements
- typedef const T * const_pointer; //!< Constant pointer to allocated elements
- typedef std::size_t size_type; //!< Integer type to use for size of objects
- typedef std::ptrdiff_t difference_type; //!< Type to hold differences between pointers
-
- /*! \libinternal \brief Standard-required typedef to use allocator with different class.
+ /*! \brief Return the alignment size. */
+ static std::size_t
+ alignment();
+ /*! \brief Allocate memory aligned to alignment() bytes.
*
- * \tparam U new class
+ * \param bytes Amount of memory (bytes) to allocate. It is valid to ask for
+ * 0 bytes, which will return a non-null pointer that is properly
+ * aligned and padded (but that you should not use).
*
- * This is used for things like std::list where the size of each link
- * is larger than the class stored in the link.
+ * \return Valid pointer if the allocation worked, otherwise nullptr.
*
- * Required by the specification for an allocator.
- */
- template <class U>
- struct rebind
- {
- typedef AlignedAllocator<U> other; //!< Align class U with our alignment
- };
-
- /*! \brief Templated copy constructor
+ * The memory will always be aligned to 128 bytes, which is our
+ * estimate of the longest cache lines on architectures currently in use.
+ * It will also be padded by the same amount at the end of the
+ * area, to help avoid false cache sharing.
*
- * This template constructor cannot be auto-generated, and is
- * normally unused, except e.g. MSVC2015 standard library uses
- * it in debug mode, presumably to implement some checks.
+ * \note Memory allocated with this routine must be released with
+ * gmx::AlignedAllocationPolicy::free(), and absolutely not the system free().
*/
- template <class U>
- explicit AlignedAllocator(const AlignedAllocator<U> &) {}
-
- /*! \brief Constructor
+ static void *
+ malloc(std::size_t bytes);
+ /*! \brief Free aligned memory
*
- * No constructor can be auto-generated in the presence of any
- * user-defined constructor, but we want the default constructor.
- */
- AlignedAllocator() {};
-
- /*! \brief Return address of an object
- *
- * \param r Reference to object of type T
- * \return Pointer to T memory
- */
- pointer
- address(reference r) const { return &r; }
-
- /*! \brief Return address of a const object
+ * \param p Memory pointer previously returned from malloc()
*
- * \param r Const reference to object of type T
- * \return Pointer to T memory
+ * \note This routine should only be called with pointers obtained from
+ * gmx::AlignedAllocationPolicy::malloc(), and absolutely not any
+ * pointers obtained the system malloc().
*/
- const_pointer
- address(const_reference r) const { return &r; }
-
- /*! \brief Do the actual memory allocation
- *
- * \param n Number of elements of type T to allocate. n can be
- * 0 bytes, which will return a non-null properly aligned
- * and padded pointer that should not be used.
- * \param hint Optional value returned from previous call to allocate.
- * For now this is not used.
- * \return Pointer to allocated memory
- *
- * \throws std::bad_alloc if the allocation fails.
- */
- pointer
- allocate(std::size_t n, typename std::allocator<void>::const_pointer gmx_unused hint = nullptr)
- {
- void *p = internal::alignedMalloc(n*sizeof(T));
+ static void
+ free(void *p);
+};
- if (p == nullptr)
- {
- throw std::bad_alloc();
- }
- else
- {
- return static_cast<pointer>(p);
- }
- }
+/*! \brief Aligned memory allocator.
+ *
+ * \tparam T Type of objects to allocate
+ *
+ * This convenience partial specialization can be used for the
+ * optional allocator template parameter in standard library
+ * containers, which is necessary e.g. to use SIMD aligned load and
+ * store operations on data in those containers. The memory will
+ * always be aligned according to the behavior of
+ * AlignedAllocationPolicy.
+ */
+template <class T>
+using AlignedAllocator = Allocator<T, AlignedAllocationPolicy>;
- /*! \brief Release memory
- *
- * \param p Pointer to previously allocated memory returned from allocate()
- * \param n number of objects previously passed to allocate()
- */
- void
- deallocate(pointer p, std::size_t gmx_unused n)
- {
- internal::alignedFree(p);
- }
- /*! \brief Construct an object without allocating memory
- *
- * \tparam Args Variable-length list of types for constructor args
- * \param p Adress of memory where to construct object
- * \param args Variable-length list of arguments to constructor
- */
- template<class ... Args>
- void
- construct(pointer p, Args && ... args) { ::new((void *)p)T(std::forward<Args>(args) ...); }
+/*! \brief Return the memory page size on this system
+ *
+ * Implements the "construct on first use" idiom to avoid the static
+ * initialization order fiasco where a possible static page-aligned
+ * container would be initialized before the alignment variable was.
+ *
+ * Note that thread-safety is guaranteed by the C++11 language
+ * standard. */
+std::size_t pageSize();
- /*! \brief Call the destructor of object without releasing memory
+/*! \libinternal \brief Policy class for configuring gmx::Allocator,
+ * to manage allocations of page-aligned memory that can be locked for
+ * asynchronous transfer to GPU devices.
+ */
+class PageAlignedAllocationPolicy
+{
+ public:
+ /*! \brief Return the alignment size of memory pages on this system.
*
- * \param p Address of memory where to destroy object
- */
- void
- destroy(pointer p) { p->~value_type(); }
-
- /*! \brief Return largest number of objects that can be allocated
+ * Guesses 4096 on Windows, otherwise queries sysconf. */
+ static std::size_t
+ alignment();
+ /*! \brief Allocate memory aligned to alignment() bytes.
*
- * This will be set such that the number of objects T multiplied by
- * the size of each object is the largest value that can be represented
- * by size_type.
- */
- std::size_t
- max_size() const { return (static_cast<size_t>(0) - static_cast<size_t>(1)) / sizeof(T); }
-
- /*! \brief Return true if two allocators are identical
+ * \param bytes Amount of memory (bytes) to allocate. It is valid to ask for
+ * 0 bytes, which will return a non-null pointer that is properly
+ * aligned and padded (but that you should not use).
*
- * \param rhs Other allocator
+ * \return Valid pointer if the allocation worked, otherwise nullptr.
*
- * This is a member function of the left-hand-side allocator.
+ * \note Memory allocated with this routine must be released with
+ * gmx::PageAlignedAllocationPolicy::free(), and absolutely not the system free().
*/
- template<class T2>
- bool
- operator==(const AlignedAllocator<T2> &gmx_unused rhs) const { return std::is_same<T, T2>::value; GMX_UNUSED_VALUE(rhs); }
-
- /*! \brief Return true if two allocators are different
+ static void *
+ malloc(std::size_t bytes);
+ /*! \brief Free aligned memory
*
- * \param rhs Other allocator.
+ * \param p Memory pointer previously returned from malloc()
*
- * This is a member function of the left-hand-side allocator.
+ * \note This routine should only be called with pointers obtained from
+ * gmx::PageAlignedAllocationPolicy::malloc(), and absolutely not any
+ * pointers obtained the system malloc().
*/
- bool
- operator!=(const AlignedAllocator &rhs) const { return !operator==(rhs); }
+ static void
+ free(void *p);
};
+/*! \brief PageAligned memory allocator.
+ *
+ * \tparam T Type of objects to allocate
+ *
+ * This convenience partial specialization can be used for the
+ * optional allocator template parameter in standard library
+ * containers, which is necessary for locking memory pages for
+ * asynchronous transfer between a GPU device and the host. The
+ * memory will always be aligned according to the behavior of
+ * PageAlignedAllocationPolicy.
+ */
+template <class T>
+using PageAlignedAllocator = Allocator<T, PageAlignedAllocationPolicy>;
+
} // namespace gmx
#endif // GMX_UTILITY_ALIGNEDALLOCATOR_H
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::Allocator template whose allocation functionality is
+ * configured both by type of object allocated and a policy class that
+ * configures the necessary matching malloc and free operation.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ALLOCATOR_H
+#define GMX_UTILITY_ALLOCATOR_H
+
+#include <cstddef>
+
+#include <memory>
+#include <new>
+
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Policy-based memory allocator.
+ *
+ * \tparam T Type of objects to allocate
+ * \tparam AllocationPolicy Policy of (matching) allocation and deallocation functions.
+ *
+ * This class can be used for the optional allocator template
+ * parameter in standard library containers. It must be configured
+ * with both the type of object to allocate, and an AllocationPolicy
+ * which effectively wraps a matching pair of malloc and free
+ * functions. This permits implementing a family of related allocators
+ * e.g. with SIMD alignment, GPU host-side page locking, or perhaps
+ * both, in a way that preserves a common programming interface and
+ * duplicates minimal code.
+
+ * \throws std::bad_alloc Instead of a GROMACS exception object, we
+ * throw the standard one on allocation failures to make it as
+ * compatible as possible with the errors expected by code using the
+ * standard library containers.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+template <class T, typename AllocationPolicy>
+class Allocator
+{
+ public:
+ // The standard library specification for a custom allocator
+ // requires these typedefs, with this capitalization/underscoring.
+ typedef T value_type; //!< Type of allocated elements
+ typedef T &reference; //!< Reference to allocated elements
+ typedef const T &const_reference; //!< Constant reference to allocated elements
+ typedef T * pointer; //!< Pointer to allocated elements
+ typedef const T * const_pointer; //!< Constant pointer to allocated elements
+ typedef std::size_t size_type; //!< Integer type to use for size of objects
+ typedef std::ptrdiff_t difference_type; //!< Type to hold differences between pointers
+
+ // This typedef is required by GROMACS for testing and assertions
+ typedef AllocationPolicy allocation_policy; //!< Type of the AllocationPolicy
+
+ /*! \libinternal \brief Standard-required typedef to use allocator with different class.
+ *
+ * \tparam U new class
+ *
+ * This is used for things like std::list where the size of each link
+ * is larger than the class stored in the link.
+ *
+ * Required by the specification for an allocator.
+ */
+ template <class U>
+ struct rebind
+ {
+ typedef Allocator<U, AllocationPolicy> other; //!< Align class U with our alignment
+ };
+
+ /*! \brief Templated copy constructor
+ *
+ * This template constructor cannot be auto-generated, and is
+ * normally unused, except e.g. MSVC2015 standard library uses
+ * it in debug mode, presumably to implement some checks.
+ */
+ template <class U>
+ explicit Allocator(const Allocator<U, AllocationPolicy> &) {}
+
+ /*! \brief Constructor
+ *
+ * No constructor can be auto-generated in the presence of any
+ * user-defined constructor, but we want the default constructor.
+ */
+ Allocator() {};
+
+ /*! \brief Return address of an object
+ *
+ * \param r Reference to object of type T
+ * \return Pointer to T memory
+ */
+ pointer
+ address(reference r) const { return &r; }
+
+ /*! \brief Return address of a const object
+ *
+ * \param r Const reference to object of type T
+ * \return Pointer to T memory
+ */
+ const_pointer
+ address(const_reference r) const { return &r; }
+
+ /*! \brief Do the actual memory allocation
+ *
+ * \param n Number of elements of type T to allocate. n can be
+ * 0 bytes, which will return a non-null properly aligned
+ * and padded pointer that should not be used.
+ * \param hint Optional value returned from previous call to allocate.
+ * For now this is not used.
+ * \return Pointer to allocated memory
+ *
+ * \throws std::bad_alloc if the allocation fails.
+ */
+ pointer
+ allocate(std::size_t n, typename std::allocator<void>::const_pointer gmx_unused hint = nullptr)
+ {
+ void *p = AllocationPolicy::malloc(n*sizeof(T));
+
+ if (p == nullptr)
+ {
+ throw std::bad_alloc();
+ }
+ else
+ {
+ return static_cast<pointer>(p);
+ }
+ }
+
+ /*! \brief Release memory
+ *
+ * \param p Pointer to previously allocated memory returned from allocate()
+ * \param n number of objects previously passed to allocate()
+ */
+ void
+ deallocate(pointer p, std::size_t gmx_unused n)
+ {
+ AllocationPolicy::free(p);
+ }
+
+ /*! \brief Construct an object without allocating memory
+ *
+ * \tparam Args Variable-length list of types for constructor args
+ * \param p Adress of memory where to construct object
+ * \param args Variable-length list of arguments to constructor
+ */
+ template<class ... Args>
+ void
+ construct(pointer p, Args && ... args) { ::new((void *)p)T(std::forward<Args>(args) ...); }
+
+ /*! \brief Call the destructor of object without releasing memory
+ *
+ * \param p Address of memory where to destroy object
+ */
+ void
+ destroy(pointer p) { p->~value_type(); }
+
+ /*! \brief Return largest number of objects that can be allocated
+ *
+ * This will be set such that the number of objects T multiplied by
+ * the size of each object is the largest value that can be represented
+ * by size_type.
+ */
+ std::size_t
+ max_size() const { return (static_cast<size_t>(0) - static_cast<size_t>(1)) / sizeof(T); }
+
+ /*! \brief Return true if two allocators are identical
+ *
+ * This is a member function of the left-hand-side allocator.
+ */
+ template<class T2>
+ bool
+ operator==(const Allocator<T2, AllocationPolicy> &) const { return std::is_same<T, T2>::value; }
+
+ /*! \brief Return true if two allocators are different
+ *
+ * \param rhs Other allocator.
+ *
+ * This is a member function of the left-hand-side allocator.
+ */
+ bool
+ operator!=(const Allocator &rhs) const { return !operator==(rhs); }
+};
+
+} // namespace gmx
+
+#endif
"Cannot allocate aligned memory with alignment of zero!\n(called from file %s, line %d)", file, line);
}
- // Our new alignedMalloc always returns 128-byte aligned memory.
- if (alignment > 128)
+ size_t alignmentSize = gmx::AlignedAllocationPolicy::alignment();
+ if (alignment > alignmentSize)
{
gmx_fatal(errno, __FILE__, __LINE__,
- "Cannot allocate aligned memory with alignment > 128 bytes\n(called from file %s, line %d)", file, line);
+ "Cannot allocate aligned memory with alignment > %u bytes\n(called from file %s, line %d)", alignmentSize, file, line);
}
}
#endif
- p = gmx::internal::alignedMalloc(nelem*elsize);
+ p = gmx::AlignedAllocationPolicy::malloc(nelem*elsize);
if (p == nullptr)
{
/* This routine can NOT be called with any pointer */
void save_free_aligned(const char gmx_unused *name, const char gmx_unused *file, int gmx_unused line, void *ptr)
{
- gmx::internal::alignedFree(ptr);
+ gmx::AlignedAllocationPolicy::free(ptr);
}
void set_over_alloc_dd(gmx_bool set)
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2015, by the GROMACS development team, led by
+ * Copyright (c) 2015,2017, 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.
* \brief Tests for gmx::AlignedAllocator
*
* \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
* \ingroup module_utility
*/
#include "gromacs/utility/alignedallocator.h"
-#include <list>
#include <vector>
#include <gtest/gtest.h>
+#include "gromacs/math/vectypes.h"
#include "gromacs/utility/real.h"
namespace gmx
{
-TEST(AlignedAllocatorTest, AllocatorAlign)
+//! Templated test fixture.
+template <typename T>
+class AllocatorTest : public ::testing::Test
{
- AlignedAllocator<real> a;
- real * p = a.allocate(1000);
+ public:
+ /*! \brief Bitmask for testing the alignment.
+ *
+ * e.g. for 128-byte alignment the mask is 128-1 - all of
+ * these bits should be zero in pointers that have the
+ * intended alignment. */
+ std::size_t mask_ = T::allocation_policy::alignment()-1;
+};
- // Mask for 128-byte alignment is 128-1 - these bits should be zero in p
- std::size_t mask = static_cast<std::size_t>(128-1);
+//! Declare allocator types to test.
+using AllocatorTypesToTest = ::testing::Types<AlignedAllocator<real>,
+ PageAlignedAllocator<real>,
+ AlignedAllocator<int>,
+ PageAlignedAllocator<int>,
+ AlignedAllocator<RVec>,
+ PageAlignedAllocator<RVec>
+ >;
+TYPED_TEST_CASE(AllocatorTest, AllocatorTypesToTest);
- EXPECT_EQ(0, reinterpret_cast<std::size_t>(p) & mask);
+// NB need to use this->mask_ because of GoogleTest quirks
+
+TYPED_TEST(AllocatorTest, AllocatorAlignAllocatesWithAlignment)
+{
+ using pointer = typename TypeParam::pointer;
+ TypeParam a;
+ pointer p = a.allocate(1000);
+
+ EXPECT_EQ(0, reinterpret_cast<std::size_t>(p) & this->mask_);
a.deallocate(p, 1000);
}
-TEST(AlignedAllocator, Vector)
+TYPED_TEST(AllocatorTest, VectorAllocatesAndResizesWithAlignment)
{
- // Mask for 128-byte alignment is 128-1 - these bits should be zero in pointers
- std::size_t mask = static_cast<std::size_t>(128-1);
+ using value_type = typename TypeParam::value_type;
+ std::vector<value_type, TypeParam> v(10);
+ EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask_);
- std::vector<real, AlignedAllocator<real> > v(10);
- EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & mask);
-
- for (std::size_t i = 10000; i <= 100000; i += 10000)
+ // Reserve a few times to check things work ok, making sure we
+ // will trigger several reallocations on common vector
+ // implementations.
+ for (std::size_t i = 1000; i <= 10000; i += 1000)
{
v.resize(i);
- EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & mask);
+ EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask_);
}
}
+TYPED_TEST(AllocatorTest, VectorAllocatesAndReservesWithAlignment)
+{
+ using value_type = typename TypeParam::value_type;
+ std::vector<value_type, TypeParam> v(10);
+ EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask_);
+
+ // Reserve a few times to check things work ok, making sure we
+ // will trigger several reallocations on common vector
+ // implementations.
+ for (std::size_t i = 1000; i <= 10000; i += 1000)
+ {
+ v.reserve(i);
+ EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask_);
+ }
+}
}