SIMD学习笔记1

news/2024/6/17 14:34:43 标签: 学习, 笔记

参考 http://const.me/articles/simd/simd.pdf
https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#cats=Arithmetic&expand=3904,3913,4011,34,4014,4602,4011&techs=MMX,AVX_512,AMX,SVML,Other&ig_expand=11

示例:对4个数字求平方

void mul4_scalar( float* ptr ) 
{ 
	for( int i = 0; i < 4; i++ ) 
	{ 
		const float f = ptr[ i ]; 
		ptr[ i ] = f * f; 
	} 
} 

使用SIMD

void mul4_vectorized( float* ptr ) 
{ 
	__m128 f = _mm_loadu_ps( ptr ); 
	f = _mm_mul_ps( f, f ); 
	_mm_storeu_ps( ptr, f ); 
} 

解释:

函数 _mm_loadu_ps

Load 128-bits (composed of 4 packed single-precision (32-bit) floating-point elements) from memory into dst. mem_addr does not need to be aligned on any particular boundary.
加载128比特数据(4个float类型)

函数__m128 _mm_mul_ps (__m128 a, __m128 b)

Synopsis
__m128 _mm_mul_ps (__m128 a, __m128 b)
#include <xmmintrin.h>
Instruction: mulps xmm, xmm
CPUID Flags: SSE
Description
Multiply packed single-precision (32-bit) floating-point elements in a and b, and store the results in dst.

函数 void _mm_storeu_ps (float* mem_addr, __m128 a)

#include <immintrin.h>
Instruction: movups m128, xmm
CPUID Flags: SSE
Description
Store 128-bits (composed of 4 packed single-precision (32-bit) floating-point elements) from a into memory. mem_addr does not need to be aligned on any particular boundary.

#include <iostream>
#include <vector>
#include <immintrin.h>

void vectorAddSSE(const std::vector<double>& v1, const std::vector<double>& v2, std::vector<double>& result)
{
    // Ensure vectors are of the same size
    size_t size = v1.size();
    if (size != v2.size() || size != result.size())
    {
        std::cerr << "Vector sizes mismatch." << std::endl;
        return;
    }

    // Process 4 elements at a time using SSE
    for (size_t i = 0; i < size; i += 4)
    {
        // Load 4 double values from each vector
        __m256d vec1 = _mm256_loadu_pd(&v1[i]);
        __m256d vec2 = _mm256_loadu_pd(&v2[i]);

        // Perform vector addition
        __m256d resultVec = _mm256_add_pd(vec1, vec2);

        // Store the result back to the result vector
        _mm256_storeu_pd(&result[i], resultVec);
    }

    // Process the remaining elements (if any) without SSE
    for (size_t i = size - size % 4; i < size; ++i)
    {
        result[i] = v1[i] + v2[i];
    }
}

int main()
{
    std::vector<double> v1 = {1.0, 2.0, 3.0, 4.0};
    std::vector<double> v2 = {5.0, 6.0, 7.0, 8.0};
    std::vector<double> result(4);

    vectorAddSSE(v1, v2, result);

    // Output the result
    std::cout << "Result: ";
    for (double value : result)
    {
        std::cout << value << " ";
    }
    std::cout << std::endl;

    return 0;
}

进阶:求矩阵的和

#pragma once
#include<vector>
#include<string>
template <typename T>
class Matrix {
private:
    std::vector<std::vector<T>> matrix;
    int m_nRows;
    int m_nCols;

public:
    // 构造函数
    Matrix(int m_nRows, int m_nCols, const T& initial = T());
    // 获取矩阵的行数和列数
    int numRows() const;  
    int numCols() const;

    // 获取矩阵中特定位置的元素
    T& at(int row, int col);
    // 获取矩阵元素的值
    T get(int row, int col) const;

    // 更新矩阵元素的值
    void set(int row, int col, const T& value);

    // 矩阵加法
    Matrix<T> operator+(const Matrix<T>& other) const;
  
