0
votes

The following piece of Android code only produces native threads running on the same CPU core. FaceEngineWrapper.Run() is a JNI wrapper for a face detect inference engine and spawns quite a lot native threads (through both std::thread and openmp in OpenCV-dnn). But these native threads end up running on the same CPU core and the whole process runs very slow.

So how to make native threads utilize all CPU cores?

Observable.create(new ObservableOnSubscribe<Boolean>() {
    @Override
    public void subscribe(ObservableEmitter<Boolean> e) throws Exception {
        e.onNext(true);
        getFrameBitmap();
        e.onComplete();
    }
}).subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Consumer<Boolean>() {
        @Override
        public void accept(Boolean aBoolean) throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                mFaceEngineWrapper.Run(); 
            }
        }).start();
      }
});

P.S. This app is tested on an Android RK3399 board, 2/4 big/litte core. The app with jni shared lib consumes 16-17% cpu and the standalone executable binary with same code and root priviledge consumes 34-35% cpu. If I enable some extra features, the cpu usage goes to about 60% for binary and this app still runs at 17% cpu while the whole delecting process gets much slower. So I guess this app's cpu usage is somehow restricted.

P.P.S. The problem is about cpu affinity. I got CPU-set mask 000001 on this 6-core CPU. But by sched_setaffinity to 111111 nothing changed and no error was returned. I really wonder why CPU affinity is set implicitly here and what I can do to disable it.

SetCpuAffinity({0,1}) is the C++ code called in the working thread (created by std::thread in Run). sched_setaffinity returns 0. But sched_getaffinity shows 000001 afterwards. I can't use pthread to do it because Android's pthread lib does not provide affinity function.

void SetCpuAffinity(const std::vector<int32_t>& cpuids) {
  int32_t nproc = sysconf(_SC_NPROCESSORS_ONLN);
  cpu_set_t cpu_set;
  CPU_ZERO(&cpu_set);
  for (auto&& cpuid: cpuids) {
    if (cpuid >= nproc || cpuid < 0) {
      LOG_ERROR("cannot set affinity cpu id to " << cpuid << " total cpu " << nproc);
    }
    CPU_SET(cpuid, &cpu_set);
  }
  int32_t res = sched_setaffinity(gettid(), sizeof(cpu_set_t), &cpu_set);
  LOG_INFO("try to set thread cpu affinity, res "<< res << ", errno " << errno);
  TestThreadAffinity();
  TestThreadCpu();
}
1
Is there a reason you need a new Thread instead of using a thread pool? - Karol Dowbecki
What number does availableProcessors() return? It is absolutely up to Android to tell you less than you physically have, and you can't do anything about it just with Java code. - Pavel Zdenek
How do you know all those threads are running on the same core? - greeble31
@KarolDowbecki C++ code will manage its own threads. Thread in Java seems quite irrevelant. - reasonsolo
@PavelZdenek I'm using a 2/4 big/litte core arm device. The worker threads are created by c++ std::thread. Java code basically acts as a main in c++. - reasonsolo

1 Answers

0
votes

Finally I figured it out. For Android background service, its affinity is restricted to one CPU core. You can't change it through sched_setaffinity or event tasket -p 3f PID root command, because the PID is in the /dev/cpuset/background/tasks list.

After echo PID > /dev/cpuset/foreground/tasks and echo PID > /dev/cpuset/top-app/tasks, the PID's affinity can be set by taskset -p mask PID.

But the running speed of my service is still quite slow, maybe its thread affinity is not changed instantly(?). And I'm not sure about the default affinity behaviour can be managed through code (either java or native but without root). Really appreciate it if someone would provide a more detailed answer.