使用OpenMP线程和std::( experimentalal::) simd计算Mandelbrot集

如何解决使用OpenMP线程和std::( experimentalal::) simd计算Mandelbrot集

我希望使用不同类型的HPC范例来实现一个简单的Mandelbrot集绘图仪,以显示它们的优缺点以及实现的难易程度。考虑一下GPGPU(CUDA / OpenACC / OpenMP4.5),线程/ OpenMP和MPI。并使用这些示例为刚接触HPC的程序员提供帮助,并了解可能的情况。代码的清晰性比从硬件中获得绝对最高的性能更为重要,这是第二步;)

由于该问题很难并行化,并且现代CPU可以使用矢量指令获得大量性能,因此我也想将OpenMP和SIMD结合起来。不幸的是,简单地添加#pragma omp simd并不能产生令人满意的结果,并且使用内在函数不是非常用户友好或未来的证明。或pretty

幸运的是,正在按照C ++标准进行工作,以便更容易地通用实现矢量指令,如TS:"Extensions for parallelism,version 2"中所述,尤其是关于数据并行类型的第9节。可以在here的基础上找到here的WIP实现。

假设我有以下课程(已更改为使其更简单一些)

#include <stddef.h>

using Range = std::pair<double,double>;
using Resolution = std::pair<std::size_t,std::size_t>;

class Mandelbrot
{
    double* d_iters;
    Range d_xrange;
    Range d_yrange;
    Resolution d_res;
    std::size_t d_maxIter;
    
public:
    Mandelbrot(Range xrange,Range yrange,Resolution res,std::size_t maxIter);
    ~Mandelbrot();

    void writeImage(std::string const& fileName);
    void computeMandelbrot();
private:
    void calculateColors();
}; 

以及以下computeMandelbrot()使用OpenMP的实现

void Mandelbrot::computeMandelbrot()
{
    double dx = (d_xrange.second - d_xrange.first) / d_res.first;
    double dy = (d_yrange.second - d_yrange.first) / d_res.second;

    #pragma omp parallel for schedule(dynamic)
    for (std::size_t row = 0; row != d_res.second; ++row)
    {
        double c_imag = d_yrange.first + row * dy;
        for (std::size_t col = 0; col != d_res.first; ++col)
        {
            double real = 0.0;
            double imag = 0.0;
            double realSquared = 0.0;
            double imagSquared = 0.0;
            double c_real = d_xrange.first + col * dx;

            std::size_t iter = 0;
            while (iter < d_maxIter && realSquared + imagSquared < 4.0)
            {
                realSquared = real * real;
                imagSquared = imag * imag;
                imag = 2 * real * imag + c_imag;
                real = realSquared - imagSquared + c_real;
                ++iter;
            }
            d_iters[row * d_res.first + col] = iter;
        }   
    }
}

我们可以假定x和y方向的分辨率均为2/4/8 / ..的倍数,具体取决于我们使用的SIMD指令。

很遗憾,std::experimental::simd上在线提供的信息很少。据我所知,也没有任何不平凡的例子。

在Vc git存储库中,有一个Mandelbrot集计算器的实现,但是它非常复杂,并且由于缺少注释而很难遵循。

很明显,我应该在函数computeMandelbrot()中更改double的数据类型,但是不确定。 TS提到了某些T类型的两种主要新数据类型,

native_simd = std::experimental::simd<T,std::experimental::simd_abi::native>;

fixed_size_simd = std::experimental::simd<T,std::experimental::simd_abi::fixed_size<N>>;

使用native_simd最有意义,因为在编译时我不知道边界。但是对我来说,这些类型的含义是什么还不清楚,native_simd<double>是单双精度还是是在其上执行了矢量指令的双精度集合?然后这个集合中有多少双打?

如果有人可以向我指出使用这些概念的示例,或者给我一些有关如何使用std :: experimental :: simd实现矢量指令的指针,我将不胜感激。

解决方法

这是一个非常基本的实现,有效(据我所知)。矢量的哪些元素的绝对值大于2的测试是通过非常繁琐且效率低下的方式完成的。一定有更好的方法可以做到这一点,但我还没有找到。

在AMD Ryzen 5 3600上,我获得了大约72%的性能提升,并为g ++提供了-march=znver2选项,这比预期的要少。

template <class T>
void mandelbrot(T xstart,T xend,T ystart,T yend)
{
    namespace stdx = std::experimental;

    constexpr auto simdSize = stdx::native_simd<T>().size();
    constexpr unsigned size = 4096;
    constexpr unsigned maxIter = 250;

    assert(size % simdSize == 0);
    unsigned* res = new unsigned[size * size];

    T dx = (xend - xstart) / size;
    T dy = (yend - ystart) / size;

    for (std::size_t row = 0; row != size; ++row)
    {
        T c_imag = ystart + row * dy;
        for (std::size_t col = 0; col != size; col += simdSize)
        {
            stdx::native_simd<T> real{0};
            stdx::native_simd<T> imag{0};
            stdx::native_simd<T> realSquared{0};
            stdx::native_simd<T> imagSquared{0};
            stdx::fixed_size_simd<unsigned,simdSize> iters{0};

            stdx::native_simd<T> c_real;
            for (int idx = 0; idx != simdSize; ++idx)
            {
                c_real[idx] = xstart + (col + idx) * dx;
            }

            for (unsigned iter = 0; iter != maxIter; ++iter)
            {
                realSquared = real * real;
                imagSquared = imag * imag;
                auto isInside = realSquared + imagSquared > stdx::native_simd<T>{4};
                for (int idx = 0; idx != simdSize; ++idx)
                {
                    // if not bigger than 4,increase iters
                    if (!isInside[idx])
                    {
                        iters[idx] += 1;
                    }
                    else
                    {
                        // prevent that they become inf/nan
                        real[idx] = static_cast<T>(4);
                        imag[idx] = static_cast<T>(4);
                    }
                }

                if (stdx::all_of(isInside) )
                {
                    break;
                }        

                imag = static_cast<T>(2.0) * real * imag + c_imag;
                real = realSquared - imagSquared + c_real;
            }
            iters.copy_to(res + row * size + col,stdx::element_aligned);
        }

    }
    delete[] res;
}

整个测试代码(从auto test = (...)开始编译为

  .L9:
  vmulps ymm1,ymm1,ymm1
  vmulps ymm13,ymm2,ymm2
  xor eax,eax
  vaddps ymm2,ymm13,ymm1
  vcmpltps ymm2,ymm5,ymm2
  vmovaps YMMWORD PTR [rsp+160],ymm2
  jmp .L6
.L3:
  vmovss DWORD PTR [rsp+32+rax],xmm0
  vmovss DWORD PTR [rsp+64+rax],xmm0
  add rax,4
  cmp rax,32
  je .L22
.L6:
  vucomiss xmm3,DWORD PTR [rsp+160+rax]
  jp .L3
  jne .L3
  inc DWORD PTR [rsp+96+rax]
  add rax,32
  jne .L6

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-