首页 > 其他分享 >高性能计算-粒子状态模拟计算优化

高性能计算-粒子状态模拟计算优化

时间:2024-12-10 11:21:03浏览次数:4  
标签:OPT 粒子 double 模拟计算 pos 高性能 dx include 优化

1. 源码为对粒子移动状态模拟的项目。要求使用多种优化方法,对比串行优化、多线程优化、全部优化下的加速比。

2. 代码

项目代码地址:https://github.com/libo-0379/StellarSim_Optimize

以下为核心优化代码及分析

/*
 * =====================================================================================
 *
 *       Filename:  sphkernel.cpp
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  02/27/22 14:55:57
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  monkey
 *   Organization:  
 *          email:  monkey@icloud.com
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include "../headers/global.h"
#include "../headers/sphkernel.h"

using namespace std;
 
//计算距离
void getPairwiseSeparations(double** &pos)
{
  // 1. 提出循环不变量
  // 2. todo simd
  // 3. 对 j 进行循环分块 影响dx dy dz的命中,不适用
  // 4. omp
  #if defined(OPT_BASE) && (defined(OPT_SIMD)||defined(OPT_OMP))
    #ifdef OPT_OMP
    #pragma omp parallel for schedule(guided) proc_bind(close) 
    #endif
    for (int i = 0; i < N; i++) 
    {
      #ifndef OPT_SIMD
        double temp1 = pos[0][i];
        double temp2 = pos[1][i];
        double temp3 = pos[2][i];
        for (int j = 0; j < N; j++) 
        {
          // dx[i][j] = -dx[j][i] 粒子彼此计算相对距离
          dx[i][j] = pos[0][j] - temp1;
          dy[i][j] = pos[1][j] - temp2;
          dz[i][j] = pos[2][j] - temp3;
        }
      #else
        float64x2_t v0 = vld1q_dup_f64(&pos[0][i]);
        float64x2_t v1 = vld1q_dup_f64(&pos[1][i]);
        float64x2_t v2 = vld1q_dup_f64(&pos[2][i]);
        for(int j=0;j<N/2*2;j+=2)
        {
            float64x2_t v0_0 = vld1q_f64(&pos[0][j]);
            float64x2_t v1_0 = vld1q_f64(&pos[1][j]);
            float64x2_t v2_0 = vld1q_f64(&pos[2][j]);
            vst1q_f64(&dx[i][j],vsubq_f64(v0_0,v0));
            vst1q_f64(&dy[i][j],vsubq_f64(v1_0,v1));
            vst1q_f64(&dz[i][j],vsubq_f64(v2_0,v2));
        }
        for (int j = N/2*2; j < N; j++) 
        {
          dx[i][j] = pos[0][j] - pos[0][i];
          dy[i][j] = pos[1][j] - pos[1][i];
          dz[i][j] = pos[2][j] - pos[2][i];
        }
      #endif
    }
  #else
    #ifdef OPT_OMP
    #pragma omp parallel for schedule(guided) proc_bind(close) 
    #endif
    for (int i = 0; i < N; i++) 
    {
      for (int j = 0; j < N; j++) 
      {
        // dx[i][j] = -dx[j][i] 粒子彼此计算相对距离
        dx[i][j] = pos[0][j] - pos[0][i];
        dy[i][j] = pos[1][j] - pos[1][i];
        dz[i][j] = pos[2][j] - pos[2][i];
        //fprintf(stdout, "%12.6f", dz[i][j]);
        //fflush(stdout);
      }
    //fprintf(stdout,"\n");
    }
  #endif
}

void getW(double** &dx, double** &dy, double** &dz, const double h)
{
  // 1. 循环不变量提出
  // 2. omp
  #if defined(OPT_OMP) || defined(OPT_BASE)
    double value1 = pow((1.0 / (h*sqrt(pi))), 3.0);
    double value2 = pow(h,2);
    #ifdef OPT_OMP
    #pragma omp parallel for schedule(guided) proc_bind(close)
    #endif
    for (int i = 0; i < N; i++) 
    {
      for (int j = 0; j < N; j++) 
      {
        r[i][j] = sqrt(pow(dx[i][j],2.0) + pow(dy[i][j],2.0) + pow(dz[i][j],2.0));
        W[i][j] = value1 * exp((-pow(r[i][j],2) / value2)); 
      }
    }
  #else   
    #ifdef OPT_OMP
    #pragma omp parallel for schedule(guided) proc_bind(close) 
    #endif
    for (int i = 0; i < N; i++) 
    {
      for (int j = 0; j < N; j++)
      {
        r[i][j] = sqrt(pow(dx[i][j],2.0) + pow(dy[i][j],2.0) + pow(dz[i][j],2.0));
        W[i][j] = pow((1.0 / (h*sqrt(pi))), 3.0) * exp((-pow(r[i][j],2) / pow(h,2))); 
        //fprintf(stdout, "%12.6f", r[i][j]);
        //fprintf(stdout, "%12.6f", W[i][j]);
        //fflush(stdout);
      }
    }
  #endif
    //fprintf(stdout,"\n");
}

void getGradW(double** &dx, double** &dy, double** &dz, const double h)
{
  // 1. 循环不变量提出
  // 2. omp
  #if defined(OPT_OMP) || defined(OPT_BASE)
    double value1 = pow(h,2);
    double value2 = -2/pow(h,5)/pow(pi,(3/2));
    #ifdef OPT_OMP
    #pragma omp parallel for schedule(guided) proc_bind(close)
    #endif
    for (int i = 0; i < N; i++) 
    {
      for (int j = 0; j < N; j++) 
      {
        r[i][j]  = sqrt(pow(dx[i][j],2.0) + pow(dy[i][j],2.0) + pow(dz[i][j],2.0));
        gradPara[i][j] = exp(-pow(r[i][j],2) / value1) * value2;
        wx[i][j] = gradPara[i][j]*dx[i][j];
        wy[i][j] = gradPara[i][j]*dy[i][j];
        wz[i][j] = gradPara[i][j]*dz[i][j];
      }
    }
  #else
    #ifdef OPT_OMP
    #pragma omp parallel for schedule(guided) proc_bind(close) 
    #endif
    for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      // r[i][j] = r[j][i] 
      r[i][j]  = sqrt(pow(dx[i][j],2.0) + pow(dy[i][j],2.0) + pow(dz[i][j],2.0));
      // gradPara[i][j] = gradPara[j][i]
      gradPara[i][j] = -2 * exp(-pow(r[i][j],2) / pow(h,2)) / pow(h,5) / pow(pi,(3/2));
      // wx[i][j] = -wx[j][i]
      wx[i][j] = gradPara[i][j]*dx[i][j];
      wy[i][j] = gradPara[i][j]*dy[i][j];
      wz[i][j] = gradPara[i][j]*dz[i][j];
      //fprintf(stdout, "%12.6f", wy[i][j]);
      //fflush(stdout);
    }
    //fprintf(stdout,"\n");
  }
  #endif
}

void getDensity(double** &pos, double &m, const double h)
{
  getPairwiseSeparations(pos);
  getW(dx, dy, dz, h);
  // 1. todo 访问顺序 
  // 2. 内层 simd
  // 3. 外层omp有数据竞争,内层omp可能伪共享, omp 不适用
  // 4. 简化计算公式 rho[j] += W[i][j] rho[i] *= m;W 每一列计算出一个 rho[j] 此处不适用
  #ifdef OPT_BASE
    for(int i = 0; i < N; i++) 
    {
      for(int j = 0; j < N; j++) 
        rho[j] += m * W[i][j];
    }
  #else
    for (int j = 0; j < N; j++) 
    {
      for (int i = 0; i < N; i++) 
        rho[j] += m * W[i][j];
      //fprintf(stdout, "%12.6f", rho[j]);
      //fflush(stdout);
      //fprintf(stdout,"\n");
    }
  #endif
}

void getPressure(double* &rho, const double k, double &n)
{
  // 1. 提出循环不变量,循环展开
  // 2. omp 线程调度开销大,不合适
  // 3. simd pow较复杂
  #ifdef OPT_BASE
    double value = 1+1/n;
    for (int j = 0; j < N; j++) 
      P[j] = k * pow(rho[j], value);
  #else
    for (int j = 0; j < N; j++) 
    {
      P[j] = k * pow(rho[j], (1+1/n));
      //fprintf(stdout, "%12.6f\n", P[j]);
    }
  #endif
  
}

void getAcc(double** &pos, double** &vel, double &m, const double h, const double k, double &n, double lmbda, const double nu)
{
  getDensity(pos, m, h);
  getPressure(rho, k, n);
  getPairwiseSeparations(pos);
  getGradW(dx, dy, dz, h);
  #if defined(OPT_BASE)
  #ifdef OPT_OMP
  #pragma omp parallel for schedule(guided) proc_bind(close)
  #endif
  for (int j = 0; j < N; j++) 
  {
    // 1. wx[i][j] = -wx[j][i] 访问 w[j][i] 可以增加缓存命中,并且可以向量化
    // 如果for循环交换 j i访问顺序,先访问i 后 j,内层循环无法做向量化优化(内循环每次计算不同的目标元素)
    // 并且,如果for循环先访问j,计算 acc[0][j], P[j]/pow(rho[j],2)是常量可以提出最后计算
    // 2. 简化计算 m* 放在求和之后
    // 3. 循环不变量提出
    double temp1 = P[j]/pow(rho[j],2);
    double temp3 =0.0,temp4=0.0,temp5=0.0;
    for (int i = 0; i < N; i++)
    {
      double temp2 = pow(P[i]/rho[i],2);
      temp3 += (temp1 + temp2) * wx[j][i];
      temp4 += (temp1 + temp2) * wy[j][i];
      temp5 += (temp1 + temp2) * wz[j][i];
    }
    acc[0][j] += (temp3 *=m);
    acc[1][j] += (temp4 *=m);
    acc[2][j] += (temp5 *=m);
  }
  #else
  #ifdef OPT_OMP
  #pragma omp parallel for schedule(guided) proc_bind(close)
  #endif
  for (int j = 0; j < N; j++) 
  {
    for (int i = 0; i < N; i++) 
    {
      acc[0][j] -= m * ( P[j]/pow(rho[j],2) + pow(P[i]/rho[i],2)  ) * wx[i][j];
      acc[1][j] -= m * ( P[j]/pow(rho[j],2) + pow(P[i]/rho[i],2)  ) * wy[i][j];
      acc[2][j] -= m * ( P[j]/pow(rho[j],2) + pow(P[i]/rho[i],2)  ) * wz[i][j];
    }
  }
  #endif
  // 1. simd
  // 2. 循环合并
  #ifdef OPT_BASE
  for (int j = 0; j < N; j++) 
  {
    acc[0][j] -= (lmbda * pos[0][j] + nu * vel[0][j]); 
    acc[1][j] -= (lmbda * pos[1][j] + nu * vel[1][j]); 
    acc[2][j] -= (lmbda * pos[2][j] + nu * vel[2][j]); 
  }
  #else
  for (int j = 0; j < N; j++) 
  {
    acc[0][j] -= lmbda * pos[0][j]; 
    acc[1][j] -= lmbda * pos[1][j];
    acc[2][j] -= lmbda * pos[2][j];
  }
  for (int j = 0; j < N; j++) 
  {
    acc[0][j] -= nu * vel[0][j];
    acc[1][j] -= nu * vel[1][j];
    acc[2][j] -= nu * vel[2][j];
  }
  #endif
}

#ifdef OPT_SIMD
// 需要用到 泰勒展开
float64_t exp_(float64_t x)
{
  //初始化第一个值
  int n = 0;
  double prior = 1.0;
  double sum = prior; //求和保存结果
  while(1)
  {
    double cur = prior * x /++n;
    sum += cur;
    prior = cur;
    if(cur<=EPSILON)
      break;
  }

  return sum;
}

// a^b = e^(b*ln(a)); neon 未提供ln,设想采用 cmath ln函数,向量化对每个元素的计算用 omp task
// float64_t pow_(float64_t a,float64_t b)
// {
//   logf()
// }

#endif

标签:OPT,粒子,double,模拟计算,pos,高性能,dx,include,优化
From: https://www.cnblogs.com/anluo8/p/18596959

相关文章

  • 【Unity 动态资源管理插件】Runtime Asset Database 支持在游戏或应用运行时加载、卸
    RuntimeAssetDatabase是一款针对Unity开发者的强大插件,它允许开发者在运行时动态管理和加载资源。通过该插件,开发者可以构建一个实时的资源数据库,支持在游戏或应用运行时加载、卸载和管理资产,从而优化资源管理和提高性能。此插件特别适用于需要大规模资源管理或实时内容......
  • LS-DYNA及高性能计算评测
    LS-DYNAx86_64二进制文件大多数版本-ifort+MKL可在IntelXeon和AMDEPYC芯片上运行在两种芯片上通过相同的输入产生相同的显式结果(对于隐式,MKL需要特殊的环境变量)附加版本AOCC+AOCL-可在英特尔至强和AMDEPYC芯片上运行在两种芯片上通过相同的输入生......
  • BlueLM-V-3B:在手机上实现高性能多模态大型语言模型的创新路径
    目录一、前言二、方案概述三、技术创新1、动态图像分辨率优化2、硬件感知的系统优化3、令牌下采样4、模型量化与整体框架优化四、方案亮点五、性能展示1、宽松纵横比匹配效果2、不同基准测试中的表现3、部署效率评估六、应用场景1、智能语音助手2、图像识别与理解3、多......
  • [深入探索FireStore Datastore模式:自动扩展与高性能的结合体]
    #引言在现代应用开发中,数据存储是不可或缺的一环。GoogleFirestore以其强大的扩展性与性能,为开发者提供了一种高效的数据存储解决方案。在这篇文章中,我们将深入探讨Firestore的Datastore模式,并学习如何使用它来保存、加载和删除Langchain文档。同时,我们还将探讨一些常见......
  • AbMole| 探索聚多巴胺纳米粒子在椎间盘退变中的作用机制
    来自上海交通大学医学院附属第九人民医院骨科种植体重点实验室的XiaoYang,YanChen,JiadongGuo等多名研究人员发表了题为《PolydopamineNanoparticlesTargetingFerroptosisMitigateIntervertebralDiscDegenerationViaReactiveOxygenSpeciesDepletion,IronI......
  • 基于国产化鸿道Intewell操作系统的高性能实时运动控制解决方案
    工业自动化控制,需要严苛的实时性、稳定性和安全性。随着全球工业自动化技术的不断进步,高性能实时运动控制已成为智能制造的核心,而现在全球竞争紧张的局势下,国产化技术的应用尤为重要,特别是在关键领域和核心产业中。目前国产化高实时运动控制达到了什么样的控制水平呢?软件+......
  • SpringBoot中HTTP高性能客户端实现
    目录1、引入OKHTTP依赖2、配置OkHttpClient客户端实例3、请求调用1、引入OKHTTP依赖 <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.12.0</version> </dependency>2、配置Ok......
  • 路径规划之启发式算法之五:粒子群算法(Particle Swarm Optimization, PSO)
            粒子群算法(ParticleSwarmOptimization,PSO)是一种基于群体智能的优化算法,最早由Eberhart和Kennedy在1995年提出。该算法模拟了鸟群觅食的行为,通过粒子(即潜在的解)在解空间中的迭代搜索来寻找最优解。一、基本思想        粒子群优化算法中,每个解决......
  • 使用 httputils + protostuff 实现高性能 rpc
    1、先讲讲protostufprotostuf一直是高性能序列化的代表之一。但是用起来,可难受了,你得先申明protostuf配置文件,并且要把这个配置文件转成类。所以必然要学习新语法、新工具。可能真的太难受了!于是乎,(有不爽的人)搞了个有创意的框架protostuff(多一个字母“f”)。它借用注解,替代......
  • 高性能计算-NEON-图像旋转
    1.目标:使用NEONintrinsic函数,对512*512png四通道图像顺时针旋转90度。思路:像素分块,对块内转置;再水平镜像。图像库使用stbimg2.代码#include<stdio.h>#include<arm_neon.h>#include<stdlib.h>#defineSTB_IMAGE_IMPLEMENTATION#include"./stb/stb_image.h"......