SSE / AVX 对齐内存上的 valarray

Posted

技术标签:

【中文标题】SSE / AVX 对齐内存上的 valarray【英文标题】:valarray on aligned memory for SSE / AVX 【发布时间】:2012-12-04 20:49:32 【问题描述】:

有没有办法确保valarray 使用对齐的内存,以便可以使用 SSE 和 AVX 对其进行矢量化?据我所知,STL 不保证对齐,您可以将分配器传递给 valarray。还有其他方法可以实现吗?

先谢谢了!

【问题讨论】:

使用 SSE 实现定义的类型有什么问题吗? @BenVoigt alignas 只能用于变量或数据成员,不能用于类型。 @ecatmur:我在返回之前检查了语法以支持您的答案,此时我被删除了。 type-id 可以有一个 abstract-declarator,它可以是一个 ptr-abstract-declarator,它可以是 noptr-abstract-声明符...哦,但是attribute-specifier-seq只有在括号存在时才被允许。 【参考方案1】:

我通常将std::vector 与我自己的分配器一起使用,该分配器将对齐作为模板参数并调用_mm_malloc()_aligned_malloc()。这很好用,也适用于 AVX(32 字节对齐)。适当编写的模板化用户代码会自动选择所需的对齐方式。

AlignmentAllocator<> 和助手的代码下方。在 gcc 和 icpc 下测试。

/// allocate and de-allocate aligned memory
template<std::size_t alignment>
struct static_allocator 
  static void*allocate(std::size_t n)
  
    if(n == 0) return 0;
    if(n > max_size())
      throw std::bad_alloc();
    void*ret =
#if defined(__GNUC__) || defined (__INTEL_COMPILER)
      _mm_malloc
#else
      _aligned_malloc
#endif
      (n,alignment);
    if(!ret)
      throw std::bad_alloc();
    return ret;
  
  static void deallocate(void*p)
  
#if defined(__GNUC__) || defined (__INTEL_COMPILER)
    _mm_free
#else
    _aligned_free
#endif
    (p);
  
  static std::size_t max_size ()
   return std::numeric_limits<std::size_t>::max(); 
;

/// allocate and de-allocate unaligned memory
template<>
struct static_allocator<1> 
  static std::size_t max_size () noexcept
   return std::numeric_limits<std::size_t>::max(); 
  static void*allocate(std::size_t n)
   
    if(n == 0) return 0;
    void*ret = new char[n];
    return ret;
  
  static void deallocate(void*p)
   delete[] static_cast<char*>(p); 
;

template<> struct static_allocator<0>;

/// allocator with explicit alignment
template<typename _Tp, std::size_t alignment = 16>
class AlignmentAllocator

  typedef static_allocator<alignment> static_alloc;
public:
  typedef size_t     size_type;
  typedef ptrdiff_t  difference_type;
  typedef _Tp*       pointer;
  typedef const _Tp* const_pointer;
  typedef _Tp&       reference;
  typedef const _Tp& const_reference;
  typedef _Tp        value_type;

  template <typename _Tp1>
  struct rebind
   typedef AlignmentAllocator<_Tp1, alignment> other; ;

  AlignmentAllocator() 

  AlignmentAllocator(const AlignmentAllocator&) 

  template <typename _Tp1>
  AlignmentAllocator(const AlignmentAllocator<_Tp1, alignment> &) 

  ~AlignmentAllocator() 

  pointer address (reference x) const
  
#if __cplusplus >= 201103L
    return std::addressof(x);
#else
    return reinterpret_cast<_Tp*>(&reinterpret_cast<char&>(x));
#endif
  

  const_pointer address (const_reference x) const
  
#if __cplusplus >= 201103L
    return std::addressof(x);
#else
    return reinterpret_cast<const _Tp*>(&reinterpret_cast<const char&>(x));
#endif
  

  pointer allocate (size_type n, const void* = 0)
   return static_cast<pointer>(static_alloc::allocate(n*sizeof(value_type))); 

  void deallocate (pointer p, size_type)
   static_alloc::deallocate(p); 

  size_type max_size () const
   return static_alloc::max_size() / sizeof (value_type); 

#if __cplusplus >= 201103L

  template<typename _Up, typename... _Args>
  void construct(_Up* p, _Args&&... args)
   ::new(static_cast<void*>(p)) _Up(std::forward<_Args>(args)...); 

  template<typename _Up>
  void destroy(_Up* p)
   p->~_Up(); 

#else

  void construct (pointer p, const_reference val)
   ::new(static_cast<void*>(p)) value_type(val); 

  void destroy (pointer p)
   p->~value_type (); 

#endif

  bool operator!=(const AlignmentAllocator&) const 
   return false; 

  // Returns true if and only if storage allocated from *this
  // can be deallocated from other, and vice versa.
  // Always returns true for stateless allocators.
  bool operator==(const AlignmentAllocator&) const 
   return true; 

;// class AlignmentAllocator<>

/// AlignmentAllocator<void> specialization.
template<std::size_t alignment>
class AlignmentAllocator<void, alignment>

public:
  typedef size_t      size_type;
  typedef ptrdiff_t   difference_type;
  typedef void*       pointer;
  typedef const void* const_pointer;
  typedef void        value_type;

  template<typename _Tp1>
  struct rebind
   typedef AlignmentAllocator<_Tp1, alignment> other; ;
;

【讨论】:

很好,但是据我所知,valarray 不能传递分配器呢?? 我以为我很清楚:不要在这里使用std::valarray,而是其他的,例如std::vector

以上是关于SSE / AVX 对齐内存上的 valarray的主要内容,如果未能解决你的问题,请参考以下文章

SSE向量化与内存对齐的关系

使用 SSE、AVX 和 OpenMP 进行快速内存转置

C/C++获得对齐的内存的跨平台解决方案

C/C++获得对齐的内存的跨平台解决方案

AVX 或 SSE 上的水平尾随最大值

强制 AVX 内部函数改为使用 SSE 指令