【问题标题】:Using OpenCL with Android JNI produces slow code due to some overhead由于一些开销,将 OpenCL 与 Android JNI 一起使用会产生缓慢的代码
【发布时间】:2019-08-18 10:25:55
【问题描述】:

我使用 OpenCL 和 OpenMP 在 android 上实现了一个算法。 OpenMP 实现的运行速度比 OpenCL 慢约 10 倍。

  • OpenMP:~250 毫秒
  • OpenCL:~25 毫秒

但总的来说,如果我从 java android 端测量时间,我会得到大致相同的时间来调用并获取我的值。

例如:

  • Java 代码:

    // calls C implementation using JNI (Java Native Interface)
    bool useOpenCL = true;
    myFunction(bitmap, useOpenCL); // ~300 ms, timed with System.nanoTime() here, but omitted code for clarity
    myFunction(bitmap, !useOpenCL); // ~300 ms, timed with System.nanoTime() here, but omitted code for clarity    
    
  • C 代码:

    JNIEXPORT void JNICALL Java_com_xxxxx_myFunctionNative(JNIEnv * env, jobject obj, jobject pBitmap, jboolean useOpenCL)
    {
    // same before, setting some variables
    
    clock_t startTimer, stopTimer;
    startTimer = clock();
    if ((bool) useOpenCL) {
       calculateUsingOpenCL(); // runs in ~25 ms, timed here, using clock()
    }
    else {
       calculateUsingOpenMP(); // runs in ~250 ms
    }
    stopTimer = clock();
    __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Time in ms: %f\n", 1000.0f* (float)(stopTimer - startTimer) / (float)CLOCKS_PER_SEC);
    
    // same from here on, e.g.: copying values to java side
    }
    

Java 代码在这两种情况下的执行时间大致相同,大约为 300 毫秒。更准确地说,elapsedTime 对 OpenCL 来说要多一些,也就是说 OpenCL 平均速度较慢。

查看 OpenMP 和 OpenCL 实现的各个运行时间,OpenCL 版本总体上应该快得多。但由于某种原因,我无法找到开销。

我还比较了 OpenCL 与普通本机代码(无 OpenMP),我仍然得到相同的结果,总体运行时间大致相同,尽管 calculateUsingOpenCL 的运行速度至少快了 10 倍。


想法:

  • 也许 GPU(在 OpenCL 情况下)通常效率较低,因为它的可用内存较少。我们需要预先分配的变量很少,每帧都会使用这些变量。因此,我们检查了 android 在两种情况下(OpenMP、OpenCL)绘制位图所需的时间。在 OpenCL 的情况下,有时绘制位图需要更长的时间(3 倍),但不会达到使程序的整体运行时间相等的量。

  • JNI 是否使用 GPU 来加速某些调用,这可能会导致 OpenCL 版本变慢?

编辑:

  • 会不会是 OpenCL 触发了 Java 垃圾回收,导致开销很大?

【问题讨论】:

  • 您在哪个设备/操作系统上进行了测试。另外您使用的是哪个版本的 OpenMP 库(32 位/64 位)?
  • 我在 Open-Q 820 μSOM 上进行了测试。 OpenMP 在这里并不重要,因为问题在于 OpenCL 很慢。
  • "使用 OpenCL,我们需要在其上分配一些变量,这些变量在每一帧中都使用" - 我不确定您所说的“变量”是什么意思,但如果您正在分配和发布新的每一帧都有缓冲区,你几乎肯定做错了。我的方法是将缓冲区作为静态变量(“cl_mem”类型),并添加两个方法“init”和“teardown”(不言自明)。然后只需在每帧渲染中通过静态变量使用这些缓冲区。这同样适用于 OpenCL 命令队列——不要每帧都创建和销毁它们;它可能很慢。您希望尽可能多地重用 CL 对象。
  • 不,我们显然不会每帧都创建变量。

标签: android performance java-native-interface opencl


【解决方案1】:

事实证明,clock() 是不可靠的(在 Android 上),因此我们使用以下方法来测量时间。用这种方法,一切正常。

int64_t getTimeNsec() {
    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    return (int64_t) now.tv_sec*1000000000LL + now.tv_nsec;
}

clock_t startTimer, stopTimer;
startTimer = getTimeNsec();
    function_to_measure();
stopTimer = getTimeNsec();
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Runtime in milliseconds (ms): %f", (float)(stopTimer - startTimer) / 1000000.0f);

这是在这里建议的: How to obtain computation time in NDK

【讨论】:

    猜你喜欢
    • 2011-09-19
    • 2012-04-08
    • 2010-11-21
    • 2014-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-19
    • 2013-01-31
    相关资源
    最近更新 更多