c++ - x86 内存排序测试显示英特尔手册中规定不应该进行重新排序?

标签 c++ x86 cpu-architecture memory-barriers lock-free

根据英特尔手册。加载和存储都不会通过类似操作重新排序 根据 8.2.3.2 加载和存储都不会通过类似操作重新排序

位于文档https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html enter image description here

但我创建了一个简单的案例,我发现 r1=1 和 r2=2 发生了。

#include <thread>
#include <iostream>

using namespace std;

volatile int x;
int b[500];
volatile int y;
volatile int start;

int s1;
int s2;
int s3;
int s0;
int foo()
{
    while(start==0);
    x=1;
    asm volatile("" ::: "memory");
    y=1;
    return 0;
}

int fool2()
{
    int a,b;
    while(start==0);
    a=x;
    asm volatile("" ::: "memory");
    b=y;

   if(a==0 && b==1)
         s0++;
   if(a==0 && b==0)
         s1++;
   if(a==1 && b==0)
         s2++;
   if(a==1 && b==1)
        s3++;
   return 0;
}

int main()
{
  int i=0;
  while(1)
  {
     x=y=0;
     thread t1(foo);
     thread t2(fool2);
     start = 1;
     t1.join();
     t2.join();
     i++;
     if((i&0xFFFF)==0)
     {
           cout<<s0<<" "<<s1<<" "<<s2<<" "<<s3<<endl;
     }
  }
}

g++ -O2 -pthread e.cpp

海湾合作委员会版本7.5.0

输出:

69 86538 1 19246512

四种情况(r1 和 r2 具有 0, 1 组合)都是可能的。

最佳答案

仔细看看intel手册第8.2.3.2节是什么。在您的示例中,您正在有效地执行以下操作:

<表类=“s-表”> <标题> 处理器 1 处理器 2 <正文> mov [_x], 1 mov r2, _x mov [ _y], 1 mov r1, _y

而不是英特尔手册所说的:

<表类=“s-表”> <标题> 处理器 1 处理器 2 <正文> mov [_x], 1 mov r1, _y mov [ _y], 1 mov r2, _x

在您的示例中,处理器 2 可能会在处理器 1 设置 _x 之前加载 _x,然后在处理器 1 存储它之后加载 _y,从而允许 (r1=1, r2=0):

<表类=“s-表”> <标题> 说明 处理器 <正文> mov r2, _x 2 mov [_x], 1 1 mov [ _y], 1 1 mov r1, _y 2

在英特尔示例中,处理器 2 只能在加载 _y 后加载 _x,而处理器 1 只能在设置 _x 后设置 _y,因此 (r1=1, r2=0) 是不可能的。

下面是一些演示英特尔行为的代码:

#include <thread>
#include <iostream>
#include <stdlib.h>

using namespace std;

volatile int x;
volatile int y;
volatile int start;

constexpr bool flipOrdering = true; //Set this to true to see Intel example, false to see your example
constexpr int jitter = 10000;       //Range of random delay inserted between load/stores to make differences more obvious

int s1;
int s2;
int s3;
int s0;
int foo() {

    while(start==0);

    for(volatile int i = rand()%jitter; i; --i);
    x = 1;
    
    for(volatile int i = rand()%jitter; i; --i);
    asm volatile("" ::: "memory");

    for(volatile int i = rand()%jitter; i; --i);
    y = 1;

    return 0;
}

int fool2() {
    int a, b;
    while(start==0);

    for(volatile int i = rand()%jitter; i; --i);
    if constexpr(flipOrdering) b = y;
    else a = x;

    for(volatile int i = rand()%jitter; i; --i);
    asm volatile("" ::: "memory");

    for(volatile int i = rand()%jitter; i; --i);
    if constexpr(flipOrdering) a = x;
    else b = y;

   if(a==0 && b==1)
         s0++;
   if(a==0 && b==0)
         s1++;
   if(a==1 && b==0)
         s2++;
   if(a==1 && b==1)
        s3++;

    return 0;
}

int main() {
    int i=0;
    while(i< 1000) {
        x=y=0;
        thread t1(foo);
        thread t2(fool2);
        start = 1;
        t1.join();
        t2.join();
        i++;

        if((i%100)==0) {
            cout<<s0<<" "<<s1<<" "<<s2<<" "<<s3<<endl;
        }
    }

    return 0;
}

这是一个 link到编译器资源管理器中运行的相同代码。

关于c++ - x86 内存排序测试显示英特尔手册中规定不应该进行重新排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68444104/

相关文章:

c++ - 有没有办法让这些密切相关的类型可以互操作?

c++ - 为什么现代 C++ IDE 不能自动生成头文件?

C++: 错误: ‘class’ 没有名为的成员

assembly - 如何使用 NASM 组装 16 位 asm 代码,然后在 Linux 中对其进行调试,然后再在 DOSBox 中生成可执行文件

c++ - 无法为 GCC 128 位整数重载 std::abs()

Linux 32位反汇编有下一个字节的调用指令

c++ - linux bind 不创建套接字

ios - Xcode 6.1 上架构 x86_64 的 undefined symbol

compiler-construction - 机器如何解释二进制?

memory-management - 确定给定地址的页码和偏移量