用SIMD指令加速求复数的模

  • 在使用fftw库傅里叶求频域的时候需要求复数的模来求频谱图,但是fftw库存储复数的类型使用的complex类型,其类型是复数的实部和虚部交替存储,如果将实部和虚部结果分别存到Mat里,再通过magnitude来求,这样的做法对于大图的耗时非常高,由于算法的特性,所以考虑直接用SIMD指令求模。

  • 求摸的公式是:
    在这里插入图片描述
    -算法思想:
    整体算法上没有太大的难度,最难的可能就是,在complex里实部和虚部是交替存放,如何把该部分正确地取出来并作运算,本人使用的方法是这样的:
    在这里插入图片描述

  • 代码如下

 parallel_for_(Range(0, h), [&](const Range& range) {
                for (int i = range.start; i < range.end; i++) {
              //   for (int i = 0; i < h; i++) {
                    int limit = 0;
                    int offset = i * new_w;
                    float* pReal_line = real_ptr + offset;
                    float* pImag_line = imag_ptr + offset;
                    float* pResult_line = result + offset;
                    for (int j = 0;;) {
                       // 剩下的元素处理j
                        for (; j < limit; j++) {
                            float  a = pow(output_array[i * new_w + j][0], 2);
                            float  b = pow(output_array[i * new_w + j][1], 2);
                            *(pResult_line + j) = sqrt(a+b);
                        }
                        if (limit == new_w) {
                            break;
                        }
                      //  AVX
                        if (VTC_HAS_SUPPORT_AVX) {
                            for (; j <= new_w - 8; j += 8) {

                               //  加载实部和虚部
                                __m256 real_parts = _mm256_loadu_ps(&output_array[i * new_w + j][0]);      // 加载实部
                                __m256 imag_parts = _mm256_loadu_ps(&output_array[i * new_w + j+4][0]);  // 加载虚部

                               // 计算实部和虚部的平方
                                __m256 real_squared = _mm256_mul_ps(real_parts, real_parts);
                                __m256 imag_squared = _mm256_mul_ps(imag_parts, imag_parts);

                                __m256 hadd = _mm256_hadd_ps(real_squared, imag_squared);

                                __m256i indices = _mm256_set_epi32(7, 6, 3, 2, 5, 4, 1, 0); // 设置索引
                                __m256  rearrange = _mm256_permutevar8x32_ps(hadd, indices); // 重排vec中的元素

                                // 计算模
                                __m256 magnitudes = _mm256_sqrt_ps(rearrange);

                                // 将结果存储到输出数组
                                _mm256_storeu_ps(pResult_line+j, magnitudes);

                            }

                        }
                     //   SSE
                        for (; j <= new_w - 4; j += 4) {
                            // 加载实部和虚部到寄存器
                            __m128 real = _mm_loadu_ps(pReal_line + j);
                            __m128 imag = _mm_loadu_ps(pImag_line + j);

                            // 计算
                            __m128 real_sq = _mm_mul_ps(real, real);
                            __m128 imag_sq = _mm_mul_ps(imag, imag);

                            // 计算平方和
                            __m128 sum_sq = _mm_add_ps(real_sq, imag_sq);

                            // 计算平方和的平方根(即模)
                            __m128 result = _mm_sqrt_ps(sum_sq);
                            // 将结果存储到结果图中
                            _mm_storeu_ps(pResult_line + j, result);
                           //加载实部和虚部
                            __m128 real_parts = _mm_loadu_ps(&output_array[i * new_w + j][0]);      // 加载实部
                            __m128 imag_parts = _mm_loadu_ps(&output_array[i * new_w + j + 2][0]);  // 加载虚部

                           // 计算实部和虚部的平方
                            __m128 real_squared = _mm_mul_ps(real_parts, real_parts);
                            __m128 imag_squared = _mm_mul_ps(imag_parts, imag_parts);

                            __m128 hadd = _mm_hadd_ps(real_squared, imag_squared);

                            __m128 rearrange = _mm_shuffle_ps(hadd, hadd, _MM_SHUFFLE(3, 1, 2, 0));

                            // 计算模
                            __m128 magnitudes = _mm_sqrt_ps(rearrange);

                            // 将结果存储到输出数组
                            _mm_storeu_ps(pResult_line + j, magnitudes);
                        }
                        limit = new_w;
                    }
                }
                });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值