我需要从一些内存位置添加几个四字:
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位数字。
答案 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);
}
}