窃下以为不可能有比循环累加更快的了,既然是相加,不管它的存储是否连续,在内存里什么形式,都要取出来放到CPU的寄存器里去,不可能直接去把内存上存的01给加了。这样的话,对于一个整数数组,可以随机寻址,从第100加到第200寻址是很快的。应该不会有更快的了。
解决方案 »
- MaskedTextBox设置美国日期格式的问题
- 求c#高级编程第6版 中文PDF
- c# .net 把一个文件夹下的文件复制到另一个文件夹下
- 在系统分析阶段,用户要求设计软件界面,大家一般用什么设计?
- 考考大家,一个基础知识的引申
- c#中,点button后,跳转到另外一个winform,原先的一个winform关闭
- 怎样在WIinform点击按钮,得到当前点击按钮的Text内容?
- progressbar的问题
- 如何计算出一个textbox有多少行字?
- 清水无鱼来 ,谢谢 && 谢谢
- 一个窗体上有多个panel,根据要求切换显示不同的面版里的控件,但这样有问题,请指教!
- ITC(深圳)高薪诚聘 PHP 软件工程师,各位帮忙UP。
如果你的计算机上有两颗CPU或者有超线程的话,这样做是有意义的,因为这样可以同时做两个加法,可是如果只是普通的CPU的话,这样做只有浪费时间。
可以限制为都是byte型的例如
地址 数值
17888423100 66
17888423101 34
17888423102 21
17888423103 78
.........................
17888423199 12把这个地址段内的全部相加求和
一个全局的名字空间
松散地同步执行
通过这个我们说明一个单程序,其对数组定义操作,数组元素由编译器分配到各个处理器。实现可以允许处理器同步地操作,但许多程序结构将迫使同步-例如所有数组元素求和的计算。一个数据并行编译器(或环境)将提供某些基本的功能,以使大范围的算法有效地执行。一种数据并行语言将提供大多数--如果不是全部的--下列功能:全数组元素的操作 例如 a = b + 2*c, 其中a,b和c是数组。部分数组 指出一个数组的子集的能力:例如一行或一列。条件操作 根据数组元素或逻辑值的判断条件,对数组元素某个集合的操作能力。这允许一些操作,象在数组内对非零元素取反。归并 例如把一个数组中的全部元素相加求和的操作。移位 沿着一个数组的给定的维移动元素。这对模板操作是很重要的,这应用在图象处理中,例如求解微分方程。一般的通讯 例如从一个向量发送数据到一个三维数组的指定元素。并行前缀/后缀操作 例如一个正在运行的全部操作。对数据分配的控制 控制数据怎样在处理器间分配,控制对象的对准以减小通讯。注意这些是高级操作,能被有效地实现。用编译器并行化在某个数组元素上定义操作的等量循环是更难的。随着数据并行语言的研究,上面所列的越来越多的特点成为可得到的。最早的数据并行语言大约在1973年,关于ILLIAC IV[4]机器的。例如,IVTRAN[9]是以FORTRAN为基础,有一个并行DO结构(DO FOR ALL),其和IF同时使用表示并行中的条件操作。它也可以表示对给定的维进行计算,以暗示编译器数据怎样分配才能有最小的通讯。在同一年,一台数据并行ALFOL 60出现,是基于Glypnir[10]语言,它也有类数组表达式和一种并行IF语句。但是,并行对象被称为Super Words(swords),为匹配机器尺寸(处理器数)其被限制。后来,在1979年,基于Pascal的Actus[11],为定义一个更完全的操作集加入环和去尾移动(rotate,shift)以及归并操作。最近,一些数据并行语言已出现,他们是一些语言的很好扩展。例如,并行Pascal[12](NASA MPP,1983),增强的Fortran-Plus[13](AMT DAP,1990)和Connection Machine C*[14](Thinking Machines.1990)。虽然在九十年代初,出现一些语言,但是他们有太多的方言,并且也有依赖机器的一些特点。这意味着用户被局限于某个生产商,而且未来的用户不愿意学习一个特别的语言。在我们努力介绍一些标准时,HPF讨论组成立了,其肩负着定义一个数据并行Fortran标准的任务。
--------------------------------------------------------------------------------
Copyright: NPACT
cpu里的确有内存加内存的指令,但那不是在内存里面做加法,也是要把内存里的数据读到寄存器里面再做加法,最后再写到内存里面。
如果你能力强的话,可以用汇编这样做:把数据成批拷贝到cache里面,那样比较快,然后从cache里面读数据到寄存器做加法,虽然复杂度没有减少,但是可以减少传输数据的时间,但是永远没有办法减少做加法的时间,除非使用并行计算。
如果你是搞统计软件,那你自己也应该知道此类运算是极度耗资源的,数组一上了内存再处理,如果已经做到了这步,那还能改进的余地极小,VS2004中的VC有针对CPU的优化。。
你看如下的算法,pMT是个指针for(m=0;m<lTempHeight;m++)
{
for(n=0;n<nMWidth;n++)
{
dSigmaT+=(double)pMT[0];
dSigmaT+=(double)pMT[1];
dSigmaT+=(double)pMT[2];
pMT += 3;
}
}
你处理每个图片的算法到底是怎么样的?
其实你图片都有1024*768,如果里面的算法复杂度再大一些的话,算得慢也是很正常的。
可以每100数为一组创建一个线程。
这样充分利用cpu。再有就是直接用汇编写代码,速度可以进一步优化。
如果1024*768个int32类型相加,long(int64)类型就足够了。
第二,镶套循环可以改为单循环,如
for (i=m*n-1; i>=0; i--)
{
//加法代码
}
可以快很多,
第三,循环变量直接使用指针,可以快一点
for (p=Start; p<End; p+=3) {//代码} 可以再快一点,
如果还达不到要求,那只有使用汇编语言写了,可以省去结果变量重复读出寄存器的问题,
不过,我个人认为,还是要从算法复杂度上解决问题,因为计算机的运算速度是有限的,
不可能做出超出极限的事情,多线程就不用考虑了,同一个CPU,不可能会更快了,不过
可以提高线程的优先级.
int c = lTempHeight * nMWidth;for (int i = 0; i < c; i++)
{
dSigmaT+=(double)*pMT;
pMT++;
}如果你的代码里面确实就只有这几句话,没有更多的跟长宽相关的东西。
for(n=0...)本身包含了一次和循环本身没有关系的赋值,也就是说如果你for(m...)这里循环1000次,那就会多一千个赋值。此外,不知道pMT[2]这样子的语句是否会比*pMT慢一点?因为也许pMT[2]的汇编是
mov ah, [esi+ebx]
而*pMT的汇编是
mov ah, [esi]
此外,如果你这个dSigmaT本身在循环内部仅仅是相加,而没有别的什么浮点运算的话,那么我建议你进一步改成:
int c = lTempHeight * nMWidth;
int iTmp = 0;for (int i = 0; i < c; i++)
{
iTmp += *pMT;
pMT++;
}dSigmaT = (double) iTmp;
要知道浮点的加可是比整数的加慢的多!此外,你这是用C#写呢还是VC.NET写?如果你对速度有要求,建议你用VC.NET写这一部分的代码,然后对结果用Managed Code输出,这样C#可以利用,而又提高了运行速度。
〉 int c = lTempHeight * nMWidth;
〉
〉 for (int i = 0; i < c; i++)
〉 {
〉 dSigmaT+=(double)*pMT;
〉 pMT++;
〉 }
这是不可以的,之所以需要圈套循环,原因是因为有些地方需要跳转一个段,上头只是一个小的循环,大循环中还需要如下的算法for(m=0;m<lTempHeight;m++)
{
for(n=0;n<nMWidth;n++)
{
dSigmaT+=(double)pMT[0];
dSigmaT+=(double)pMT[1];
dSigmaT+=(double)pMT[2];
pMT += 3;
}
pMT += (nWidth + nOffset) * 3;
}其中 nWidth 和 nOffset 是其它的两个long型变量> 此外,不知道pMT[2]这样子的语句是否会比*pMT慢一点?因为也许pMT[2]的汇编是
> mov ah, [esi+ebx]
> 而*pMT的汇编是
> mov ah, [esi]这个可以试验一下,等出结果讨论由double改为long也是可以考虑一下谢谢大家
mov ecx, 0
jmp loop_cmp:
loop_entry:
; bra bra bra of the codes in the looploop_cmp:
cmp ecx, 12345678h
jl loop_entry:如果是编译成Native的,那么还有可能被编译器优化成其他形式。(我没仔细研究过,直觉告诉我不太可能会优化成其他形式。)如果你是用C#写的,或者VC.NET写的managed,那么就是编译成IL,那么我可以肯定就是这种jmp...jl的形式。(应该说是br...blt的形式。)这种方式显然不是最优化的,最优化的应该是如下这样的汇编:mov ecx, 12345678h ; 设置循环次数
lds esi, _The_thing_you_want_to_count_for ; 设置开始位置这样的好处是避免了条件转xor edx, edx ; 清除临时变量,实际上是想清除他的高位部分。
; 这里应该还有一句clX还是stX(X是什么忘了)的语句,用于设置esi的改变方向(递增还是递减)
loop_entry:
mov dl, [esi] ; 获取一个子节
add eax, edx ; 累加到eax当中,因为edx高位为零,这里进行了一个隐藏的位长转换。
loop loop_entry: ; 循环移,虽然loop也是一种条件转移,但是相对于jxx来说,CPU对这个指令的分支预测显然要准确的多。那个类加器用32位的就够了,即使对于300万像素的也够了,300W * 256 = 3M * 256 = 768M,long的空间是-2G~2G,如果还觉得不保险的话,可以用uint,那就变成0~4G空间了。4G/256 = 16777216 = 1677.7W,现有的以及可以预见将来的摄像机也不太可能达到这个级别吧?用超过CPU数据处理长度的类型比如long(64位,对应于VC.NET的__int64)会明显降低速度的。此外,就算你不用一层循环做,那么也要明白a[0]...a[1]...a[2]...a+=3这样的做法是在浪费运算能力——这个体现在a+=3上面,实际上你只要把里面那一层的循环结束条件乘以3,然后改成a+=*b++就行了,能够节省你的运算。还有,我要提醒你的是,这里我提到的优化都只能够是争分夺秒,比如从1.8s变成1.7s,甚至只是1.795s。当然,从double改成long会有一个飞跃,从long改成int应该也会有一定提高,但是诸如用汇编、a+=*b++等,效果就不明显了。如果你是用C#或者managed C++,那么我建议你改用unmanaged C++,这样肯定能提高不少。
t = 1s / 1G / 8
那么1677.7W个多出来的int操作就花费:
1677W * t = 16.77M * 1s / 1000M / 8 = 0.002s照这么算,实际上如果你的CPU真能够达到一个周期同时执行8条指令的话,那么我估计做一次这样的运算总共也不可能超过0.1秒。也就是说估计会影响2%的效率,如果你真要争分夺秒的话对这个问题做优化是值得的,而且是很简单的。那么怎么做呢?你可以在每一个图片里面用int,图片间的相加用long,或者如果你要处理的是一个已经合成了的图片,那么在里面那个循环用int,外面那个循环再做一次long累加。
nOffset = 4 - nWidth;
for..
{
for..
{
a+=*b++;
}
nStart += nOffset; // 我对于你的程序前面pMT += 3而又在这里
// pMT = (nWidth+Offset)*3 表示怀疑,这样岂不是相当于跳过了一行?
}如果是为了这样,那我建议你不用试图跳过这些填充字节,而是把这些字节也进行累加。因为填充字节都是零,因此应该是无害的,加指令可比条件跳转要快多了。
另外再多说一句,a*3 写成 (a << 1) + a,应该会快一点点。
C# 也同样可以使用指针,不必用 C++ 来实现。public unsafe static int SumIntArray(int[] src, int start, int length)
{
// 参数检查略
....
//
int n = 0;
fixed(int* pSrc = src)
{
int* ps = pSrc + start;
int* pe = pSrc + start + length
while (ps < pe)
{
n += *ps
ps++;
}
}
return n;
}
//(以上代码未经测试)C# 编译器编译以上 C# 代码至 IL 代码和 .NET CLR 编译 IL 代码至 Native 代码,这些过程中会发生什么变化,我不清楚,究竟能比托管代码快多少,我未曾测试研究过。这是一个值得探讨的话题。
但托管代码比非托管代码性能就一定差,这个结论至今我还未得到一个绝对的肯定。
以下是测试代码,可能有不妥之处,请指正。
using System;namespace Test
{
public class Class1
{
static void Main()
{
int[] intArray = new int[1000000];
for (int i = 0; i < intArray.Length; i++)
intArray[i] = i;
int u, m;
long startTicks, tick1, tick2;
for (int j = 0; j < 10; j++)
{
startTicks = DateTime.Now.Ticks;
u = Sum_Unsafe(intArray, 0, 1000000);
tick1 = DateTime.Now.Ticks - startTicks;
startTicks = DateTime.Now.Ticks;
m = Sum_Managed(intArray, 0, 1000000);
tick2 = DateTime.Now.Ticks - startTicks;
Console.WriteLine("Test " + j.ToString());
Console.WriteLine("Method Unsafe:" + u.ToString());
Console.WriteLine("Taken Ticks:" + tick1.ToString());
Console.WriteLine();
Console.WriteLine("Method Managed:" + m.ToString());
Console.WriteLine("Taken Ticks:" + tick2.ToString());
Console.WriteLine("====================================");
}
}
public unsafe static int Sum_Unsafe(int[] src, int start, int length)
{
// 参数检查略
//.... int n = 0;
fixed(int* pSrc = src)
{
int* ps = pSrc + start;
int* pe = pSrc + start + length;
while (ps < pe)
{
n += *ps;
ps++;
}
}
return n;
} public static int Sum_Managed(int[] src, int start, int length)
{
int n = 0;
for (int i = 0; i < length; i++)
{
n += src[start+i];
}
return n;
}
}
}
速度从以前的1.36s提高到了0.198788114131824s离希望的目标还是有点距离,可能只能靠其它的俩调整了在静态的图片中,也就是我不必要考虑太多模糊运算的前提下,我采用了先检查起始部分的匹配相识性,如果相识,再检测其它的部分的处理方式,(也就是最大限度地减少了频繁的累加运算)速度为0.0297881434651611s 谢谢sumtec(Psydian) 的大力帮助,问题分析得很彻底,非常有帮助
call System.Int32 function1(void)
stloc.3
ldloc.3
这样的句子,也许在翻译成NativeCode就变成了:
call function1
push ax
pop ax
而实际上后面两句话根本就是多余的。我说的效率损失就是这个含义,而不是翻译本身所带来的。也就是说,你用NGen这样的工具只能够提高程序装入速度或者第一次运行的速度,而不会提高你的实际运行效率。如果你要实验我所说的区别,那么就应该用C#写一个程序,然后用VC.NET写一个一模一样的unmanaged程序,然后再作比较。
To cellblue:
你现在的思路就对了:减少不必要的运算,或者提前做出运算,或者空间换时间,这些才是提高效率的关键,光是依靠工具还有double->int这样的方式是达不到你的目的的。如果你这里面可能会对某一个图片(或者某个图片的某一部分)进行重复分析,那么你就需要考虑可不可以将这些计算结果保存下来,避免重复的结果相同的计算了。
源数据保存在A[1000]中,则创建另一数组B[1000],B里面的B[n]是A数组中A[n]和前面所有数的和。例如:
B[0]=A[0]
B[1]=A[0]+A[1]
B[2]=A[0]+A[1]+A[2]
以此类推。当然对A进行初始化的时候,就可以一边对A赋值,一边在临时变量中进行累加,把每次累加的值就放入B数组的相应位置。
有了数组B后,要算A的从M到N的累加和,只要用B[N]-B[M-1]即可。做算法应该把思路打开,不要局限在表面的框框里,而应该跳出圈子。
源数据保存在A[1000]中,则创建另一数组B[1000],B里面的B[n]是A数组中A[n]和前面所有数的和。例如:
B[0]=A[0]
B[1]=A[0]+A[1]
B[2]=A[0]+A[1]+A[2]
以此类推。当然对A进行初始化的时候,就可以一边对A赋值,一边在临时变量中进行累加,把每次累加的值就放入B数组的相应位置。
有了数组B后,要算A的从M到N的累加和,只要用B[N]-B[M-1]即可。做算法应该把思路打开,不要局限在表面的框框里,而应该跳出圈子。
9M * 255(原来个值也是不准确的) = 2295M = 2.3G。所以估计也就能够承受500W左右的像素,600W的有点悬。呵呵,有人提出B[x]-B[y]的方法,这个是一个不错的建议,前提你需要进行超过一次的完全求和过程。当然,这也是一个空间换时间的办法。如果有300W的像素,那么就会额外增加9M的空间。
◥◣◣◣★◢◢◢◤
◣★◥◤★◢
◣★◢
◥◤
★欢迎大家对我的作品提出宝贵意见,并在此感谢项有建版主的支持。下载(项有建网站):http://www.338888.com/PumpCSDN.rar
讨论:http://expert.csdn.net/Expert/TopicView1.asp?id=2445039用我的这款浏览器,能够轻而易举的实现每天定时自动登录所有用户。方法是,把CSDN浏览器可执行文件加入到Win2000的计划列表中,并在命令行参数中加入-l就可以按照规定的时间登陆了。如果CSDN浏览器开着,则直接点击工具条上面的笑脸按钮即可。
该款浏览器有很多特性:
1、主工具条可以停靠在不同的地方;发帖面板能够停靠在不同的地方,并能够自动隐藏
2、具有帖子地址收藏功能;
3、自动登录所有用户功能;
4、通过链接托拽,创建不同CSDN论坛的快捷页面;
5、页面自动登录;
6、常用文字可收藏到仓库中,用时直接双击拷贝;
7、多文档的模式打开多个的帖子,并能后台打开;
8、多用户无缝切换发言(这对某些人来说,可能是最重要的)。注意事项,如果是老用户,可以用该版本覆盖原有版本的exe文件进行升级,但必须在原版本目录下手动删除CSDN_TB.ini和Layout.ini两个配置文件,否则工具条不会更新。
根据我的测试,单纯做一次累加运算,需要0.0310156737797681s由于会溢出,因此我测试了一下一个较小的图片
累加到ulong
0.000269028605590934s
累加到uint
0.00000159238115458808 1.59238115458808E-05s然而,这里头只能为long型,因此速度问题还是有限制
int tempsum;sum = 0;
for (int i ...)
{
tempsum = 0;
for (int j...)
{
tempsum += ...;
}
sum += tempsum;
}这样会比你直接用ulong快很多。关于.NET 托管语言的低层次优化,这里有一篇文章:
http://www.microsoft.com/china/msdn/library/dndotnet/html/fastmanagedcode.asp你可以看到long和int之间的差别。
此外,即使你无法从最原始的数据开始积累,如果你是多次需要使用其中的数据的话,你仍然可以使用B[y]-B[x]的思想:你可以判断是否已经产生过B[]数组,如果没有就转到顺便产生这个树组的那个循环块,如果已经产生了,那么就直接计算B[y]-B[x]。也许你在第一次进行累加计算的时候可能会额外负担0.01s的时间,但是以后每次运算就只需要不超过10ns的时间了。这样仍然是很划算的一件事情。
ulong[] ulMBM = new ulong[lMWidth * lMHeight];for(m=0;m<lMHeight;m++)
{
for(n=0;n<lMWidth;n++)
{
dSigmaTemp += (uint)*pMT;
pMT++;
dSigmaTemp += (uint)*pMT;
pMT++;
dSigmaTemp += (uint)*pMT;
pMT++; ulMBM[m * lMWidth + n] = dSigmaTemp; }
pMT += nOffset;
}
类似于这样的结构,好像还不好改为sumtec(Psydian) 所说的那个
ulong dSigmaTemp = 0;
ulong[] ulMBM = new ulong[lMWidth * lMHeight];
uint k;
ushort tmpk = 0;
for(m=0;m<lMHeight;m++)
{
for(n=0;n<lMWidth;n++)
{
tmp = *pMT; // 在这里(uint)强制转换是没有意义的,甚至会引起副作用
pMT++; // 因为最终还要变换到(ulong)上面,如果pMT是byte*的话
tmp += *pMT;// 不过我这里是变换到(ushort)
pMT++;
dSigmaTemp += tmp + *pMT;
pMT++;
ulMBM[k++] = dSigmaTemp; // 这里你用了表达式运算,实际上是不需要的
// 稍微分析一下就知道,你这里是顺序填充,只要不断增加索引就行了。
}
pMT += nOffset;
}
甚至实际上那个ulMBM[k++]也可以用ulong指针代替,也许会有那么一点点的速度提高。此外,请你告诉我那个pMT += nOffset的作用是什么?是用来察看一个“窗口”呢?还是用来强制双字对齐呢?
在这里,pMT += nOffset的作用是让本道程序符合于不同的图象格式,针对BitMap等,nOffset都等于0,针对其它的,有存在nOffset不为零的,也就是强制双字对齐谢谢
{
for(...)
{
}
}
else
{
for(...)
{
for(...)
{
}
p += nOffset;
}
}不过这样的优化已经有点变态了。