    // 矩阵减法
    Matrix<T> operator-(const Matrix<T>& other) const;
    
    // 矩阵乘法
    Matrix<T> operator*(const Matrix<T>& other) const;

    //[]操作符重载 
    const std::vector<T>& operator[](size_t row) const;
    //[]操作符重载 
    std::vector<T>& operator[](size_t row);
    
    // 掩膜操作
    Matrix<T> mask(const Matrix<T>& mask, T value = static_cast<T>(0));
    // 统计矩阵中某个值的数量
    int countValue(const T& value) const;

    //保存到图片
    void saveImage(const std::string& filename);

    void printPart(int x, int y, int step, std::string s = "");
private:
    
};



//sse逐点相
// lineAdd_sse 的特化的前向声明
template <typename T>
inline void lineAdd_sse(const T* left, const T* right, int nCounts, T* save) {};
template <>
inline void lineAdd_sse(const double* left, const double* right, int size, double* save);
template <>
inline void lineAdd_sse(const float* left, const float* right, int size, float* save);
template <>
inline void lineAdd_sse(const int* left, const int* right, int size, int* save);

/ 矩阵加法 sse
template <typename T>
Matrix<T> MatrixAdditionSSE(const Matrix<T>& m1, const Matrix<T>& m2);

实现

template<>
inline void lineAdd_sse(const float* left, const float* right, int size, float* save)
{
    //Process 8 float elements at a time using SSE
    for (int i = 0; i < size-7; i += 8)
    {
        _mm256_storeu_ps(save + i, _mm256_add_ps(_mm256_loadu_ps(left + i), _mm256_loadu_ps(right + i)));
    }
    // Process the remaining elements (if any) without SSE
    for (size_t i = size - size % 8; i < size; ++i)
    {
        save[i] = left[i] + right[i];
    }
}
template<>
inline void lineAdd_sse(const double* left, const double* right, int size, double* save)
{
    // Process 4 double elements at a time using SSE
    for (size_t i = 0; i < size-3; i += 4)
    {
        // Load 4 double values from each vector
        __m256d vec1 = _mm256_loadu_pd(&left[i]);
        __m256d vec2 = _mm256_loadu_pd(&right[i]);

        // Perform vector addition
        __m256d resultVec = _mm256_add_pd(vec1, vec2);

        // Store the result back to the result vector
        _mm256_storeu_pd(&save[i], resultVec);
    }

    // Process the remaining elements (if any) without SSE
    for (size_t i = size - size % 4; i < size; ++i)
    {
        save[i] = left[i] + right[i];
    }
}
 
template <>
inline void lineAdd_sse(const int* left, const int* right, int size, int* save)
{
    // Process 8 int elements at a time using SSE
    for (int i = 0; i < size - 7; i += 8)
    {
        __m256i vec1 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&left[i]));
        __m256i vec2 = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&right[i]));

        // Perform vector addition
        __m256i resultVec = _mm256_add_epi32(vec1, vec2);

        // Store the result back to the result vector
        _mm256_storeu_si256(reinterpret_cast<__m256i*>(&save[i]), resultVec);
    }

    // Process the remaining elements (if any) without SSE
    for (int i = size - size % 8; i < size; ++i)
    {
        save[i] = left[i] + right[i];
    }
}



template <typename T>
Matrix<T> MatrixAdditionSSE(const Matrix<T>& m1, const Matrix<T>& m2)
{
    static_assert(sizeof(T) == sizeof(float) || sizeof(T) == sizeof(double) || sizeof(T) == sizeof(int),
        "Unsupported element type for SSE");
    if (m1.numRows() != m2.numRows() || m1.numCols() != m2.numCols())
    {
        throw std::invalid_argument("Matrix dimensions don't match for addition");
    }

    Matrix<T> result(m1.numRows(), m1.numCols());
    std::vector<std::thread> threads;
    const int numThreads = std::thread::hardware_concurrency(); // Number of available threads
    const int rowsPerThread = (m1.numRows() + numThreads - 1) / numThreads; // Rows per thread

    for (int i = 0; i < numThreads; ++i)
    {
        threads.emplace_back([&m1, &m2, &result, i, rowsPerThread]()
            {
                for (int row = i * rowsPerThread; row < std::min((i + 1) * rowsPerThread, result.numRows()); ++row)
                {
                    lineAdd_sse(&m1[row][0], &m2[row][0], result.numCols(), &result[row][0]);
                }
            });
    }

    for (auto& thread : threads)
    {
        thread.join();
    }

    return  std::move(result);
}

