简单介绍和应用

openmp主要用来将串行程序并行化(以共享内存型的方式),还可以加入一些同步互斥操作, 编译器自动进行并行优化,如果编译器不支持,代码仍然可以正常运作,只是不能以多线程的方式加速。

  • 应用
    简单应用的话比较容易上手,头文件里加上#include <omp.h> ,gnu编译选项时加上-fopenmp
  • openmp里主要有四个组件(4.0版本)。

    1.directives指令,每个指令以`#pragma omp`开头
    2.Runtime Library Routines 运行时接口
    3.Environment Variables 环境变量
    4.Clauses子句 可以算是对指令的补充描述,搭配指令一起食用
  • 例子 faiss L2Distance
 /*********************************************************
 * main
 */
 #include <omp.h>
 /* Compute the L2 norm of a set of nx vectors */
 void fvec_norms_L2 (float * __restrict nr,
                     const float * __restrict x,
                     size_t d, size_t nx)
 {
 
 #pragma omp parallel for
     for (int64_t i = 0; i < nx; i++) {
         nr[i] = sqrtf (fvec_norm_L2sqr (x + i * d, d));
     }
 }

 /*********************************************************
 * SSE and AVX implementations
 */

#ifdef __SSE3__

#include <immintrin.h>

// reads 0 <= d < 4 floats as __m128
static inline __m128 masked_read (int d, const float *x)
{
    assert (0 <= d && d < 4);
    ALIGNED(16) float buf[4] = {0, 0, 0, 0};
    switch (d) {
      case 3:
        buf[2] = x[2];
      case 2:
        buf[1] = x[1];
      case 1:
        buf[0] = x[0];
    }
    return _mm_load_ps (buf);
    // cannot use AVX2 _mm_mask_set1_epi32
}

float fvec_norm_L2sqr (const float *  x,
                      size_t d)
{
    __m128 mx;
    __m128 msum1 = _mm_setzero_ps();

    while (d >= 4) {
        mx = _mm_loadu_ps (x); x += 4;
        msum1 = _mm_add_ps (msum1, _mm_mul_ps (mx, mx));
        d -= 4;
    }

    mx = masked_read (d, x);
    msum1 = _mm_add_ps (msum1, _mm_mul_ps (mx, mx));

    msum1 = _mm_hadd_ps (msum1, msum1);
    msum1 = _mm_hadd_ps (msum1, msum1);
    return  _mm_cvtss_f32 (msum1);
}

/*********************************************************
 * NEON implementations
 */
#elif defined(__aarch64__)

#include <arm_neon.h>

float fvec_norm_L2sqr (const float *x, size_t d)
{
    if (d & 3) return fvec_norm_L2sqr_ref (x, d);
    float32x4_t accu = vdupq_n_f32 (0);
    for (size_t i = 0; i < d; i += 4) {
        float32x4_t xi = vld1q_f32 (x + i);
        accu = vfmaq_f32 (accu, xi, xi);
    }
    float32x4_t a2 = vpaddq_f32 (accu, accu);
    return vdups_laneq_f32 (a2, 0) + vdups_laneq_f32 (a2, 1);
}

 /*********************************************************
 * scalar implementations
 */
#else

float fvec_norm_L2sqr_ref (const float *x, size_t d)
{
    size_t i;
    double res = 0;
    for (i = 0; i < d; i++)
       res += x[i] * x[i];
    return res;
}

float fvec_norm_L2sqr (const float *x, size_t d)
{
    return fvec_norm_L2sqr_ref (x, d);
}

#endif

原理

还问原理有点离谱呀,这涉及编译器问题了呀沃土了。

  • OpenMP执行模式
    OpenMP的执行模型采用fork-join的形式,其中fork创建线程或者唤醒已有线程;join即多线程的会合。fork-join执行模型在刚开始执行的时候,只有一个称为“主线程”的运行线程存在。主线程在运行过程中,当遇到需要进行并行计算的时候,派生出线程来执行并行任务。在并行执行的时候,主线程和派生线程共同工作。在并行代码执行结束后,派生线程退出或者阻塞,不再工作,控制流程回到单独的主线程中。

更多操作见openmp文档

https://www.openmp.org/specif...


ysysys
10 声望1 粉丝