使用多线程时加速低于预期

问题描述 投票:0回答:1

备注:我觉得这有点愚蠢,但这可能对某人有帮助

所以,我试图通过使用并行性来提高程序的性能。但是,我遇到了测量加速的问题。我有 4 个 CPU:

~% lscpu
...
CPU(s):                4
...

但是,加速比远低于四倍。这是一个最小的工作示例,其中有一个顺序版本、一个使用 OpenMP 的版本和一个使用 POSIX 线程的版本(以确保这不是由于任一实现造成的)。

纯顺序(

add_seq.c
):

#include <stddef.h>

int main() {
    for (size_t i = 0; i < (1ull<<36); i += 1) {
        __asm__("add $0x42, %%eax" : : : "eax");
    }
    return 0;
}

OpenMP (

add_omp.c
):

#include <stddef.h>

int main() {
    #pragma omp parallel for schedule(static)
    for (size_t i = 0; i < (1ull<<36); i += 1) {
        __asm__("add $0x42, %%eax" : : : "eax");
    }
    return 0;
}

POSIX 线程 (

add_pthread.c
):

#include <pthread.h>
#include <stddef.h>

void* f(void* x) {
    (void) x;
    const size_t count = (1ull<<36) / 4;
    for (size_t i = 0; i < count; i += 1) {
        __asm__("add $0x42, %%eax" : : : "eax");
    }
    return NULL;
}
int main() {
    pthread_t t[4];
    for (size_t i = 0; i < 4; i += 1) {
        pthread_create(&t[i], NULL, f, NULL);
    }
    for (size_t i = 0; i < 4; i += 1) {
        pthread_join(t[i], NULL);
    }
    return 0;
}

生成文件:

CFLAGS := -O3 -fopenmp
LDFLAGS := -O3 -lpthread  # just to be sure

all: add_seq add_omp add_pthread

所以,现在运行这个(使用 zsh 的内置时间):

% make -B && time ./add_seq && time ./add_omp && time ./add_pthread
cc -O3 -fopenmp  -O3 -lpthread    add_seq.c   -o add_seq
cc -O3 -fopenmp  -O3 -lpthread    add_omp.c   -o add_omp
cc -O3 -fopenmp  -O3 -lpthread    add_pthread.c   -o add_pthread
./add_seq  24.49s user 0.00s system 99% cpu 24.494 total
./add_omp  52.97s user 0.00s system 398% cpu 13.279 total
./add_pthread  52.92s user 0.00s system 398% cpu 13.266 total

检查CPU频率,顺序代码的最大CPU频率为2.90 GHz,并行代码(所有版本)的统一CPU频率为2.60 GHz。算一下数十亿条指令:

>>> 24.494 * 2.9
71.0326
>>> 13.279 * 2.6
34.5254
>>> 13.266 * 2.6
34.4916

因此,总而言之,线程代码的运行速度仅是顺序代码的两倍,尽管它使用的 CPU 时间是顺序代码的四倍。为什么会这样?

备注:asm_omp.c

的汇编
似乎效率较低,因为它通过递增寄存器并将其与迭代次数进行比较来执行for循环,而不是递减并直接检查ZF;然而,这对性能没有影响

c multithreading performance pthreads openmp
1个回答
3
投票
嗯,答案很简单:实际上只有两个 CPU 核心:

% lscpu ... Thread(s) per core: 2 Core(s) per socket: 2 Socket(s): 1 ...
因此,虽然 

htop

 显示了四个 CPU,但其中两个是虚拟的,并且只是因为 
超线程 才存在。由于超线程的核心思想是在两个进程中共享单个核心的资源,因此它不会使类似的代码更快(它仅在使用不同资源运行两个线程时有用)。

因此,最终发生的情况是 time/

clock() 测量每个逻辑核心的使用情况作为底层物理核心的使用情况。由于所有报告都报告 ~100% 使用率,我们得到 ~400% 使用率,尽管它只代表两倍的加速。

直到那时,我确信这台计算机包含 4 个物理核心,并且完全忘记检查超线程。

  • 类似问题
  • 相关问题
© www.soinside.com 2019 - 2024. All rights reserved.