并发与并行编程概览

背景

并发(Concurrent)与并行(Parallel)是提高业务量和性能所关注的因素,二者有些细微差别:

  1. 并发:指具有“同时”处理多任务的能力,此时不一定并行,单核通过时间片调度即可实现;
  2. 并行:指具有同一时刻处理多任务的能力,必须要求多核,且多个核心协同工作。

落实下来,无非就是多进程多线程,一般出于许多因素,选择多线程。

多线程具有许多实现方式,现在此记录。

拟考虑现在有两个向量,现在需要使用并发技术将二者加起来。向量定义如下:

#define ANSI_COLOR_RED     "x1b[31m"
#define ANSI_COLOR_GREEN   "x1b[32m"
#define ANSI_COLOR_RESET   "x1b[0m"

...

static const VECTOR_LENGTH = 10;

static struct {                
  int vector1[VECTOR_LENGTH];
  int vector2[VECTOR_LENGTH];  
  int vector_total[VECTOR_LENGTH];
} global_args = {              
  .vector1 = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19},
  .vector2 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20},                              
  .vector_total = { }          
};

pthread

最简单的就是 POSIX pthread。

#include <assert.h>
#include <pthread.h>

/**
 * Calculate the sum of two vectors
 */
static void* create_thread(void* argument) {
  int index = *((int*) argument);

  global_args.vector_total[index] = global_args.vector1[index] + global_args.vector2[index];

#ifdef DEBUG
    printf(ANSI_COLOR_RED "In thread %d, calculated the result: %dn" ANSI_COLOR_RESET,
      index, global_args.vector_total[index]);
#endif

  return NULL;
}

// Usage
... {
  ...
  pthread_t threads[VECTOR_LENGTH];
  int thread_args[VECTOR_LENGTH];
  int result_code;

  // create all threads one by one
  for (size_t i = 0; i < VECTOR_LENGTH; ++i) {
#ifdef DEBUG
    printf(ANSI_COLOR_GREEN "In main: creating thread %zun" ANSI_COLOR_RESET, i);
#endif
    thread_args[ i ] = i;
    result_code = pthread_create(&threads[i], NULL, create_thread, &thread_args[i]);
    assert(!result_code);
  }

  // wait for each thread to complete
  for (size_t i = 0; i < VECTOR_LENGTH; ++i) {
    // block until thread 'i' completes
    result_code = pthread_join(threads[i], NULL);
    assert(!result_code);
#ifdef DEBUG
    printf(ANSI_COLOR_GREEN "In main: thread %zu has completedn" ANSI_COLOR_RESET, i);
#endif
  }
  ...
}

注:引入 pthread 需要 “-lpthread”。

OpenMP

OpenMP(Open Multi-Processing)是一套支持跨平台共享内存方式的多线程并发的编程API,更易用。

/**
 * Calculate the sum of two vectors                                             
 */
static void* openmp_thread(void* argument) {
  int index = *((int*) argument);                                               

  global_args.vector_total[index] = global_args.vector1[index] + global_args.vector2[index];
    
#ifdef DEBUG         
    printf(ANSI_COLOR_RED "In thread %d, calculated the result: %dn" ANSI_COLOR_RESET,
      index, global_args.vector_total[index]);                                  
#endif

  return NULL;
}

... {
  ...
  int thread_args[VECTOR_LENGTH];

  // create all threads by the openmp pragmas
  #pragma omp parallel for
  for (size_t i = 0; i < VECTOR_LENGTH; ++i) {
    thread_args[ i ] = i;
    openmp_thread(&thread_args[i]);
  }
  ...
}

注:需要安装 lib:openmpi-dev,引入 openmp 需要 “-fopenmp”。

Open MPI

Open MPI 是开源高效的消息传递库,以下是向量相加的 MPI 实现。

#include <mpi.h>

#define N 10
static unsigned VECTOR_LENGTH = N;

... {
  ...

  // Init mpi
  int rank, size;
  // double start, end;

  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);

  // start = MPI_Wtime();

  MPI_Bcast(&VECTOR_LENGTH, 1, MPI_INT, 0, MPI_COMM_WORLD);

  MPI_Bcast(&global_args.vector1, VECTOR_LENGTH, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&global_args.vector2, VECTOR_LENGTH, MPI_INT, 0, MPI_COMM_WORLD);
  MPI_Bcast(&global_args.vector_total, VECTOR_LENGTH, MPI_INT, 0, MPI_COMM_WORLD);

  // create all threads by the open mpi
  for (size_t i = rank; i < VECTOR_LENGTH; i += size) {
    global_args.vector_total[i] = global_args.vector1[i] + global_args.vector2[i];

#ifdef DEBUG
    printf(ANSI_COLOR_RED "In rank %d, calculated the result: %dn" ANSI_COLOR_RESET,
      rank, global_args.vector_total[i]);
#endif
  }

  // end = MPI_Wtime();

  MPI_Finalize();

  printf(ANSI_COLOR_GREEN "Performing the final serial computation on thread: %dn"
    ANSI_COLOR_RESET, rank);

  ...
}

注:需要安装 lib:omp-dev,Fedora 等系统还需手动加载模块:

module load mpi/openmpi-x86_64

编译以及执行方式:

  • 编译:mpicc
  • 执行:mpirun (-np 参数代表并发度)


参考:

作者: YanWen

Web 开发者

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s