CPU无序效应测试程序

CPU无序效应测试程序,c,x86,synchronization,memory-barriers,barrier,C,X86,Synchronization,Memory Barriers,Barrier,我编写了一个多线程程序来演示英特尔处理器的无序效应。这个程序附在这篇文章的末尾。 预期结果应该是当handler1将x打印为42或0时。但是,实际结果始终为42,这意味着不会发生无序效应 我用命令“gcc-pthread-o0ofordertest.c”编译了这个程序 我在英特尔IvyBridge处理器英特尔(R)至强(R)CPU E5-1650 v2上的Ubuntu12.04 LTS(Linux内核3.8.0-29-generic)上运行编译后的程序 有人知道我应该怎么做才能看到无序效应吗 #

我编写了一个多线程程序来演示英特尔处理器的无序效应。这个程序附在这篇文章的末尾。 预期结果应该是当handler1将x打印为42或0时。但是,实际结果始终为42,这意味着不会发生无序效应

我用命令“gcc-pthread-o0ofordertest.c”编译了这个程序 我在英特尔IvyBridge处理器英特尔(R)至强(R)CPU E5-1650 v2上的Ubuntu12.04 LTS(Linux内核3.8.0-29-generic)上运行编译后的程序

有人知道我应该怎么做才能看到无序效应吗

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int f = 0, x = 0;

void* handler1(void *data)
{
    while (f == 0);
    // Memory fence required here
    printf("%d\n", x);
}

void* handler2(void *data)
{
    x = 42;
    // Memory fence required here
    f = 1;
}

int main(int argc, char argv[])
{
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, handler1, NULL);
    pthread_create(&tid2, NULL, handler2, NULL);

    sleep(1);
    return 0;
}
#包括
#包括
#包括
int f=0,x=0;
void*handler1(void*data)
{
而(f==0);
//此处需要内存围栏
printf(“%d\n”,x);
}
void*handler2(void*data)
{
x=42;
//此处需要内存围栏
f=1;
}
int main(int argc,char argv[]
{
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,handler1,NULL);
pthread_create(&tid2,NULL,handler2,NULL);
睡眠(1);
返回0;
}

您将竞争条件与无序执行范例混为一谈。不幸的是,我非常确定您不能“公开”无序执行,因为它是以这样一种方式显式设计和实现的,即保护您(正在运行的程序及其数据)不受其影响

更具体地说:无序执行完全发生在CPU的“内部”。无序指令的结果不会直接发布到寄存器文件,而是排队以保持顺序。 因此,即使指令本身被无序执行(基于主要确保这些指令可以彼此独立运行的各种规则),它们的结果也总是按照外部观察者预期的正确顺序重新排序

您的程序所做的是:它尝试(非常粗糙)要模拟一种竞争条件,在这种竞争条件下,您希望看到
f
的分配在
x
之前完成,同时您希望上下文切换正好在此时发生,您假定新线程将调度在与另一个。 然而,正如我在上面解释的那样,即使你幸运地达到了列出的所有条件(在
f
赋值之后,但在
x
赋值之前安排第二个线程并且在同一个CPU核上安排新线程)-这本身就是一个极低概率的事件-即使如此,您真正暴露的只是潜在的竞争条件,而不是无序执行

很抱歉让您失望,但您的程序无法帮助您观察无序执行效果。至少不具有足够高的可能性,因此不具有实用性

您可以在此处阅读更多有关无序执行的信息:

更新 经过一些思考,我认为您可以立即修改指令,以期暴露无序的执行。但即使这样,我担心这种方法也会失败,因为新的“更新”指令不会正确地反映在CPU的管道中。我的意思是:CPU很可能已经获取并解析了您要修改的指令,因此将执行的内容将不再匹配内存字的内容(即使是CPU的一级缓存中的内容)。
但是,假设这种技术可以帮助您,它需要直接在汇编中进行一些高级编程,并且需要您的代码以最高权限级别(环0)运行。我建议在编写自修改代码时要格外小心,因为它有很大的潜在副作用。

请注意:以下内容仅针对内存重新排序。据我所知,您无法在管道外观察到无序执行,因为这将导致CPU无法遵守其接口。(例如:你应该告诉英特尔,这将是一个bug)。具体来说,必须在重新排序缓冲区和指令失效记账中出现故障

根据(特别是第3A卷第8.2.3.4节):

Intel-64内存排序模型允许使用早期存储将负载重新排序到其他位置

它还规定(我在总结,但所有这些都在第8.2节内存排序中提供,并在第8.2.3节中提供了示例),加载永远不会与加载一起重新排序,存储永远不会与存储一起重新排序,存储永远不会与早期加载一起重新排序。这意味着Intel 64中的这些操作之间存在隐式隔离(弱类型中的3个)

为了观察内存的重新排序,您只需要非常仔细地实现该示例,以便实际观察效果。我所做的充分实现证明了这一点。(我将在随附的帖子中跟进更多细节)

基本上,第一个线程(示例中的处理器0)执行以下操作:

    x = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r1 = y;
    y = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r2 = x;
在自己线程中的
while
循环内部(使用
SCHED_FIFO:99固定到CPU)

第二个(我的演示中的观察者)执行以下操作:

    x = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r1 = y;
    y = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r2 = x;
也在
while
循环中,在其自己的线程中使用相同的调度程序设置

重新排序检查如下(与示例中的规定完全相同):

禁用
CPU\u围栏后,我看到:

[  0][myles][~/projects/...](master) sudo ./build/ooo
after 100000 attempts, 754 reorders observed
启用
CPU_FENCE
后(使用“重量级”
mfence
指令),我看到:


我希望这能为你澄清一些事情

这不是无序,而是一种竞赛条件。(顺便说一句,x86是一种有序加载/存储体系结构。)@Olaf,感谢您的评论。但是,根据,x86至少存在存储后加载问题。如果它对一个内核有数据依赖性,我知道硬件将保持秩序。否则,可能会出现故障机制