为什么4个进程优于4个线程?

diz*_*l3d 2 c++ multithreading operating-system intel

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

4个线程,同时工作,执行任务1 193周在30秒的时间.但4个过程中,同时工作,执行任务1 348周在30秒的时间.为什么会有这么大的差异?我在[Mac OS X 10.7.5,Intel Core i5(4个逻辑核心)]上测试了它.想想,在Windows和Linux中存在同样的差异.

Dav*_*eas 8

res变量是static,这意味着它是由所有在相同的进程中的线程的共享.这意味着在四个线程的情况下,res一个线程中的变量的每个修改必须对其他线程可用,这通常涉及总线上的某种锁定,一级高速缓存的无效以及所有其他线程的重新加载的CPU.

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

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

res ^= x;
Run Code Online (Sandbox Code Playgroud)

这不是原子操作,处理器将加载旧值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
Run Code Online (Sandbox Code Playgroud)


Mat*_*son 5

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

static volatile long res = 1;
Run Code Online (Sandbox Code Playgroud)

因此,如果您只在四个进程中的每个进程中运行一个线程,则有四个不同的"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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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