2

我刚刚开始使用 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);
}

爪哇版:

    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);
    }
4

1 回答 1

2

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

为 armeabi-v7a 构建可启用 VFP 指令,因此任何严重依赖浮点计算的东西都会显着加快速度。

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

-mhard-float另一方面,通过在 armeabi-v7a 库中指定硬浮点 ABI( -- 需要 NDK r9b),您可以获得小的性能提升。

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

于 2013-11-14T16:32:22.897 回答