2
void task1(void* arg) {
    static volatile long res = 1;
    for (long i = 0; i < 100000000; ++i) {
        res ^= (i + 1) * 3 >> 2;
    }
}

4 个线程同时工作,在 30 秒内执行task1 193次。但是 4 个进程同时工作,在 30 秒内执行task1 348次。为什么会有这么大的差异?我在[Mac OS X 10.7.5, Intel Core i5 (4 logical cores)]上对其进行了测试。想一想,Windows 和 Linux 的区别是一样的。

4

3 回答 3

8

res变量是,这static意味着它被同一进程中的所有线程共享。这意味着在四个线程的情况下,res一个线程中变量的每次修改都必须对其他线程可用,这通常涉及总线上的某种锁定、一级缓存的无效和所有其他线程的重新加载中央处理器。

在四个进程的情况下,变量并没有真正被不同的进程共享,因此它们可以真正并行运行而不会相互干扰。

请注意,主要区别不是线程/进程,而是在一种情况下每个人都访问相同的变量而在另一种情况下他们访问不同的变量这一事实。此外,在线程的情况下,真正的问题不是性能,而是最终结果可能不正确的事实:

res ^= x;

这不是原子操作,处理器将加载 的旧值res,然后将其修改到寄存器中并将其写回。如果没有同步原语,多个线程可以加载相同的值,独立修改它并写回变量,在这种情况下,一些线程的工作将被其他线程覆盖。最终结果将取决于不同线程的执行模式,而不是取决于程序的代码。

要模拟变量的非共享,您需要确保在线程中访问不同的缓存行。最简单的更改是static从变量中删除限定符,以便每个线程将更新它自己的堆栈中的变量,该变量将位于与其他线程的变量不同的内存地址,并希望映射到不同的缓存行。另一种选择是一起创建四个变量,但在它们之间添加足够的填充,以便它们分布到不同的缓存行:

struct padded_long {
    volatile unsigned long res;
    char [CACHE_LINE_SIZE - sizeof(long)]; // Find this in your processor documentation
};
void f(void *) {
   static padded_long res[4];
   // detect which thread is running based on the argument and use res[0]..res[3]
   // for the different threads
于 2013-05-19T14:17:44.577 回答
5

这是一个进程中所有线程的一个变量:

static volatile long res = 1;

因此,如果您只在四个进程中的每一个中运行一个线程,那么您将拥有四种不同的“res”,它们存在于不同的内存位中。在线程的情况下,“res”对于所有四个线程来说都是同一个变量,因此每次更新时,其他三个处理器都必须使其副本无效(删除),并从最后更新的处理器中获取一个新的它。这会减慢一切。如果你真的想为每个线程更新一个变量,我建议你这样做:

void task1(void* arg) {
    volatile long* res = const_cast<volatile long *>(
           reinterpret_cast<long *>(arg));
    for (long i = 0; i < 100000000; ++i) {
        res ^= (i + 1) * 3 >> 2;
    }
}

并传入long不同的内存部分(例如,用于new long为每个线程生成唯一地址)。

于 2013-05-19T14:18:39.060 回答
3

线程共享res变量,因此所有 4 个内核都竞争一个缓存行。不同的过程创建自己的res,从而消除了变量的竞争。

于 2013-05-19T14:16:28.207 回答