相关疑难解决方法(0)

现代x86硬件可以不将单个字节存储到内存中吗?

说到C++的并发内存模型,Stroustrup的C++编程语言,第4版,第1节.41.2.1,说:

...(像大多数现代硬件一样)机器无法加载或存储任何小于单词的东西.

但是,我的x86处理器,几年前,可以存储小于一个字的对象.例如:

#include <iostream>
int main()
{
    char a =  5;
    char b = 25;
    a = b;
    std::cout << int(a) << "\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果没有优化,GCC将其编译为:

        [...]
        movb    $5, -1(%rbp)   # a =  5, one byte
        movb    $25, -2(%rbp)  # b = 25, one byte
        movzbl  -2(%rbp), %eax # load b, one byte, not extending the sign
        movb    %al, -1(%rbp)  # a =  b, one byte
        [...]
Run Code Online (Sandbox Code Playgroud)

评论是由我提出的,但是汇编是由GCC提出的.当然,它运行良好.

显然,我不明白Stroustrup在谈到硬件可以加载和存储任何小于一个单词的内容时所说的内容.据我所知,我的计划什么也不做,但加载和存储对象小于一个字的.

C++对零成本,硬件友好的抽象的彻底关注使C++与其他易于掌握的编程语言区别开来.因此,如果Stroustrup在公交车上有一个有趣的信号心理模型,或者有其他类似的东西,那么我想了解Stroustrup的模型.

什么 Stroustrup谈论,拜托?

更长时间的背景声明 …

c++ concurrency x86 assembly memory-model

25
推荐指数
2
解决办法
1869
查看次数

如何解释 Xeon 处理器在具有顺序复制和分散存储的循环中性能不佳?

c++在某些英特尔至强处理器上运行以下代码时,我偶然发现了一个特殊的性能问题:

// array_a contains permutation of [0, n - 1]
// array_b and inverse are initialized arrays
for (int i = 0; i < n; ++i) {
  array_b[i] = array_a[i];
  inverse[array_b[i]] = i;
}
Run Code Online (Sandbox Code Playgroud)

循环的第一行按顺序复制array_aarray_b(预期很少有缓存未命中)。第二行计算array_b(许多缓存未命中,因为array_b是随机排列)的倒数。我们也可以将代码分成两个单独的循环:

for (int i = 0; i < n; ++i)
  array_b[i] = array_a[i];
for (int i = 0; i < n; ++i)
  inverse[array_b[i]] = i;
Run Code Online (Sandbox Code Playgroud)

我原以为这两个版本(单循环与双循环)在相对现代的硬件上的性能几乎相同。但是,在执行单循环版本时,某些 Xeon 处理器似乎非常慢。

您可以在下方看到以纳秒为单位n的挂机时间除以在一系列不同处理器上运行代码段的时间。出于测试目的,代码是使用 GCC 7.5.0 编译的,并-O3 -funroll-loops -march=native …

performance intel cpu-architecture cpu-cache amd-processor

14
推荐指数
1
解决办法
408
查看次数

是什么特意将x86缓存行标记为脏 - 任何写入,或者是否需要显式更改?

这个问题专门针对现代x86-64缓存一致性架构 - 我很欣赏其他CPU的答案可能会有所不同.

如果我写入内存,MESI协议要求首先将缓存行读入缓存,然后在缓存中进行修改(将值写入缓存行,然后将其标记为脏).在较旧的写入微架构中,这将触发高速缓存行被刷新,在写回期间,被刷新的高速缓存行可能会延迟一段时间,并且一些写入组合可能在两种机制下发生(更可能是回写) .我知道这与访问相同缓存行数据的其他核心如何交互 - 缓存监听等.

我的问题是,如果商店恰好匹配缓存中已有的值,如果没有单个位被翻转,那么任何英特尔微架构都会注意到这一点并且将该行标记为脏,从而可能将该行标记为独占,以及在某些时候跟随的回写内存开销?

当我向更多的循环进行矢量化时,我的矢量化操作组合基元不会明确地检查值的变化,并且在CPU/ALU中这样做似乎很浪费,但我想知道底层缓存电路是否可以在没有显式编码的情况下完成(例如,商店微操作或缓存逻辑本身).由于跨多个内核的共享内存带宽变得更加成为资源瓶颈,这似乎是一种越来越有用的优化(例如,重复调整相同的内存缓冲区 - 如果它们已经存在,我们不会重新读取RAM中的值在缓存中,但强制写回相同的值似乎很浪费).回写缓存本身就是对这类问题的承认.

我可以礼貌地要求阻止"在理论上"或"它确实无关紧要"的答案 - 我知道记忆模型是如何工作的,我正在寻找的是关于如何写出相同价值的硬性事实(而不是避免一个商店)将影响内存总线的争用你可以安全地假设是一台运行多个工作负载的机器几乎总是受内存带宽限制.另一方面,解释为什么芯片不这样做的确切原因(我悲观地假设他们没有这样做)将具有启发性......

更新: 这里的预期线路上的一些答案https://softwareengineering.stackexchange.com/questions/302705/are-there-cpus-that-perform-this-possible-l1-cache-write-optimization但仍然很多推测"它必须很难,因为它没有完成",并说如何在主CPU核心中这样做会很昂贵(但我仍然想知道为什么它不能成为实际缓存逻辑本身的一部分).

x86 x86-64 cpu-architecture cpu-cache memory-bandwidth

8
推荐指数
3
解决办法
577
查看次数

次优高速缓存行预取的成本

使用__builtin_prefetch(..., 1)内部函数(准备写入时的预取)完成后期预取的成本是多少?也就是说,在需求加载或写入需要它之前没有到达L1缓存的预取?

例如

void foo(std::uint8_t* line) {
    __builtin_prefetch(line + std::hardware_constructive_interference_size, 1);
    auto next_line = calculate_address_of_next_line(line);
    auto result = transform(line);
    write(next_line, result)
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果成本transform低于预取,那么这个代码最终会比没有预取的效率低吗?关于缓存预取的维基百科文章讨论了for循环的最佳步幅,但未提及该场景中次优预取的影响(例如,如果k太低会发生什么?).

这是否足够流水线以至于次优预取无关紧要?出于这个问题的目的,我只考虑Intel x86(Broadwell时代的处理器).

c++ performance x86 assembly prefetch

8
推荐指数
1
解决办法
177
查看次数

英特尔硬件上的商店缓冲区大小?什么是商店缓冲区?

英特尔优化手册似乎对存储缓冲区的数量存在于处理器的许多地方,但谈判没有谈存储缓冲区的大小.这是公共信息还是商店缓冲区的大小保留为微架构细节?

我正在研究的处理器主要是Broadwell和Skylake,但其他人的信息也不错.

另外,存储缓冲区究竟做了什么?

performance x86 assembly intel cpu-architecture

8
推荐指数
1
解决办法
717
查看次数

For循环效率:合并循环

我一直有这个想法,减少迭代次数是方式来使程序更加高效.由于我从未真正确认过,我开始测试这个.

我制作了以下C++程序来测量两个不同函数的时间:

  • 第一个函数执行单个大循环并使用一组变量.
  • 第二个函数执行多个同样大的循环,但每个变量只有一个循环.

完整的测试代码:

    #include <iostream>
    #include <chrono>

    using namespace std;

    int* list1; int* list2;
    int* list3; int* list4;
    int* list5; int* list6;
    int* list7; int* list8;
    int* list9; int* list10;

    const int n = 1e7;

    // **************************************
    void myFunc1()
    {
        for (int i = 0; i < n; i++)
        {
            list1[i] = 2;
            list2[i] = 4;
            list3[i] = 8;
            list4[i] = 16;
            list5[i] = 32;
            list6[i] = 64;
            list7[i] = 128;
            list8[i] = 256;
            list9[i] = …
Run Code Online (Sandbox Code Playgroud)

c++ performance benchmarking loops

7
推荐指数
2
解决办法
851
查看次数

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

L1 缓存控制器处理来自 CPU 的内存请求的顺序

在总存储顺序 (TSO) 内存一致性模型下,x86 cpu 将有一个写入缓冲区来缓冲写入请求,并且可以为来自写入缓冲区的重新排序的读取请求提供服务。并且它说写缓冲区中的写请求将退出并以FIFO顺序向缓存层次结构发出,这与程序顺序相同。

我很好奇:

为了服务从写缓冲区发出的写请求,一级缓存控制器是否处理写请求,完成写请求的缓存一致性,并按照与发出顺序相同的顺序将数据插入一级缓存?

hardware x86 cpu-architecture memory-barriers cpu-cache

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

缓冲缓存更改会阻止Meltdown吗?

如果新的CPU有一个缓存缓冲区,如果提交的指令只提交给实际的CPU缓存,那么类似于Meltdown的攻击仍然可能吗?

建议是让推测性执行能够从内存加载,但在实际提交之前不要写入CPU缓存.

cpu x86 caching cpu-architecture cpu-cache

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

如何在我的C代码中使用PREFETCHT0指令?

我想在我的C程序中预取某些地址(这是大型数组的某些元素的地址),并看到它对时间的影响.

关于PREFETCH的指令我在这里找到了PREFETCH0.但我不知道如何使用内联汇编在C中使用它.如果某个机构能够在C程序中如何使用该指令和地址作为参数,那将是非常有帮助的.

c linux x86 assembly inline-assembly

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