相关疑难解决方法(0)

字对齐加载比x64处理器上的未对齐加载更快吗?

在x86/64(Intel/AMD 64位)处理器上,在字边界上对齐的变量是否比未对齐的加载操作更快?

我的一位同事辩称,未对齐的载荷很慢,应该避免.他引用了项目填充到结构中的单词边界,作为未对齐加载缓慢的证明.例:

struct A {
  char a;
  uint64_t b;
};
Run Code Online (Sandbox Code Playgroud)

结构A通常大小为16个字节.

另一方面,Snappy压缩器文档指出Snappy假设"未对齐的32位和64位加载和存储很便宜".根据源代码,英特尔32和64位处理器也是如此.

那么:这里的真相是什么?如果和未对齐的载荷减少多少?在哪种情况下?

c x86-64 alignment

6
推荐指数
3
解决办法
4822
查看次数

在C/C++中对齐特定地址边界的内存是否仍能提高x86的性能?

许多低延迟开发指南讨论了在特定地址边界上对齐内存分配:

https://github.com/real-logic/simple-binary-encoding/wiki/Design-Principles#word-aligned-access

http://www.alexonlinux.com/aligned-vs-unaligned-memory-access

但是,第二个链接是从2008年开始的.在2019年,在地址边界上调整内存是否仍能提高英特尔CPU的性能?我认为英特尔CPU不再会因访问未对齐地址而导致延迟损失?如果没有,在什么情况下应该这样做?我应该对齐每个堆栈变量吗?类成员变量?

有没有人有任何例子表明他们在调整内存方面取得了显着的性能提升?

c c++ performance x86 latency

6
推荐指数
1
解决办法
415
查看次数

使用-O3时确定段错误的原因?

-O3使用GCC 4.8/4.9/5.1 编译程序时,我无法确定段错误的原因.对于GCC 4.9.x,我在Cygwin,Debian 8(x64)和Fedora 21(x64)上看过它.其他人在GCC 4.8和5.1上体验过它.

该程序-O2很好,其他版本的GCC很好,在其他编译器(如MSVC,ICC和Clang)下很好.

下面是GDB下的崩溃,但没有任何事情发生在我身上.源代码misc.cpp:26如下,但它是一个简单的异或:

((word64*)buf)[i] ^= ((word64*)mask)[i];
Run Code Online (Sandbox Code Playgroud)

有问题的代码在演员表之前检查64位字对齐.从反汇编中-O3,我知道它与vmovdqa指令有关:

(gdb) disass 0x0000000000539fc3
...

   0x0000000000539fbc <+220>:   vxorps 0x0(%r13,%r10,1),%ymm0,%ymm0
=> 0x0000000000539fc3 <+227>:   vmovdqa %ymm0,0x0(%r13,%r10,1)
   0x0000000000539fca <+234>:   add    $0x20,%r10
Run Code Online (Sandbox Code Playgroud)

看起来GCC正在使用SSE向量-O3,而不是使用它们-O2.(感谢亚历杭德罗提出的建议).

我会天真地问:vmovdqa对齐要求是否大于64位字?是这样,为什么GCC在单词不是128位对齐时选择它?

是什么导致了这里的段错误?我该如何进一步排除故障?


另请参阅错误66852 - 在64位对齐阵列上发出的vmovdqa指令,导致segfault.它是针对这个问题提交的,所以目前尚未证实.


$ gdb ./cryptest.exe 
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
...
(gdb) r v
...
Testing MessageDigest algorithm SHA-3-224.
.....
Program received signal SIGSEGV, …
Run Code Online (Sandbox Code Playgroud)

c++ gdb sse gcc4.9

5
推荐指数
1
解决办法
1325
查看次数

循环向量化 - 使用掩码对 7 字节记录的匹配进行计数

我有一个相当简单的循环:

auto indexRecord = getRowPointer(0);
bool equals;
// recordCount is about 6 000 000
for (int i = 0; i < recordCount; ++i) {
    equals = BitString::equals(SelectMask, indexRecord, maxBytesValue);
    rowsFound += equals;
    indexRecord += byteSize; // byteSize is 7
}
Run Code Online (Sandbox Code Playgroud)

哪里BitString::equals

static inline bool equals(const char * mask, const char * record, uint64_t maxVal) {
    return !(((*( uint64_t * ) mask) & (maxVal & *( uint64_t * ) record)) ^ (maxVal & *( uint64_t * ) record));
} …
Run Code Online (Sandbox Code Playgroud)

c++ gcc simd vectorization bitmap-index

5
推荐指数
1
解决办法
252
查看次数

Clang++:为什么在添加另一个结构成员时这个 memcpy 循环惯用法没有优化?

鉴于此代码片段

#include <cstdint>
#include <cstddef>

struct Data {
  uint64_t a;
  //uint64_t b;
};

void foo(
    void* __restrict data_out,
    uint64_t* __restrict count_out,
    std::byte* __restrict data_in,
    uint64_t count_in)
{
  for(uint64_t i = 0; i < count_in; ++i) {
    Data value = *reinterpret_cast<Data* __restrict>(data_in + sizeof(Data) * i);
    static_cast<Data* __restrict>(data_out)[(*count_out)++] = value;
  }
}
Run Code Online (Sandbox Code Playgroud)

clang 用 memcpy 调用替换循环foo,正如预期的那样 ( godbolt ),给出 Rpass 输出:

example.cpp:16:59: remark: Formed a call to llvm.memcpy.p0.p0.i64() intrinsic from load and store instruction in _Z3fooPvPmPSt4bytem function [-Rpass=loop-idiom]
    static_cast<Data* …
