在以下SO问题中:https://stackoverflow.com/questions/2067955/fast-bitmap-blur-for-android-sdk @zeh声称Java模糊算法的端口到C的运行速度快40倍。
鉴于大部分代码仅包含计算,并且所有分配仅在实际算法编号处理之前“一次”完成-谁能解释为什么此代码运行速度快40倍? Dalvik JIT是否应该翻译字节码并大大减少与本机编译码速度的差距?
注:我还不确定该算法的x40性能会提高,但是我在Android上遇到的所有严肃的图像处理算法都在使用NDK-因此,这支持了NDK代码运行得更快的想法。
对于处理数据数组的算法,有两件事会显着改变Java和C之类的语言之间的性能:
数组边界检查-Java将检查每个访问bmap [i],并确认i在数组边界之内。如果代码试图越界访问,您将得到一个有用的异常。 C&C ++不会检查任何内容,只是信任您的代码。对越界访问的最佳响应是页面错误。更有可能的结果是“意外行为”。
pointers-您可以通过使用指针来显着减少操作。
[使用这个普通的滤镜示例(类似于模糊,但是是一维):
for{i=0; i<ndata-ncoef; ++i) {
z[i] = 0;
for{k=0; k<ncoef; ++k) {
z[i] += c[k] * d[i+k];
}
}
[访问数组元素时,coef [k]为:
这些数组访问中的每一个都可以得到改善,因为您知道索引是顺序的。编译器或JIT都无法知道索引是顺序的,因此无法完全优化(尽管它们一直在尝试)。
在C ++中,您应该像这样编写代码:
int d[10000];
int z[10000];
int coef[10];
int* zptr;
int* dptr;
int* cptr;
dptr = &(d[0]); // Just being overly explicit here, more likely you would dptr = d;
zptr = &(z[0]); // or zptr = z;
for{i=0; i<(ndata-ncoef); ++i) {
*zptr = 0;
*cptr = coef;
*dptr = d + i;
for{k=0; k<ncoef; ++k) {
*zptr += *cptr * *dptr;
cptr++;
dptr++;
}
zptr++;
}
[当您第一次执行这样的操作(并成功将其正确设置)时,您会惊讶地发现它的速度有多快。所有用于获取索引以及将索引与基地址相加的数组地址计算都将由增量指令替换。
[对于诸如图像模糊的2D阵列操作,无害的代码数据[r,c]涉及两个值提取,一个乘法和一个和。因此,对于2D数组,指针的好处是可以删除乘法运算。
因此该语言可以真正减少CPU必须执行的操作。代价是C ++代码难以阅读和调试。指针错误和缓冲区溢出是黑客的食粮。但是当涉及到原始数字磨削算法时,速度的提高很容易被忽略。
上面没有提到的另一个因素是垃圾收集器。问题在于,垃圾回收需要时间,而且它可以随时运行。这意味着一个创建大量临时对象的Java程序(请注意,某些类型的String操作可能对此不利)通常会触发垃圾收集器,从而使该程序(应用程序)变慢。
以下是基于级别的编程语言列表,>>
此处较低级别的语言可以直接访问硬件。只要级别增加,对硬件的访问就会减少。因此,汇编语言的代码以最快的速度运行,而其他语言的代码则根据其级别运行。
这是C语言的代码比Java代码运行快得多的原因。