2

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

我使用命令“gcc -pthread -O0 out-of-order-test.c”编译了程序我在英特尔 IvyBridge 处理器英特尔(R ) Xeon(R) CPU E5-1650 v2。

有谁知道我应该怎么做才能看到乱序效应?

#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;
}
4

2 回答 2

6

您将竞态条件与乱序执行范例混合在一起。不幸的是,我很确定您不能“暴露”无序执行,因为它是明确设计和实现的,以保护您(正在运行的程序及其数据)免受其影响。

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

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

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

您可以在这里阅读更多关于乱序执行的信息:http: //courses.cs.washington.edu/courses/csep548/06au/lectures/introOOO.pdf

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

于 2015-11-21T22:59:44.710 回答
4

请注意:以下仅针对内存重新排序。据我所知,您无法观察到管道之外的乱序执行,因为这将构成 CPU 无法遵守其接口。(例如:你应该告诉英特尔,这将是一个错误)。具体来说,重新排序缓冲区和指令报废簿记必须出现故障。

根据英特尔的文档(特别是第 3A 卷,第 8.2.3.4 节):

Intel-64 内存排序模型允许使用较早的存储重新排序加载到不同的位置。

它还指定(我在总结,但所有这些都可以在第 8.2 节内存排序和 8.2.3 中的示例中获得)加载永远不会随着加载重新排序,存储永远不会随着存储重新排序,并且存储永远不会随着早期加载重新排序. 这意味着在 Intel 64 中这些操作之间存在隐式围栏(3 个弱类型)。

要观察内存重新排序,您只需要非常小心地实现该示例以实际观察效果。这是我所做的完整实现的链接,它演示了这一点。(我将在此处的随附帖子中跟进更多详细信息)。

本质上,第一个线程(示例中的 processor_0)执行此操作:

    x = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r1 = y;

while在自己的线程中的循环内部(使用 固定到 CPU SCHED_FIFO:99)。

第二个(观察者,在我的演示中)这样做:

    y = 1;
#if CPU_FENCE
    __cpu_fence();
#endif
    r2 = x;

也在while其自己的线程中使用相同的调度程序设置循环。

像这样检查重新排序(完全按照示例中的指定):

if (r1 == 0 and r2 == 0)
++reorders;

对于CPU_FENCE残疾人,这就是我所看到的:

[  0][myles][~/projects/...](master) sudo ./build/ooo
after 100000 attempts, 754 reorders observed

启用(使用CPU_FENCE“重量级”mfence指令)我看到:

[  0][myles][~/projects/...](master) sudo ./build/ooo
after 100000 attempts, 0 reorders observed

我希望这可以为您澄清事情!

于 2015-11-22T06:24:39.780 回答