Run Code Online (Sandbox Code Playgroud)

c++ clang compiler-optimization

5
推荐指数
0
解决办法
249
查看次数

是什么阻止编译器优化手写的 memcmp()?

鉴于:

#include <string.h>

bool test_data(void *data)
{
    return memcmp(data, "abcd", 4) == 0;
}
Run Code Online (Sandbox Code Playgroud)

编译器可以将其优化为:

test_data:
    cmpl    $1684234849, (%rdi)
    sete    %al
    ret
Run Code Online (Sandbox Code Playgroud)

这很好。

但如果我使用我自己的memcmp()(而不是来自<string.h>),编译器无法将其优化为单个cmpl指令。相反,它这样做:

test_data:
    cmpl    $1684234849, (%rdi)
    sete    %al
    ret
Run Code Online (Sandbox Code Playgroud)
test_data:
    cmpb    $97, (%rdi)
    jne     .L5
    cmpb    $98, 1(%rdi)
    jne     .L5
    cmpb    $99, 2(%rdi)
    jne     .L5
    cmpb    $100, 3(%rdi)
    sete    %al
    ret
.L5:
    xorl    %eax, %eax
    ret
Run Code Online (Sandbox Code Playgroud)

链接: https: //godbolt.org/z/Kfhchr45a

  • 是什么阻止编译器进一步优化它?
  • 我是否做了一些阻碍优化的事情?

c optimization assembly x86-64 memcmp

5
推荐指数
2
解决办法
333
查看次数

std::atomic_ref 需要对齐

根据cppreference,硬件可能要求 an 引用的对象atomic_ref<T>比其他T对象具有更严格的对齐方式,并且 an 上的操作是否atomic_ref是无锁的可以取决于引用对象的对齐方式。

为什么只需要引用的对象具有atomic_ref适当的对齐方式,而std::atomic似乎没有强加这一要求?

c++ atomic memory-alignment stdatomic c++20

4
推荐指数
1
解决办法
288
查看次数

是否可以通过溢出C中的第一个元素来写入数组的第二个元素?

在低级语言中,有可能mov是第一个数组元素的双字(32位),它将溢出写入第二个,第三个和第四个元素,或者mov一个字(16位)到第一个,它将溢出到第二个元件.

如何在c中实现同样的效果?当试图例如:

char txt[] = {0, 0};
txt[0] = 0x4142;
Run Code Online (Sandbox Code Playgroud)

它发出警告 [-Woverflow]

并且值txt[1] 不会改变txt[0]设置为0x42.

如何获得与汇编相同的行为:

mov word [txt], 0x4142

先前的汇编指令将设置在第一元件[txt+0]0x42第二元件[txt+1]0x41.

编辑

这个建议怎么样?

将数组定义为单个变量.

uint16_t txt;
txt = 0x4142;
Run Code Online (Sandbox Code Playgroud)

并使用((uint8_t*) &txt)[0]第一个元素和((uint8_t*) &txt)[1]第二个元素访问元素.

c c++ assembly nasm

3
推荐指数
1
解决办法
185
查看次数

GCC 中的 `movaps` 与 `movups`:它是如何决定的?

我最近研究了一个用GCC 8编译的软件中的segfault。代码如下(这只是一个草图)

struct Point
{
  int64_t x, y;
};

struct Edge
{
  // some other fields
  // ...
  Point p; // <- at offset `0xC0`

  Edge(const Point &p) p(p) {}
};

Edge *create_edge(const Point &p)
{
  void *raw_memory = my_custom_allocator(sizeof(Edge));
  return new (raw_memory) Edge(p);
}
Run Code Online (Sandbox Code Playgroud)

这里的关键点是my_custom_allocator()返回指向未对齐内存的指针。代码崩溃是因为为了将原始点复制pEdge::p新对象的字段中,编译器在 [内联] 构造函数代码中使用了movdqu/movaps

movdqu 0x0(%rbp), %xmm1  ; read the original object at `rbp`
...
movaps %xmm1, 0xc0(%rbx) ; store it into the new `Edge` object …
Run Code Online (Sandbox Code Playgroud)

c++ optimization x86 gcc memory-alignment

3
推荐指数
1
解决办法
1802
查看次数

为什么跨缓存行边界的变量上的原子存储会编译为普通的 MOV 存储指令?

我们看一下代码

#include <stdint.h>
#pragma pack (push,1)
typedef struct test_s
{
    uint64_t a1;
    uint64_t a2;
    uint64_t a3;
    uint64_t a4;
    uint64_t a5;
    uint64_t a6;
    uint64_t a7;
    uint8_t b1;
    uint64_t a8;
}test;

int main()
{
    test t;
    __atomic_store_n(&(t.a8), 1, __ATOMIC_RELAXED);
}
Run Code Online (Sandbox Code Playgroud)

由于我们有打包结构,a8 不是自然对齐的,也应该在不同的 64 字节缓存边界之间分割,但生成的程序集 GCC 12.2 是

main:
        push    rbp
        mov     rbp, rsp
        mov     eax, 1
        mov     QWORD PTR [rbp-23], rax
        mov     eax, 0
        pop     rbp
        ret
Run Code Online (Sandbox Code Playgroud)

为什么它翻译成简单的MOV?在这种情况下 MOV 不是原子的吗?

添加:clang 16 调用原子函数的相同代码并转换为

main:                                   # @main
        push    rbp
        mov     rbp, rsp
        sub …
Run Code Online (Sandbox Code Playgroud)

c x86 gcc atomic memory-alignment

3
推荐指数
1
解决办法
239
查看次数