用SSE指令计算点乘和累加
这几天在做学校的一个学习小项目,需要用到SIMD指令计算提速。也是第一次碰这个,看了一些资料和代码,模仿着写了两个函数。
void sse_mul_float(float *A, float *B, int cnt):两段内存float数据点乘,结果覆盖第一组内存。
float sse_acc_float(float *A, int cnt):一组内存float值累加。
注:
1. 没有考虑中间的精确问题,结果会有误差。
2. 每个函数包括指令操作部分和C++语句计算部分。本文附的代码注释介绍指令部分思路。
**3. 关于内存对齐,我不是很懂,所以下面的代码中判断是否对齐的相关语句我写的也不是很正确,所有后面都补上了一点C++的明白操作。
因此,有些指令操作也许没用上。
头文件
#include "time.h"
#include "stdafx.h"
#include<iostream>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include <math.h>
#include <time.h>
#include <windows.h>
#include <iomanip>
#include <sys/timeb.h>
using namespace std;
sse_mul_float asm部分
//MOV EAX,1 ;request CPU feature flags
//CPUID ;0Fh, 0A2h CPUID instruction
//TEST EDX,4000000h ;test bit 26 (SSE2)
//JNZ >L18 ;SSE2 available int cnt1;
int cnt2;
int cnt3; //we process the majority by using SSE instructions
if (((int)A % ) || ((int)B % )) //如果内存不对齐
{ cnt1 = cnt / ; //该loop一轮处理16个float*float
cnt2 = (cnt - ( * cnt1)) / ; //该loop一轮处理4个float*float
cnt3 = (cnt - ( * cnt1) - ( * cnt2)); //该loop一轮处理1个float*float _asm
{ mov edi, A; //先将内存地址放入指针寄存器
mov esi, B;
mov ecx, cnt1; //循环寄存器置值
jecxz ZERO; //如果数据量不超过16个,则跳过L1 L1: //xmm 寄存器有128bit
//movups XMM,XMM/m128
//传128bit数据,不必对齐内存16字节.
movups xmm0, [edi];
movups xmm1, [edi + ];
movups xmm2, [edi + ];
movups xmm3, [edi + ];
//为什么只载入4*4个float? 到上面看看这一轮需要处理多少数据 movups xmm4, [esi];
movups xmm5, [esi + ];
movups xmm6, [esi + ];
movups xmm7, [esi + ]; //mulps XMM,XMM/m128
//寄存器按双字对齐,
//共4个单精度浮点数与目的寄存器里的4个对应相乘,
//结果送入目的寄存器, 内存变量必须对齐内存16字节.
mulps xmm0, xmm4;
mulps xmm1, xmm5;
mulps xmm2, xmm6;
mulps xmm3, xmm7; //(一个float占4字节,也就是32bit)
//到这里,xmm0-3寄存器里都有了4个float的乘积结果
//然后回载到相应内存
movups[edi], xmm0;
movups[edi + ], xmm1;
movups[edi + ], xmm2;
movups[edi + ], xmm3; //记得给指针移位
//64=16 * 4
//每一轮处理了16次float * float,每一个float占4字节
//所以移位应该加64
add edi, ;
add esi, ; loop L1; ZERO:
mov ecx, cnt2;
jecxz ZERO1; L2: movups xmm0, [edi]; //对于4个float,一个xmm寄存器正好够用
movups xmm1, [esi];
mulps xmm0, xmm1; //对应相乘,结果在xmm0
movups[edi], xmm0; //由xmm0回载内存
add edi, ; //指针移位
add esi, ; loop L2; ZERO1: mov ecx, cnt3;
jecxz ZERO2; mov eax, ; L3: movd eax, [edi]; //对于单个float * float,无需sse指令
imul eax, [esi];
movd[edi], eax;
add esi, ;
add edi, ; loop L3; ZERO2: EMMS; //清空 } }
else
{ cnt1 = cnt / ; //该loop一轮处理28个float*float
cnt2 = (cnt - ( * cnt1)) / ; //该loop一轮处理4个float*float
cnt3 = (cnt - ( * cnt1) - ( * cnt2)); //该loop一轮处理1个float*float _asm
{ mov edi, A;
mov esi, B;
mov ecx, cnt1;
jecxz AZERO; AL1: //movaps XMM, XMM / m128
//把源存储器内容值送入目的寄存器, 当有m128时, 必须对齐内存16字节, 也就是内存地址低4位为0.
movaps xmm0, [edi];
movaps xmm1, [edi + ];
movaps xmm2, [edi + ];
movaps xmm3, [edi + ];
movaps xmm4, [edi + ];
movaps xmm5, [edi + ];
movaps xmm6, [edi + ];
//7*4=28,处理28个float*float mulps xmm0, [esi]; //对应点乘
mulps xmm1, [esi + ];
mulps xmm2, [esi + ];
mulps xmm3, [esi + ];
mulps xmm4, [esi + ];
mulps xmm5, [esi + ];
mulps xmm6, [esi + ]; movaps[edi], xmm0; //回载
movaps[edi + ], xmm1;
movaps[edi + ], xmm2;
movaps[edi + ], xmm3;
movaps[edi + ], xmm4;
movaps[edi + ], xmm5;
movaps[edi + ], xmm6; add edi, ;
add esi, ; loop AL1; AZERO:
mov ecx, cnt2;
jecxz AZERO1; AL2: movaps xmm0, [edi];
mulps xmm0, [esi];
movaps[edi], xmm0;
add edi, ;
add esi, ; loop AL2; AZERO1: mov ecx, cnt3;
jecxz AZERO2; mov eax, ; AL3: movd eax, [edi];
imul eax, [esi];
movd[edi], eax;
add esi, ;
add edi, ; loop AL3; AZERO2: EMMS; } }
由于内存对齐的问题,导致末尾有部分数据不正常,特添加C++部分修复。
sse_mul_float c++部分
int start;
start = cnt - (cnt % );
for (int i = start; i < cnt; i++)
{
A[i] *= B[i];
}
用于累加的这个函数,分两块。一块是用指令把大部分数据处理掉,而极少部分数据使用C++语句,这样能各取所长。
sse_acc_float asm部分
float temp = ; int cnt1;
int cnt2;
int cnt3;
int select = ; //we process the majority by using SSE instructions
if (((int)A % )) //unaligned 如果这次调用,内存数据不对齐
{
select = ; cnt1 = cnt / ;
cnt2 = (cnt - ( * cnt1)) / ;
cnt3 = (cnt - ( * cnt1) - ( * cnt2)); __asm
{ mov edi, A;
mov ecx, cnt1;
pxor xmm0, xmm0;
jecxz ZERO; L1: movups xmm1, [edi];
movups xmm2, [edi + ];
movups xmm3, [edi + ];
movups xmm4, [edi + ];
movups xmm5, [edi + ];
movups xmm6, [edi + ]; //addps 对应相加
//结果返回目的寄存器
addps xmm1, xmm2;
addps xmm3, xmm4;
addps xmm5, xmm6; addps xmm1, xmm5;
addps xmm0, xmm3; addps xmm0, xmm1;
//至此,xmm0内4个float的和就是24个float的和 add edi, ; loop L1; ZERO: movd ebx, xmm0; //低4个字节(第一个float)传入ebx
psrldq xmm0, ; //xmm0右移4字节
movd eax, xmm0; //右移后,低4个字节(第二个float)传入eax movd xmm1, eax; //第一个float传入xmm1低32bit
movd xmm2, ebx; //第二个float传入xmm2低32bit
addps xmm1, xmm2; //两个寄存器内4个float对应相加
movd eax, xmm1; //只取我们要的低位float,传入eax
movd xmm3, eax; //第一和第二个float的和存在xmm3低32位
psrldq xmm0, ; //又截掉一个float movd ebx, xmm0; //第三个float进ebx
psrldq xmm0, ; //截掉第三个float
movd eax, xmm0; //第四个float进eax movd xmm1, eax;
movd xmm2, ebx;
addps xmm1, xmm2; //第三和第四个float的和存在xmm1低32位
movd eax, xmm1;
movd xmm4, eax;
addps xmm3, xmm4; //4个float的和存在xmm3低32位 movd eax, xmm3;
mov temp, eax; //这部分求和存在temp地址区 EMMS; }
}
else // aligned 如果这次调用,内存数据对齐
{
select = ; cnt1 = cnt / ;
cnt2 = (cnt - ( * cnt1)) / ;
cnt3 = (cnt - ( * cnt1) - ( * cnt2)); __asm
{ mov edi, A;
mov ecx, cnt1;
pxor xmm0, xmm0;
jecxz ZZERO; LL1: movups xmm1, [edi];
movups xmm2, [edi + ];
movups xmm3, [edi + ];
movups xmm4, [edi + ];
movups xmm5, [edi + ];
movups xmm6, [edi + ]; addps xmm1, xmm2;
addps xmm3, xmm4;
addps xmm5, xmm6;
addps xmm1, xmm5;
addps xmm0, xmm3;
addps xmm0, xmm1; add edi, ; movups xmm1, [edi];
movups xmm2, [edi + ];
movups xmm3, [edi + ];
movups xmm4, [edi + ];
movups xmm5, [edi + ];
movups xmm6, [edi + ]; addps xmm1, xmm2;
addps xmm3, xmm4;
addps xmm5, xmm6;
addps xmm1, xmm5;
addps xmm0, xmm3;
addps xmm0, xmm1; add edi, ; movups xmm1, [edi];
movups xmm2, [edi + ]; addps xmm1, xmm2;
addps xmm0, xmm1; add edi, ; loop LL1; ZZERO: movd ebx, xmm0;
psrldq xmm0, ;
movd eax, xmm0; movd xmm1, eax;
movd xmm2, ebx;
addps xmm1, xmm2;
movd eax, xmm1;
movd xmm3, eax;
psrldq xmm0, ; movd ebx, xmm0;
psrldq xmm0, ;
movd eax, xmm0; movd xmm1, eax;
movd xmm2, ebx;
addps xmm1, xmm2;
movd eax, xmm1;
movd xmm4, eax;
addps xmm3, xmm4; movd eax, xmm3;
mov temp, eax; EMMS; }
}
sse_acc_float c++部分
//上面的select记录本次调用sse_acc_float时,数据是否对齐内存
//下面分情况把剩余的和累加
int start;
float c = 0.0f;
if (select == )
{ start = cnt - (cnt % );
for (int i = start; i < cnt; i++)
{
c += A[i];
} }
else
{
start = cnt - (cnt % );
for (int i = start; i < cnt; i++)
{
c += A[i];
} } //temp 是用指令计算 ,大部分数据的和
//c 是用C++语句计算, 所有数据模24或者56剩余部分数据的和
return(temp + c);
推荐参考:SIMD(单道指令多道数据流)指令(MMX/SSE1/SSE2)详解(中文).
我是一名编程菜鸟,有什么技术上的问题,欢迎讨论和交流指正。谢谢!
获取全部源码:点此 dot_acc.cpp
用SSE指令计算点乘和累加的更多相关文章
- AVX 指令详解 ,还有SSE指令
https://blog.csdn.net/fengbingchun/article/details/23598709 本人从来不复制的,自己看!.
- SIMD指令集——一条指令操作多个数,SSE,AVX都是,例如:乘累加,Shuffle等
SIMD指令集 from:https://zhuanlan.zhihu.com/p/31271788 SIMD,即Single Instruction, Multiple Data,一条指令操作多个数 ...
- SIMD学习 -- 用SSE2指令作点乘和累加计算
这几天在做学校的一个学习小项目,需要用到SIMD指令计算提速.也是第一次碰这个,看了一些资料和代码,模仿着写了两个函数. void sse_mul_float(float *A, float *B, ...
- SSE图像算法优化系列十七:多个图像处理中常用函数的SSE实现。
在做图像处理的SSE优化时,也会经常遇到一些小的过程.数值优化等代码,本文分享一些个人收藏或实现的代码片段给大家. 一.快速求对数运算 对数运算在图像处理中也是个经常会遇到的过程,特备是在一些数据压缩 ...
- SSE图像算法优化系列十八:三次卷积插值的进一步SSE优化。
本文是在学习https://blog.csdn.net/housisong/article/details/1452249一文的基础上对算法的理解和重新整理,再次非常感谢原文作者的深入分析以及分享. ...
- SSE图像算法优化系列二十一:基于DCT变换图像去噪算法的进一步优化(100W像素30ms)。
在优化IPOL网站中基于DCT(离散余弦变换)的图像去噪算法(附源代码) 一文中,我们曾经优化过基于DCT变换的图像去噪算法,在那文所提供的Demo中,处理一副1000*1000左右的灰度噪音图像耗时 ...
- SSE图像算法优化系列十四:局部均方差及局部平方差算法的优化。
关于局部均方差有着较为广泛的应用,在我博客的基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用及使用局部标准差实现图像的局部对比度增强算法中都有谈及,即可以用于去噪也可以用来增强图像,但是 ...
- SSE图像算法优化系列五:超高速指数模糊算法的实现和优化(10000*10000在100ms左右实现)。
今天我们来花点时间再次谈谈一个模糊算法,一个超级简单但是又超级牛逼的算法,无论在效果上还是速度上都可以和Boxblur, stackblur或者是Gaussblur想媲美,效果上,比Boxblur来的 ...
- SSE图像算法优化系列一:一段BGR2Y的SIMD代码解析。
一个同事在github上淘到一个基于SIMD的RGB转Y(彩色转灰度或者转明度)的代码,我抽了点时间看了下,顺便学习了一些SIMD指令,这里把学习过程中的一些理解和认识共享给大家. github上相关 ...
随机推荐
- svn conflicts: local delete, incoming delete upon update
svn st查看更新的时候发现存在conflicts,提示很多 local delete, incoming delete upon update , $:svn st ? C IMIRROR.T3 ...
- WebAppBuilder自定义主题
WebAppBuilder自定义主题 by 李远祥 基本步骤: 创建新主题的文件夹 注册新的主题到manifest.json 文件 覆盖HeaderController 部件的颜色. 覆盖panel的 ...
- 重新学习WCF
近来工作不怎么忙,一直在想一个问题,今年刚刚毕业,对于我们这应届生到底应该学习那些技术呢? 面对着现在技术横生,到底哪项是适合自己的呢?自己一直都在迷茫,若有那位大神再次经过,望给出您宝贵的建议. 最 ...
- GET和POST的区别,何时使用POST?
GET:一般用于信息获取,使用URL传递参数,对所发送信息的数量也有限制,一般在2000个字符 POST:一般用于修改服务器上的资源,对所发送的信息没有限制. GET方式需要使用Re ...
- Angular2组件与指令的小实践——实现一个图片轮播组件
如果说模块系统是Angular2的灵魂,那其组件体系就是其躯体,在模块的支持下渲染出所有用户直接看得见的东西,一个项目最表层的东西就是组件呈现的视图.而除了直接看的见的躯体之外,一个完整的" ...
- 怎样的 Hash 算法能对抗硬件破解
前言 用过暴力破解工具 hashcat 的都知道,这款软件的强大之处在于它能充分利用 GPU 计算,比起 CPU 要快很多.所以在破解诸如 WiFi 握手包.数据库中的口令 Hash 值时,能大幅提高 ...
- linux 下安装eclipse和pydev插件用来开发python程序
1.网上的教程要求必须要安装java虚拟机,但是我觉得不用,但是为了不出意外就装吧. (1)去官网下载安装包www.sun.com (2)安装包是.tar.gz的可以用 tar -xzvf +压缩包路 ...
- Android学习20--OpenGL的"mapPoints"
在OpenGL中有时会需要求一个3维空间中的点在平移(缩放,旋转)后坐标是多少.需求相当于二维的mapPoints.可以通过这个函数实现 void multiplyMV (float[] result ...
- JavaScript 基本类型值-Number类型
▓▓▓▓▓▓ 大致介绍 在JavaScript的内部采用IEEE754格式来表示数字,所以不区分整数和浮点数,都是用64位浮点数的形式储存.就是说,在JavaScript内部,就根本没有小数.但是有些 ...
- Python2.7学习笔记-定义函数、filter/map/reduce/lambda
我把写的代码直接贴在下面了,注释的不是很仔细,主要是为了自己复习时方便查找,并不适合没有接触过python的人看,其实我也是初学者. #定义函数 def my_abs(x): if x>=0: ...