首页 > 解决方案 > Android - 从 Java 线程调用的与 OpenMP(带缩减)并行的 C++ for 循环会导致段错误

问题描述

考虑以下 OpenMP 并行化 for 循环:

void testopenmp()
{    
    int     an_int = 0;
    float   a_float = 0.0f;    
    const int num_iter = 1000;    
    #pragma omp parallel for schedule(dynamic) reduction(+ : an_int, a_float)
    for (int i = 0; i < num_iter; i++)
    {
        an_int++;
        a_float += (float)3.1415f;
    }
    LOG_INFO("an_int %d   a_float %f", an_int, a_float %d);
}

如您所见,此例程没有做任何有用的事情。这只是在并行for循环中对 OpenMP 缩减的测试。考虑以下活动:

package com.testopenmp;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    Thread  worker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        worker = new Thread(new Runnable() {
            @Override
            public void run() {

                while (true)
                {
                    try {

                        testopenmp();
                        Thread.sleep(100);

                    } catch (InterruptedException e)
                    {
                        e.printStackTrace();
                        return ;
                    }
                }
            }
        });
        worker.start();

    }



    @Override
    public void onDestroy()
    {
        worker.interrupt();
        super.onDestroy();
    }


    public static native void testopenmp();

    static {
        System.loadLibrary("native");
    }
}

在模拟器(Api 29、x86 或 x86_64 abi)上执行以下步骤时会发生崩溃:

  1. 运行应用程序
  2. 按下后退按钮
  3. 再次运行应用程序

第 3 步(通常)会产生分段错误。有时我需要多次关闭和打开以使其崩溃。在崩溃的那一刻,调用堆栈如下:

art_sigsegv_fault 0x00007817c0f96a40
art::FaultManager::HandleFault(int, siginfo*, void*) 0x00007817c0f96f15
___lldb_unnamed_symbol25$$app_process64 0x00005ebbee282ee8
___lldb_unnamed_symbol1$$libc.so 0x0000781842514240
__kmp_acquire_ticket_lock 0x000078176a6b3435
__kmp_enter_critical_section_reduce_block(ident*, int, int (*) [8]) 0x000078176a66feb1
__kmpc_reduce_nowait 0x000078176a66fbde
::.omp_outlined._debug__(int &, const int &, float &) native.cpp:17
::.omp_outlined.() native.cpp:18
__kmp_invoke_microtask 0x000078176a6bed93
__kmp_invoke_task_func 0x000078176a67bd1f
__kmp_launch_thread 0x000078176a67b08b
__kmp_launch_worker(void*) 0x000078176a6b9e15
__pthread_start(void*) 0x0000781842580fcf
__start_thread 0x0000781842518fe8

如果不使用 OpenMP pragma,应用程序不会崩溃。是 Android Studio 项目的链接,因此您可以随时试用。我想了解为什么testopenmp()当应用程序关闭并再次运行时该函数会导致分段错误。

标签: androidmultithreadingandroid-ndkopenmp

解决方案


使用我的 google pixel 2xl 在不同的场景中尝试了您的应用程序。如果我设置const int num_iter = 100000000;并最小化多次重新打开应用程序(大约 4 次),我会2019-12-11 12:22:29.891 8213-8636/com.testopenmp A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x158 in tid 8636 (Thread-13), pid 8213 (com.testopenmp) 在并行部分之前和之后发生崩溃记录显示多个线程同时执行并行部分。

获得相同崩溃的另一种方法是隔离 Runnable r 并更改 mainActivity 代码:

for (int i = 0; i<9; i++) {
    worker = new Thread(r);
    worker.start();
}

使用 9 个或更多线程时,我会遇到与以前相同类型的崩溃,并且在堆栈跟踪上使用空指针访问。2019-12-11 12:22:30.571 8654-8654/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x158 2019-12-11 12:22:30.571 8654-8654/? A/DEBUG: Cause: null pointer dereference

我不知道这是否是 OpenMP 实现的错误,或者对可以同时执行的并行 OpenMP 部分的数量有硬性限制,另一方面,OpenMP 旨在利用并行性,所以你可能只想要在给定时间执行一个 OpenMP 部分,以便 OpenMP 尽可能地利用 CPU 线程。添加同时执行的另一个 OpenMP 部分只会使性能变差。


推荐阅读