在GCC中针对英特尔x64的指针对齐

时间:2016-09-02 21:07:53

标签: c++ pointers alignment

我需要从一些内存位置添加几个四字:

uint64_t sum2 (const char * p, size_t n)
{
    uint64_t res = 0;
    const uint32_t * q = (const uint32_t*) p;
    size_t i;
    for (i = 0; i < n; i++) res += q[i];
    return res;
}

我知道这段代码不必在任意机器上处理任意C编译器。并非每个指向char的指针都可以转换为指向int的有效指针。但是,在Intel上,您可以从任何地址读取32位值,在大多数情况下即使没有任何性能损失,因此无论p的对齐方式如何,此代码都可以正常工作。我的程序运行在64位Intel Sandy Bridge上,使用GCC 4.8使用-msse4.2 -O3编译。

当地址不是4对齐时,此代码SIGSEGV。原因是循环展开四次并使用SSE编译。使用MOVDQA一起读取四个值,这需要对齐16。在循环之前,指针对齐16,前提是它已经对齐了4。

如何在GCC上阻止此SSE优化?我真的需要添加错位的32位数字。

3 个答案:

答案 0 :(得分:1)

它可能会有性能损失,但我认为您需要使用memcpy复制到正确对齐的临时版。

uint64_t sum2 (const char * p, size_t n)
{
    uint64_t res = 0, temp;
    const uint32_t * q = (const uint32_t*) p;
    size_t i;
    for (i = 0; i < n; i++) {
        memcpy(&temp, &q[i], sizeof(*q));
        res += temp;
    }
    return res;
}

希望它没有调整q&q[i]。如果是这种情况,您需要自己进行地址算法。

uint64_t sum2 (const char * p, size_t n)
{
    uint64_t res = 0, temp;
    size_t i;
    for (i = 0; i < n; i++, p += sizeof(uint32_t)) {
        memcpy(&temp, p, sizeof(uint32_t));
        res += temp;
    }
    return res;
}

答案 1 :(得分:1)

您可以有选择地控制特定功能的优化和代码生成,请参阅 https://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-Pragmas.html

https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html

特别:

__attribute__ ((target("no-sse2")))
uint64_t sum2 (const char * p, size_t n)
{
    uint64_t res = 0;
    const uint32_t * q = (const uint32_t*) p;
    size_t i;
    for (i = 0; i < n; i++) res += q[i];
    return res;
}

答案 2 :(得分:1)

以符合标准的方式,两全其美?

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>



uint64_t sum2_unaligned (const char * p, size_t n)
{
    uint64_t res = 0, temp;
    const uint32_t * q = (const uint32_t*) p;
    size_t i;
    for (i = 0; i < n; i++) {
        memcpy(&temp, &q[i], sizeof(*q));
        res += temp;
    }
    return res;
}

uint64_t sum2_aligned (const std::uint64_t * p, size_t n)
{
    uint64_t res = 0, temp;
    const uint32_t * q = (const uint32_t*) p;
    size_t i;
    for (i = 0; i < n; i++) {
        res += p[i];
    }
    return res;
}

uint64_t sum2 (const char* p, size_t n)
{
  constexpr auto alignment = alignof(std::uint64_t);
  void* mem = const_cast<void*>(reinterpret_cast<const void*>(p));
  std::size_t space = n * sizeof(std::uint64_t);
  auto mem2 = std::align(alignment, space, mem, space);
  if (mem2 != mem)
  {
    return sum2_unaligned(p, n);
  }
  else
  {
    return sum2_aligned(reinterpret_cast<std::uint64_t const*>(p), n);
  }
}