You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenMP并行计算慢于串行,6核CPU下性能未达预期求排查

并行计算性能未达预期的问题分析

问题背景

尝试通过并行与串行处理完成数学计算,对比两者性能差异,相关代码如下:

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>

#include <bitset>
#include <iostream>

const int M = 100;
const int N = 100;

using namespace std;

int getRandomNum(int start, int end) {
  return rand() % (end - start + 1) + start;
}

int* parallelTask(int arr[M][N]) {
  int* answer = new int[M];
  #pragma omp parallel
  {
    #pragma omp for
    // here is the same code as in sequentialTask
  }
}

int* sequentialTask(int arr[M][N]) {
  int* answer = new int[M];
  for (int i = 0; i < M; i++) {
    int num = 0;
    for (int j = 0; j < N; j++) {
      for (int k = 1; k < N - j; k++) {
        int mult = arr[i][j] * arr[i][j + k];
        bitset<32> bitset = mult;
        string mult_binary = bitset.to_string();
        for (int l = 0; l < mult_binary.size(); l++) {
          if (mult_binary[l] == '1') {
            num += 1;
          }
        }
      }
    }
  }
  return answer;
}

void main() {
  srand((unsigned int)time(NULL));
  int arr[M][N];

  for (int i = 0; i < M; i++) {
    for (int j = 0; j < N; j++) {
      arr[i][j] = getRandomNum(1, 10);
    }
  }

  double start;
  double end;

  start = omp_get_wtime();
  parallelTask(arr);
  end = omp_get_wtime();
  printf("Parallel work took %f seconds\n", end - start);

  start = omp_get_wtime();
  sequentialTask(arr);
  end = omp_get_wtime();
  printf("Regular work took %f seconds\n", end - start);
}

测试结果

初始测试(未指定线程数):

Parallel work took 2,314297 seconds
Regular work took 2,011105 seconds

指定num_threads(6)后:

Parallel work took 1,742959 seconds
Regular work took 2,010864 seconds

使用6核处理器,预期并行处理性能提升应更明显,请问问题出在何处?


问题原因分析

  • 并行任务粒度太小,线程开销占比过高
    当前设置M=100,6个线程平均仅能分到约16行的计算任务。线程的创建、调度、同步以及OpenMP并行区域的初始化开销,会大幅抵消并行计算带来的性能收益,甚至在初始测试中超过串行的执行时间。只有当每个线程处理的计算量足够大时,并行的优势才能体现,建议将M调至10000以上,或增大N的数值,让单线程的计算负载足够覆盖线程开销。

  • 核心计算逻辑效率极低,串行版本易被编译器优化
    你当前统计二进制1的个数的方式完全没必要:将整数转成bitset再转成string遍历,这种方法的性能极差。直接使用编译器内置函数(比如GCC的__builtin_popcount(mult))就能高效完成统计,速度提升几个数量级。同时,三重循环的冗余计算也未做优化,串行版本因为逻辑简单,编译器可能自动做了部分优化,而并行版本的线程开销加上低效的计算逻辑,导致性能提升不显著。

  • 内存局部性与系统资源影响
    虽然指定了6个线程,但操作系统可能有其他进程占用CPU资源,导致6个线程无法完全利用6核的全部算力。另外,虽然数组是行优先存储,并行处理行循环的内存局部性较好,但在计算量不足的情况下,这一点优势无法发挥作用。


内容的提问来源于stack exchange,提问作者0SouthBoss0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 11:06:22