http://www.niftyadmin.cn/n/5366799.html

相关文章

网络选择流程分析(首选网络类型切换流程)

首先是界面,我在此平台的界面如下: 对应的入口源码位置在Settings的UniEnabledNetworkModePreferenceController中,当然其他平台可能在PreferredNetworkModePreferenceController中,流程上都是大同小异 然后点击切换按钮会调用到UniEnabledNetworkModePreferenceControlle…

(已解决)vue+element-ui实现个人中心,仿照原神

差一个个人中心页面&#xff0c;看到了这个博主的个人中心&#xff0c;真的很不错 地址&#xff1a;vueelement仿原神实现好看的个人中心 最终效果&#xff1a;

中移在线:基于openGauss的数据库自主创新替代实践

中移在线基于openGauss输出了安全可控、数据无损、业务低感知的自主创新数据库替代方案&#xff0c;不仅满足了业务应用的自主创新转型要求&#xff0c;其高可用架构和高性能优势也提升了业务系统的稳定性和服务效率。 应用场景 中国移动在线营销服务中心是中国移动客户服务的…

Leetcode 3026. Maximum Good Subarray Sum

Leetcode 3026. Maximum Good Subarray Sum 1. 解题思路2. 代码实现 题目链接&#xff1a;3026. Maximum Good Subarray Sum 1. 解题思路 这一题的话主要就是要快速遍历所有的good subarray并快速获得每一个good subarray的和的最大值。 因此&#xff0c;问题就主要就成了两…

大数据可视化/算法推荐/情感分析——基于Django电影评论数据可视化分析推荐系统(完整系统源码+数据库+详细文档+论文+部署教程)

文章目录 大数据可视化/算法推荐/情感分析——基于Django电影评论数据情感分析可视化分析推荐系统源码资料获取方式在文章末尾 一、 选题背景二、研究目的三、开发技术介绍1、Django框架2、LDA3、机器学习推荐算法4、大数据爬虫5、大数据Echarts可视化 四、系统设计思想五、部分…

基于Vue的移动端UI框架整理

一、Vant 官方地址&#xff1a;https://youzan.github.io/vant/#/zh-CN/ 简介&#xff1a;有赞公司开发。 特性&#xff1a;60 高质量组件、90% 单元测试覆盖率、完善的中英文文档和示例、支持按需引入、支持主题定制、支持国际化、支持 TS、支持 SSR。 特别说明&#xff1…

【0256】揭晓pg内核中MyBackendId的分配机制(后端进程Id,BackendId)(二)

上一篇:【0255】揭晓pg内核中MyBackendId的分配机制(后端进程Id,BackendId)(一) 文章目录 1. 前言2. 分配BackendId2.1 何时为backend process分配BackendId2.1.1 找出未使用的slot(inactive slot)2.3 BackendId序号从多少开始?2.4 后端进程退出后,其BackendId被释放…

5.0 HDFS 集群服务建立教程

HDFS 集群是建立在 Hadoop 集群之上的&#xff0c;由于 HDFS 是 Hadoop 最主要的守护进程&#xff0c;所以 HDFS 集群的配置过程是 Hadoop 集群配置过程的代表。 使用 Docker 可以更加方便地、高效地构建出一个集群环境。 每台计算机中的配置 Hadoop 如何配置集群、不同的计…