C 语言多进程 vs 多线程性能问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tom82232
V2EX    问与答

C 语言多进程 vs 多线程性能问题

  •  
  •   tom82232 2020-11-02 15:07:03 +08:00 1750 次点击
    这是一个创建于 1860 天前的主题,其中的信息可能已经有所发展或是发生改变。
    测试源码如下:
    #define _GNU_SOURCE
    #include <sched.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <stdint.h>
    #include <unistd.h>
    #include <sys/syscall.h>
    #include <sys/types.h>
    #include <fcntl.h>

    typedef struct __test{
    uint64_t a;
    uint64_t b;
    uint64_t c;
    uint64_t d;
    uint64_t e;
    }test_t;


    #define CPU_MAX 128
    int cpuid[CPU_MAX];
    test_t tdata[CPU_MAX];
    int seq[CPU_MAX];
    int thd_num = 1;
    uint64_t prev[CPU_MAX];
    uint64_t tmp[CPU_MAX];
    int flag_stat = 0;

    static void
    init(void)
    {
    for(int i = 0; i < CPU_MAX; i++) {
    cpuid[i] = -1;
    seq[i] = i;
    tdata[i].e = 100;
    tdata[i].a = 0;
    tdata[i].b = 0;
    tdata[i].c = 0;
    tdata[i].d = 0;
    prev[i] = 0;
    }
    }

    static int
    parse_cpuid(const char *str)
    {
    char buf[256];
    int flag_start = 0;
    int thdnum = 0;
    int len = strlen(str);
    if(len > 255) {
    memcpy(buf, str, 255);
    buf[255] = 0;
    }
    else if(0 < len) {
    strcpy(buf, str);
    }
    else return 0;

    for(int i = 0; i < 256; i++) {
    if(buf[i] == ' ' || buf[i] == '\t' || buf[i] == ',') buf[i] = 0;
    }

    for(int i = 0; i < 256; i++) {
    if(flag_start && 0 == buf[i]) flag_start = 0;
    if(0 == flag_start && 0 != buf[i]) {
    flag_start = 1;
    cpuid[thdnum] = atoi(buf+i);
    if(0 > cpuid[thdnum] || CPU_MAX <= cpuid[thdnum]) return -1;
    thdnum++;
    }
    }

    return 0;
    }
    static int
    parse_cmd(int argc, char **argv)
    {
    if(0 < argc) {
    thd_num = atoi(argv[0]);
    if(0 > thd_num) return -1;
    argc--, argv++;
    }

    if(0 < argc) {
    return parse_cpuid(argv[0]);
    }

    return 0;
    }

    static void*
    task_root(void *arg)
    {
    register int seq = *(int*)arg;
    int cid = cpuid[seq];
    cpu_set_t cpumask;
    pid_t pid = syscall(__NR_gettid);

    if(0 <= cid && CPU_MAX > cid) {
    //绑定 CPUID
    CPU_ZERO(&cpumask);
    CPU_SET(cid, &cpumask);
    sched_setaffinity(pid, sizeof(cpumask), &cpumask);
    usleep(1000);
    printf("task[%d] run on cpuid=%d\n", seq, cid);
    }
    else {
    printf("task[%d] run on cpuid=auto\n", seq);
    }

    while(1) {
    tdata[seq].a += 1;
    tdata[seq].b += tdata[seq].e*8;
    tdata[seq].c += 1;
    tdata[seq].d += tdata[seq].e*8;
    }

    return NULL;
    }

    static void*
    stat_root(void *arg)

    {
    arg = arg;
    uint64_t ttl;
    uint64_t d10[CPU_MAX];

    while(1) {

    if(0 == flag_stat) {
    usleep(1000);
    }
    else {
    flag_stat = 0;
    ttl = 0;
    for(int i = 0; i < thd_num; i++) {
    tmp[i] = tdata[i].a;
    }

    for(int i = 0; i < thd_num; i++) {
    tmp[i] = tdata[i].a;
    d10[i] = tmp[i] - prev[i];
    ttl += d10[i];
    }

    printf("\nTotal do times: %lu(sec)\n", ttl/10);
    for(int i = 0; i < thd_num; i++) {
    if(-1 == cpuid[i]) {
    printf("\ttask[%d] cpu=auto do times: %lu(/sec)\n", i, d10[i]/10);
    }
    else {
    printf("\ttask[%d] cpu=%d do times: %lu(/sec)\n", i, cpuid[i], d10[i]/10);
    }

    prev[i] = tmp[i];
    }
    }
    }

    return NULL;
    }

    static void*
    timer_root(void *arg)
    {
    arg = arg;
    uint64_t ttl;
    uint64_t d10[CPU_MAX];

    while(1) {
    sleep(10);
    flag_stat = 1;
    }

    return NULL;
    }

    static int
    task_set(void)
    {
    pthread_t thdid;
    for(int i = 0; i < thd_num; i++) {
    pthread_create(&thdid, NULL, task_root, &seq[i]);
    }

    pthread_create(&thdid, NULL, timer_root, NULL);
    pthread_create(&thdid, NULL, stat_root, NULL);

    return 0;
    }

    int main(int argc, char **argv)
    {
    //program thread-num thread-cpu-seq
    init();
    argc--, argv++;
    if(-1 == parse_cmd(argc, argv)) {
    printf("Input param error\n");
    exit(-1);
    }

    task_set();

    while(1) {
    sleep(1000);
    }

    return 0;
    }

    双 CPU-4 核-超线程 Intel(R) Xeon(R) CPU E5620 @ 2.40GHz
    逻辑 CPU 0 8 2 10 4 12 6 14 1 9 3 11 5 13 7 15
    pysical id 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
    core id 0 0 1 1 9 9 10 10 0 0 1 1 9 9 10 10
    系统:centos8

    测试线程:
    单线程绑定 0 逻辑 CPU 测试结果(执行累加次数) 108363138 (次 /秒)
    双线程绑定 0,1 逻辑 CPU 测试结果(执行累加次数) 79961987 (次 /秒)
    3 线程绑定 0,1,2 逻辑 CPU 测试结果(执行累加次数) 108639280 (次 /秒)
    4 线程绑定 0,1,2,3 逻辑 CPU 测试结果(执行累加次数) 97511278 (次 /秒)
    5 线程绑定 0,1,2,3,4 逻辑 CPU 测试结果(执行累加次数) 115751101 (次 /秒)
    6 线程绑定 0,1,2,3,4,5 逻辑 CPU 测试结果(执行累加次数) 138198505 (次 /秒)
    7 线程绑定 0,1,2,3,4,5,6 逻辑 CPU 测试结果(执行累加次数) 129034696 (次 /秒)
    8 线程绑定 0,1,2,3,4,5,6,7 逻辑 CPU 测试结果(执行累加次数) 126871151 (次 /秒)
    9 线程绑定 0,1,2,3,4,5,6,7,8 逻辑 CPU 测试结果(执行累加次数) 157512706 (次 /秒)
    10 线程绑定 0,1,2,3,4,5,6,7,8,9 逻辑 CPU 测试结果(执行累加次数) 141989197 (次 /秒)
    [结论]
    增加线程数量并没有想象中的增加性能。
    预期的目标应该是增加线程数(在同一个 CPU 中分配给不同的 CORE 的时候性能应该倍数增加,实际上没有),和预期的相差很大。

    测试进程:
    每个进程开始一个累加统计线程
    测试结果
    进程 1 绑定 CPU0 测试结果(执行累加次数) 102847956 (次 /秒)
    进程 2 绑定 CPU2 测试结果(执行累加次数) 102903297 (次 /秒)
    进程 3 绑定 CPU4 测试结果(执行累加次数) 102909125 (次 /秒)
    进程 4 绑定 CPU6 测试结果(执行累加次数) 102934502 (次 /秒)
    进程 5 绑定 CPU3 测试结果(执行累加次数) 107560180 (次 /秒)
    假如开一个进程,绑定 CPU 和别的进程绑定的 CPU 在同一个 CPU 的同一个 CORE 下的话,性能会变成一半,两个进程加起来和单独只开一个进程的时候差不多,
    [结论]
    开进程处理数据没有相互影响,和预期的一样,增加进程性能倍数增加。
    两个进程开在同一个 CPU 的同一个 CORE 中的时候性能减半,因为使用同一套 CORE 的计算单元,所以可以理解。

    [疑问]
    多线程的性能变化应该和多进程的一样预期,实际测试结果多线程的性能提升很少。(到底是什么因素导致的这个测试结果,没有思路,有知道的大佬能告诉一下吗???
    6 条回复    2020-11-02 17:36:42 +08:00
        1
    msg7086  
       2020-11-02 15:16:02 +08:00
    你用多线程跑的时候,CPU 吃满了吗?
    tom82232
        2
    tom82232  
    OP
       2020-11-02 15:18:46 +08:00
    @msg7086 CPU 是 100%us
    msg7086
        3
    msg7086  
       2020-11-02 15:46:33 +08:00
    @tom82232 我的意思是,每个 Core 都吃满了么。然后就是看看多线程下的算力和时间计算有没有错。
    上面代码贴得太乱实在读不下去了。
    besto
        4
    besto  
       2020-11-02 16:10:33 +08:00
    先说点 别的,timer 做的毫无意义,你这里面全部没 mutex,不如 stat 的时候就一个简单逻辑 sleep(x), 然后直接算这个期间到底算了多少。多线程没有显著提升我觉得有很大原因是和 cache 命中有关系。 试试加大计算量:
    diff --git a/b.c b/b.c
    index 5168b0d..a92dc5e 100644
    --- a/b.c
    +++ b/b.c
    @@ -28,6 +28,8 @@ uint64_t prev[CPU_MAX];
    uint64_t tmp[CPU_MAX];
    int flag_stat = 0;

    +uint8_t *cache;
    +
    static void
    init(void)
    {
    @@ -116,6 +118,7 @@ tdata[seq].a += 1;
    tdata[seq].b += tdata[seq].e*8;
    tdata[seq].c += 1;
    tdata[seq].d += tdata[seq].e*8;
    +memset(cache, 0, 64*1024*1024);
    }

    return NULL;
    @@ -195,6 +198,7 @@ return 0;

    int main(int argc, char **argv)
    {
    + cache = malloc(64*1024*1024);
    //program thread-num thread-cpu-seq
    init();
    argc--, argv++;
    tom82232
        5
    tom82232  
    OP
       2020-11-02 16:32:29 +08:00
    @besto timer 可以不去考虑,都没关系。累加的时候处理的数据都没有变化,这样也会发生 cache 不命中?
    besto
        6
    besto  
       2020-11-02 17:36:42 +08:00
    @tom82232 说实话,我不能确定是不是 cache 命中导致,但现象让我很怀疑。另一个可怀疑的点就是你的这个计算运算量的方法。。。所以让每个循环多做点事情也可能是减少误差的真正原因
    关于    帮助文档     自助推广系统     博客     API     FAQ     Solana     949 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 23:16 PVG 07:16 LAX 15:16 JFK 18:16
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86