我刚刚开始与NDK一起玩,以探索我所承诺的出色的性能提升。为了感觉到两者之间的区别,我尝试了一个愚蠢的数字处理任务(将Mandelbrot设置为位图),并将其与相同代码的Java版本进行了比较。令我大吃一惊的是,C版本的速度要慢得多(平均为5.0秒,而HTC One为1.6秒)。甚至更陌生,花费也不是因为进行本地呼叫的开销,而是实际的数字处理需要更长的时间。

这是不对的,可以吗?我错过了什么?

C版本(已删除调试计时器代码):

const int MAX_ITER = 63;
const float MAX_DEPTH = 16;

static uint16_t rgb565(int red, int green, int blue)
{
    return (uint16_t)(((red << 8) & 0xf800) | ((green << 2) & 0x03e0) | ((blue  >> 3) & 0x001f));
}

float zAbs(float re, float im) {
    return re*re + im*im;
}

int depth(float cRe, float cIm) {
    int i=0;
    float re, im;
    float zRe = 0.0f;
    float zIm = 0.0f;
    while ((zAbs(zRe, zIm) < MAX_DEPTH) && (i < MAX_ITER)) {
        re = zRe * zRe - zIm * zIm + cRe;
        im = 2.0f * zRe * zIm + cIm;
        zRe = re;
        zIm = im;
        i++;
    }
  return i;
}

extern "C"
void Java_com_example_ndktest_MainActivity_renderFractal(JNIEnv* env, jobject thiz, jobject bitmap, float re0, float im0, float b)
{
    AndroidBitmapInfo info;
    void* pixels;
    int ret;

    long t0 = currentTimeInMilliseconds();

    if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
        return;
    }

    if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) {
        LOGE("Bitmap format is not RGB_565 !");
        return;
    }

    if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    }


    int w = info.width;
    int h = info.height;

    float re, im;
    int z = 0;
    uint16_t*  px = (uint16_t*)pixels;
    for(int y=0; y<h; y++) {
        im = im0 + b*((float)y/(float)h);
        for(int x=0; x<info.width; x++) {
            re = re0 + b*((float)x/(float)w);
            z = depth(re, im);
            px[y*w + x] = rgb565(0, z*4, z * 16);
        }
    }

    AndroidBitmap_unlockPixels(env, bitmap);
}


Java版本:

    private static final int MAX_ITER = 63;
    private static final float MAX_DEPTH = 16;

    static int rgb565(int red, int green, int blue)
    {
        return ((red << 8) & 0xf800) | ((green << 2) & 0x03e0) | ((blue >> 3) & 0x001f);
    }

    static float zAbs(float re, float im) {
        return re*re + im*im;
    }

    static int depth(float cRe, float cIm) {
        int i=0;
        float re, im;
        float zRe = 0.0f;
        float zIm = 0.0f;
        while ((zAbs(zRe, zIm) < MAX_DEPTH) && (i < MAX_ITER)) {
            re = zRe * zRe - zIm * zIm + cRe;
            im = 2.0f * zRe * zIm + cIm;
            zRe = re;
            zIm = im;
            i++;
        }
      return i;
    }

    static void renderFractal(Bitmap bitmap, float re0, float im0, float b)
    {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        int[] pixels = new int[w * h];

        bitmap.getPixels(pixels, 0, w, 0, 0, w, h);

        float re, im;
        int z = 0;
        for(int y=0; y<h; y++) {
            im = im0 + b*((float)y/(float)h);
            for(int x=0; x<w; x++) {
                re = re0 + b*((float)x/(float)w);
                z = depth(re, im);
                pixels[y*w + x] = rgb565(0, z*4, z * 16);
            }
        }

        bitmap.setPixels(pixels, 0, w, 0, 0, w, h);
    }

最佳答案

如评论中所述,这是因为NDK代码是为armeabi目标而不是armeabi-v7a目标构建的。前者旨在用于广泛的硬件,包括不带浮点硬件的设备,因此它可以在软件中进行所有浮点计算。

为armeabi-v7a构建可启用VFP指令,因此任何严重依赖浮点计算的事物都将大大加快速度。

如果您是专门为armeabi-v7a打造的,则将排除相当广泛的设备选择,即使是相对较新的设备(例如Samsung Galaxy Ace)。这些设备具有VFP支持,但是CPU基于ARMv6指令集而不是ARMv7。没有“带VFP的ARMv7之前的CPU”构建目标,因此您必须针对armeabi进行构建,或者使用自定义构建规则并仔细选择支持的设备。

另一方面,通过在armeabi-v7a库中指定硬浮动ABI(-mhard-float –需要NDK r9b),可能会提高性能。

FWIW是即时编译器(例如Dalvik中的即时编译器)的卖点之一,就是它们可以识别系统功能并适当地调整代码生成。

关于android - NDK性能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19981969/

10-12 04:03