备注:我对此感到有些愚蠢,但这可能有助于某人
所以,我试图通过使用并行性来提高程序的性能。但是,我遇到了测量加速的问题。我有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,并行代码(所有版本)具有2.60 GHz的统一CPU频率。计算数十亿条指令:
>>> 24.494 * 2.9
71.0326
>>> 13.279 * 2.6
34.5254
>>> 13.266 * 2.6
34.4916
所以,总而言之,线程代码的运行速度只是顺序代码的两倍,尽管它使用的CPU时间是其四倍。为什么会这样?
备注: asm_omp.c
的程序集似乎效率低,因为它通过递增寄存器并将其与迭代次数进行比较来执行for循环而不是递减和直接检查ZF;但是,这对性能没有影响
答案 0 :(得分:3)
嗯,答案很简单:实际上只有两个CPU内核:
% lscpu
...
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
...
所以,尽管htop
显示了4个CPU,但是有两个是虚拟的,只有hyperthreading。由于超线程的核心思想是在两个进程中共享单个核心的资源,因此它确实有助于更快地运行类似的代码(仅在使用不同资源运行两个线程时才有用)。
因此,最终,时间/ clock()测量每个逻辑核心的使用情况与底层物理核心的使用情况相同。由于所有报告约100%的使用率,我们得到约400%的使用率,尽管它只代表了两倍的加速。
直到那时,我确信这台计算机包含4个物理内核,并且完全忘记检查超线程。