JNI实现在windows下性能比JAVA好很多,但在linux下性能比JAVA实现差很多

实现的JNI接口,同一套代码,为什么在windows下性能比JAVA实现要好很多。但在linux下实现性能却比JAVA实现差很多。以下是具体数据:
在Windows 11 下(4核8线程 Intel(R) Core(TM)i7-10510U CPU @ 1.80GHz)
项目是navmesh 3D寻路,

📄 性能测试结果如下:

在Windows 11 下(64位,4核8线程 Intel(R) Core(TM)i7-10510U CPU @ 1.80GHz)

Benchmark                              Mode  Cnt        Score       Error  Units
NavEngineBenchMark.javaFind           thrpt   10    28605.626 ±  1331.775  ops/s
NavEngineBenchMark.javaFindNearest    thrpt   10   497177.122 ± 12048.418  ops/s
NavEngineBenchMark.javaRaycast        thrpt   10   401200.127 ±  7926.908  ops/s
NavEngineBenchMark.nativeFind         thrpt   10    82924.746 ±  1942.112  ops/s
NavEngineBenchMark.nativeFindNearest  thrpt   10  1438972.883 ± 28769.610  ops/s
NavEngineBenchMark.nativeRaycast      thrpt   10  1096445.690 ± 29597.755  ops/s
  • 寻路API,性能达到了原先的289.89%;
  • 寻找最近可通点API,性能达到了原先的289.43%;
  • 光线照反射API,性能达到了原先的273.29%;

在CentOS Linux 7 (64位,8核16线程 Intel(R) Xeon(R) Platinum 8372C CPU model 106 @ 3.20GHz)

Benchmark                              Mode  Cnt       Score     Error  Units
NavEngineBenchMark.javaFind           thrpt   10   38372.243 ±  80.293  ops/s
NavEngineBenchMark.javaFindNearest    thrpt   10  518859.216 ± 767.435  ops/s
NavEngineBenchMark.javaRaycast        thrpt   10  442360.257 ± 287.334  ops/s
NavEngineBenchMark.nativeFind         thrpt   10   26796.756 ±  49.669  ops/s
NavEngineBenchMark.nativeFindNearest  thrpt   10  393484.307 ± 844.553  ops/s
NavEngineBenchMark.nativeRaycast      thrpt   10  305434.422 ± 851.248  ops/s
  • 寻路API,性能降到了原先的69.83%;
  • 寻找最近可通点API,性能降到了原先的 75.84%;
  • 光线照反射API,性能降到了原先的69.05%;

jmh 性能对比项目地址为:https://github.com/jiangguilong2000/gamioo/
jni 项目为: 为https://github.com/jiangguilong2000/recastnavigation

主要代码维护在RecastDll里的NavEngine.cpp,对应到JAVA的是io.gamioo.nav.NavEngine.
项目是navmesh 3D寻路,在IDEA下,运行NavEngineBenchMark直接跑起来,或者在windows下和linux下到gamioo目录,执行gradle gamioo-navigation:jmh,能直接跑出如上结果
由于有部分小伙伴提到需要给出源码,才能给出建议,现给出如下:
主要5个文件,
io_gamioo_nav_NavEngine.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class io_gamioo_nav_NavEngine */

#ifndef _Included_io_gamioo_nav_NavEngine
#define _Included_io_gamioo_nav_NavEngine
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     io_gamioo_nav_NavEngine
 * Method:    load
 * Signature: (I[BI)I
 */
JNIEXPORT jint JNICALL Java_io_gamioo_nav_NavEngine_load
  (JNIEnv *, jobject, jint, jbyteArray, jint);

/*
 * Class:     io_gamioo_nav_NavEngine
 * Method:    find
 * Signature: (IFFFFFF)[F
 */
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_find
  (JNIEnv *, jobject, jint, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat);

/*
 * Class:     io_gamioo_nav_NavEngine
 * Method:    findNearest
 * Signature: (IFFF)[F
 */
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_findNearest
  (JNIEnv *, jobject, jint, jfloat, jfloat, jfloat);

/*
 * Class:     io_gamioo_nav_NavEngine
 * Method:    raycast
 * Signature: (IFFFFFF)[F
 */
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_raycast
  (JNIEnv *, jobject, jint, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat);

/*
 * Class:     io_gamioo_nav_NavEngine
 * Method:    release
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_release
  (JNIEnv *, jobject, jint);

/*
 * Class:     io_gamioo_nav_NavEngine
 * Method:    releaseAll
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_releaseAll
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

NavEngine.h

#include <cstring>
#include <unordered_map>
#include <iostream>
#include <exception>
#include <string>
#include <cstdint>
#include <map>

#include <sstream>

class NavMeshContex;


class NavMesh
{
public:
    static NavMesh* instance;

    static NavMesh* GetInstace();

    std::map<int32_t, NavMeshContex*> navMeshContexs;

    NavMeshContex* New(int32_t id, const char* buffer, int32_t n);

    NavMeshContex* Get(int32_t id);

    void Clear();

    const char* Version();

    void Remove(int32_t id);

private:
    NavMesh();
};


struct NavMeshSetHeader
{
    int magic;
    int version;
    int numTiles;
    dtNavMeshParams params;
};

struct NavMeshTileHeader
{
    dtTileRef tileRef;
    int dataSize;
};

Util.h

#pragma once
namespace  Util
{


    /**JByteaArray -> char* */
     static char* ConvertJByteaArrayToChars(JNIEnv* env, jbyteArray bytearray){
        char* chars = NULL;
        jbyte* bytes;
        bytes = env->GetByteArrayElements(bytearray, 0);
        size_t chars_len = env->GetArrayLength(bytearray);
        chars = new char[chars_len + 1];
        memset(chars, 0, chars_len + 1);
        memcpy(chars, bytes, chars_len);
        chars[chars_len] = 0;

        env->ReleaseByteArrayElements(bytearray, bytes, 0);

        return chars;
    }
    


    /** float* -> jfloatArray */
     static  jfloatArray ConvertFloatStarToJfloatArray(JNIEnv* env, float* array, int length) {
        jfloatArray ret = env->NewFloatArray(length);
        env->SetFloatArrayRegion(ret, 0, length, array);
        return ret;
    }
};

核心代码: NavEngine.cpp

#include "DetourNavMesh.h"
#include "jni.h"
#include "DetourNavMeshQuery.h"
#include <cstring>
#include <unordered_map>
#include "DetourCommon.h"
#include <iostream>
#include <exception>
#include <string>
#include <cstdint>
#include <map>
#include "io_gamioo_nav_NavEngine.h"
#include "Util.h"
#include "NavEngine.h"
#include <sstream>
using namespace std;
static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
static const int NAVMESHSET_VERSION = 1;
static const float extents[3] = {1.0f,1.0f,1.0f};
static const float raycastExtents[3] = { 0.0f,1.0f,0.0f };


int32_t InitNav(const char* buffer, int32_t n, dtNavMesh*& navMesh)
{
    int index = 0;
    // Read header.
    NavMeshSetHeader header;

    int count = sizeof(NavMeshSetHeader);
    if (index + count > n)
    {
        return -1;
    }
    memcpy(&header, buffer + index, count);
    index += count;

    if (header.magic != NAVMESHSET_MAGIC)
    {
        return -2;
    }
    if (header.version != NAVMESHSET_VERSION)
    {
        return -3;
    }

    dtNavMesh* mesh = dtAllocNavMesh();
    if (!mesh)
    {
        return -4;
    }
    dtStatus status = mesh->init(&header.params);
    if (dtStatusFailed(status))
    {
        return -5;
    }

    // Read tiles.
    for (int i = 0; i < header.numTiles; ++i)
    {
        NavMeshTileHeader tileHeader;

        count = sizeof(NavMeshTileHeader);
        if (index + count > n)
        {
            return -6;
        }
        memcpy(&tileHeader, buffer + index, count);
        index += count;

        if (!tileHeader.tileRef || !tileHeader.dataSize)
            break;

        unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
        if (!data) break;
        memset(data, 0, tileHeader.dataSize);

        count = tileHeader.dataSize;
        if (index + count > n)
        {
            return -7;
        }
        memcpy(data, buffer + index, count);
        index += count;

        mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
    }
    navMesh = mesh;
    return 0;
}

static const int MAX_POLYS = 256;
static const int MAX_SMOOTH = 2048;

class NavMeshContex
{
public:
    dtNavMesh* navMesh;
    dtNavMeshQuery* navQuery;


    int32_t Init(const char* buffer, int32_t n)
    {
        int32_t ret = InitNav(buffer, n, navMesh);
        std::string s;

        if (ret != 0)
        {
            return -1;
        }

        navQuery = new dtNavMeshQuery();
        navQuery->init(navMesh, 2048);
        return 0;
    }

    ~NavMeshContex()
    {
        if (navQuery != nullptr)
        {
            dtFreeNavMeshQuery(navQuery);
        }
        if (navMesh != nullptr)
        {
            dtFreeNavMesh(navMesh);
        }
    }

    std::string toString() {
        
        std::stringstream ss;
        ss << "NavMeshContex[" << static_cast<const void*>(this) << "]";
        return ss.str();
    }

};

NavMesh* NavMesh::instance = nullptr;

NavMesh::NavMesh()
{
}

NavMesh* NavMesh::GetInstace()
{
    if (NavMesh::instance == nullptr)
    {
        NavMesh::instance = new NavMesh();
    }
    return NavMesh::instance;
}

NavMeshContex* NavMesh::New(int32_t id, const char* buffer, int32_t n)
{
    NavMeshContex* navMeshContex = new NavMeshContex();
    int32_t ret = navMeshContex->Init(buffer, n);

    if (ret != 0)
    {
        delete navMeshContex;
        return nullptr;
    }
    navMeshContexs[id] = navMeshContex;
    return navMeshContex;
}

NavMeshContex* NavMesh::Get(int32_t id)
{
    const auto it = navMeshContexs.find(id);

    if (it != navMeshContexs.end())
    {
        return it->second;
    }
    return nullptr;
}

void NavMesh::Clear()
{
    for (auto kv : navMeshContexs)
    {
        delete kv.second;
    }
    navMeshContexs.clear();
}

void NavMesh::Remove(int32_t id)
{
    const auto it = navMeshContexs.find(id);
    if (it != navMeshContexs.end())
    {
        NavMeshContex* navMesh = it->second;
        navMeshContexs.erase(it);
        delete navMesh;
    }
}

const char* NavMesh::Version()
{
    return  ""+ NAVMESHSET_VERSION;
}


/**
 * 加载地图
 *
 * @param id 寻路数据地图ID
 * @param content   地图文件的路径例
 * @param length    数据长度
 * @return navmeshId, 为0或负数表示加载失败,为正数表示加载成功,后续寻路时传入此id为参数
 */
JNIEXPORT jint JNICALL Java_io_gamioo_nav_NavEngine_load(JNIEnv* env, jobject jobj, jint id, jbyteArray content, jint length)
{
    char* buffer = Util::ConvertJByteaArrayToChars(env, content);
    NavMeshContex*  context=NavMesh::GetInstace()->New(id, buffer, length);
    delete buffer;
    if (context== nullptr) {
        return -1;
    }
    else {
        return id;
    }
}

NavMeshContex* RecastGet(int32_t id)
{
    return NavMesh::GetInstace()->Get(id);
}


/**
 * 寻路
 *
 * @param navmeshId 寻路数据地图ID
 * @param startX    起始点X
 * @param startY    起始点Y
 * @param endX      结束点X
 * @param endY      结束点Y
 * @return 返回路径点列表,注意,查找不到时,会返回空
 */

JNIEXPORT  jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_find(JNIEnv* env, jobject jobj, jint id, jfloat startX, jfloat startY, jfloat startZ, jfloat endX, jfloat endY, jfloat endZ)
{
    jfloatArray ret= NULL;
    float straightPath[MAX_POLYS * 3];
    float startPos[3] = {startX ,startY,startZ };
    float endPos[3] = {endX ,endY,endZ };

    dtPolyRef startRef = 0;
    dtPolyRef endRef = 0;
    float startNearestPt[3];
    float endNearestPt[3];

    dtQueryFilter filter;
    filter.setIncludeFlags(0xffff);
    filter.setExcludeFlags(0);
    NavMeshContex* navMeshContex =RecastGet(id);
    if (navMeshContex==nullptr)
    {
        return ret;
    }
    //cout << navMeshContex->toString()<<endl;
    int dtStatus =navMeshContex->navQuery->findNearestPoly(startPos, extents, &filter, &startRef, startNearestPt);
    if ((dtStatus & DT_SUCCESS) == 0)
    {
        return ret;
    }

    dtStatus =navMeshContex->navQuery->findNearestPoly(endPos, extents, &filter, &endRef, endNearestPt);
    if ((dtStatus & DT_SUCCESS) == 0)
    {
        return ret;
    }
    dtPolyRef polys[MAX_POLYS];
    int npolys;
    unsigned char straightPathFlags[MAX_POLYS];
    dtPolyRef straightPathPolys[MAX_POLYS];
    int nstraightPath = 0;

    dtStatus=navMeshContex->navQuery->findPath(startRef, endRef, startNearestPt, endNearestPt, &filter, polys, &npolys, MAX_POLYS);
    if ((dtStatus & DT_SUCCESS) == 0)
    {
        return ret;
    }
    //printf("npolys: %d\n", npolys);
    if (npolys)
    {
        float epos1[3];
        dtVcopy(epos1, endNearestPt);

        if (polys[npolys - 1] != endRef)
        {
            dtStatus=navMeshContex->navQuery->closestPointOnPoly(polys[npolys - 1], endNearestPt, epos1, 0);
            if ((dtStatus & DT_SUCCESS) == 0)
            {
                return ret;
            }
        }
        dtStatus = navMeshContex->navQuery-> findStraightPath(startNearestPt, endNearestPt, polys, npolys, straightPath, straightPathFlags, straightPathPolys, &nstraightPath, MAX_POLYS, 0);
        if ((dtStatus & DT_SUCCESS) == 0)
        {
            return ret;
        }
        ret= Util::ConvertFloatStarToJfloatArray(env, straightPath,nstraightPath * 3);
    }

    return ret;
}


/**
 * 光线照射发,寻找可以支线通过的hit点,如果可通过则返回hit
 *
 * @param navmeshId 寻路数据地图ID
 * @param startX    起始点X
 * @param startY    起始点Y
 * @param endX      结束点X
 * @param endY      结束点Y
 * @return 返回射线射过去遇到的第一个阻挡点,如果到终点都没有阻挡,返回终点
 */
JNIEXPORT  jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_raycast(JNIEnv* env, jobject jobj, jint id, jfloat startX, jfloat startY, jfloat startZ, jfloat endX, jfloat endY, jfloat endZ)
{
    jfloatArray ret = NULL;
    float hitPoint[3];
    float startPos[3] = { startX ,startY,startZ };
    float endPos[3] = { endX ,endY,endZ };
    dtPolyRef startRef = 0;
    dtPolyRef endRef = 0;
    float startNearestPt[3];
    //float endNearestPt[3];

    dtQueryFilter filter;
    filter.setIncludeFlags(0xffff);
    filter.setExcludeFlags(0);
    NavMeshContex* navMeshContex = RecastGet(id);
    if (navMeshContex == nullptr)
    {
        return ret;
    }
    //cout << navMeshContex->toString() << endl;
    int dtStatus = navMeshContex->navQuery->findNearestPoly(startPos, raycastExtents, &filter, &startRef, startNearestPt);
    if ((dtStatus & DT_SUCCESS) == 0)
    {
        return ret;
    }
    // dtStatus = navMeshContex->navQuery->findNearestPoly(endPos, raycastExtents, &filter, &endRef, endNearestPt);
    // if ((dtStatus & DT_SUCCESS) == 0)
    // {
    //     return ret;
    // }
    dtPolyRef polys[MAX_POLYS];
    int npolys;
    int nstraightPath = 0;
    float t = 0;
    float hitNormal[3];
    memset(hitNormal, 0, sizeof(hitNormal));
    //printf("version: %d\n", 3);
     dtStatus = navMeshContex->navQuery->raycast(startRef, startNearestPt, endPos, &filter, &t, hitNormal, polys, &npolys, MAX_POLYS);
     if ((dtStatus & DT_SUCCESS) == 0)
     {
         return ret;
     }
    // printf("endPos: %f\n", t);
    if (t > 1) {
        // no hit;
        ret = Util::ConvertFloatStarToJfloatArray(env, endPos, 3);
        return ret;
    }
    else {
        
        // hit
        hitPoint[0] = startPos[0] + (endPos[0] - startPos[0]) * t;
        hitPoint[1] = startPos[1] + (endPos[1] - startPos[1]) * t;
        hitPoint[2] = startPos[2] + (endPos[2] - startPos[2]) * t;
    //    printf("hitPoint: %f,%f,%f\n", hitPoint[0], hitPoint[1], hitPoint[2]);
    //    printf("npolys: %d\n", npolys);
        ret = Util::ConvertFloatStarToJfloatArray(env, hitPoint, 3);
        //if (npolys) {
        //    float h = 0;
        //    dtStatus = navMeshContex->navQuery->getPolyHeight(polys[npolys - 1], hitPoint, &h);
        //    if ((dtStatus & DT_SUCCESS) == 0)
        //    {
        //        return ret;
        //    }
        //    hitPoint[1] = h;
        //    ret = Util::ConvertFloatStarToJfloatArray(env, hitPoint, 3);
        //}
    }
    return ret;
}

/**
 * 找到目标点最近的静态可达点
 *
 * @param navmeshId 寻路数据地图ID
 * @param pointX    参考点X
 * @param pointY    参考点Y
 * @return 如果目标点可达,返回目标点即可, 如果搜索范围内没有,返回空
 */
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_findNearest(JNIEnv* env, jobject jobj, jint id, jfloat pointX, jfloat pointY,jfloat pointZ)
{
    jfloatArray ret = NULL;
    float nearestPos[3]={0.0f};
    float startPos[3] = { pointX ,pointY,pointZ };
    dtPolyRef startRef = 0;
    dtQueryFilter filter;
    filter.setIncludeFlags(0xffff);
    filter.setExcludeFlags(0);
    NavMeshContex* navMeshContex = RecastGet(id);
    if (navMeshContex == nullptr)
    {
        return ret;
    }
    int dtStatus = navMeshContex->navQuery->findNearestPoly(startPos, extents, &filter, &startRef, nearestPos);
    if ((dtStatus & DT_SUCCESS) == 0)
    {
        return ret;
    }
    if (nearestPos[0]*10000==0&& nearestPos[1] * 10000 == 0 && nearestPos[2] * 10000 == 0) {
    //    printf("nearestPos: %f %f %f\n", nearestPos[0], nearestPos[1], nearestPos[2]);

        return ret;
    }
    
    ret = Util::ConvertFloatStarToJfloatArray(env, nearestPos, 3);
    return ret;
}


/**
 * 释放加载的地图数据
 *
 * @param navmeshId 寻路数据地图ID
 */
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_release(JNIEnv* env, jobject jobj, jint id)
{
    NavMesh::GetInstace()->Remove(id);
}

/**
 * 释放加载的所有地图数据
 */
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_releaseAll(JNIEnv* env, jobject jobj)
{
    NavMesh::GetInstace()->Clear();
}

编译脚本 CMakeLists.txt

cmake_minimum_required(VERSION 3.22)
find_package(JNI REQUIRED)
# Use C++11
set(CMAKE_CXX_STANDARD 11)
# Require (at least) it
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Don't use e.g. GNU extension (like -std=gnu++11) for portability
set(CMAKE_CXX_EXTENSIONS OFF)
include_directories(${JNI_INCLUDE_DIRS})

if ( WIN32 AND NOT CYGWIN AND NOT ( CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" ) )
    set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT" CACHE STRING "")
    set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd" CACHE STRING "")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT" CACHE STRING "")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd" CACHE STRING "")
endif ()


project(RecastDll)

find_path(RecastDll_PROJECT_DIR NAMES SConstruct
    PATHS
    ${CMAKE_SOURCE_DIR}
    NO_DEFAULT_PATH
)

MARK_AS_ADVANCED(RecastDll_PROJECT_DIR)

# 配置cpp文件
file(GLOB RECASTDLL_SOURCES 
    Source/*.cpp
    ../Detour/Source/*.cpp 
    ../DetourCrowd/Source/*.cpp 
    ../DetourTileCache/Source/*.cpp 
    ../Recast/Source/*.cpp
)

# 配置头文件
include_directories(
    Include
    ../DebugUtils/Include
    ../Detour/Include
    ../DetourCrowd/Include
    ../DetourTileCache/Include
    ../Recast/Include
)

macro(source_group_by_dir proj_dir source_files)
    if(MSVC)
        get_filename_component(sgbd_cur_dir ${proj_dir} ABSOLUTE)
        foreach(sgbd_file ${${source_files}})
            get_filename_component(sgbd_abs_file ${sgbd_file} ABSOLUTE)
            file(RELATIVE_PATH sgbd_fpath ${sgbd_cur_dir} ${sgbd_abs_file})
            string(REGEX REPLACE "\(.*\)/.*" \\1 sgbd_group_name ${sgbd_fpath})
            string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup)
            string(REPLACE "/" "\\" sgbd_group_name ${sgbd_group_name})
            if(sgbd_nogroup)
                set(sgbd_group_name "\\")
            endif(sgbd_nogroup)
            source_group(${sgbd_group_name} FILES ${sgbd_file})
        endforeach(sgbd_file)
    endif(MSVC)
endmacro(source_group_by_dir)

source_group_by_dir(${CMAKE_CURRENT_SOURCE_DIR} RECASTDLL_SOURCES)

add_library(RecastDll SHARED ${RECASTDLL_SOURCES})

if ( WIN32 AND NOT CYGWIN )
    target_compile_definitions (RecastDll PRIVATE DLL_EXPORTS)
endif ( )

该回答引用ChatGPT

如有疑问,可以回复我!

这可能有多种原因,以下是一些可能性:

1、平台差异:Windows 和 Linux 有不同的系统架构和库,这可能会影响 JNI 代码的性能。可能 Windows 版本的代码在该平台上进行了更好的优化,而 Linux 版本则没有。

2、编译器差异:JNI 代码可能在 Windows 和 Linux 上使用不同的编译器编译,这可能会导致不同的优化水平和性能。

3、代码实现差异:在 Windows 和 Linux 上运行 JNI 代码的方式可能不同,这可能会影响代码的性能。可能 Windows 和 Linux 上的代码实现方式存在一些差异,从而导致它们在不同平台上的性能不同。

4、硬件差异:您在 Windows 和 Linux 上运行代码的计算机可能有不同的硬件配置,这可能会影响代码的性能。可能在 Windows 上运行代码的计算机更适合运行该代码,而在 Linux 上运行的计算机则不太适合。

参考GPT和自己的思路:根据您提供的信息,可以推测以下原因导致JNI在Linux下性能较差:

1 平台差异:JNI在不同操作系统上的性能表现可能不同,因为不同操作系统的底层架构和实现方式不同。您可以考虑对不同操作系统分别进行优化。

2 编译器优化:您可以尝试使用不同的编译器进行编译,或者调整编译器的优化级别,以获取更好的性能。

3 环境配置:Linux和Windows系统的环境配置可能不同,您需要确保在Linux系统上也正确地设置了所有必要的参数,如内存大小、线程池大小等等。

4 调用方式:您的JNI接口是如何被调用的?如果JNI接口在Linux下被频繁调用,那么可能会导致性能问题。您可以尝试将JNI接口调用频率降低,或者使用异步方式调用JNI接口。

5 其他因素:还有其他因素可能会导致性能差异,如操作系统版本、硬件配置、代码质量等等。您可以进一步排查这些因素,找出导致性能问题的根本原因。
以下是一个简单的 JNI 示例以及流程:

1 编写 C/C++ 代码实现功能,并编译成动态链接库(.dll 或 .so 文件)。

#include <jni.h>

JNIEXPORT jstring JNICALL Java_MyClass_hello(JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "Hello, World!");
}


2 在 Java 中声明 native 方法,并加载动态链接库。

public class MyClass {
    public native String hello();

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


3 生成 JNI 头文件。
在 C/C++ 中,可以使用 javah 工具自动生成 JNI 头文件。在命令行中进入包含 Java 类的目录,然后输入以下命令:

javah -jni MyClass


将生成名为 MyClass.h 的头文件,内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MyClass */

#ifndef _Included_MyClass
#define _Included_MyClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     MyClass
 * Method:    hello
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_MyClass_hello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

4 实现 JNI 方法。
在 C/C++ 中,可以使用 jclass 和 jmethodID 类型的参数来访问 Java 类和方法。JNIEnv 结构体提供了许多有用的函数来访问 Java 对象、数组等。

#include <jni.h>

JNIEXPORT jstring JNICALL Java_MyClass_hello(JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "Hello, World!");
}

5 编译动态链接库。
使用相应的编译器和链接器编译动态链接库。在 Windows 上,使用 MSVC 或 MinGW,将输出一个 .dll 文件。在 Linux 上,使用 GCC 或 Clang,将输出一个 .so 文件。

6 运行程序。
将生成的动态链接库文件复制到 Java 程序所在的目录下,然后运行程序即可。

public static void main(String[] args) {
    MyClass myObj = new MyClass();
    System.out.println(myObj.hello());  // 输出 "Hello, World!"
}

这是一个简单的 JNI 示例,实现了一个简单的 hello 方法,返回一个字符串。实际上,JNI 可以访问 Java 对象、数组等,并在 C/C++ 中创建、释放它们。在实际应用中,需要谨慎处理内存管理等问题。

这可能是由于不同操作系统的JNI实现方式不同导致的。在Windows下,JNI使用的是Microsoft Visual C++编译器,而在Linux下,JNI使用的是GNU C++编译器。这两种编译器的实现方式不同,可能会导致性能上的差异。此外,不同操作系统的内存管理方式也不同,也可能会影响JNI性能。建议在Linux下进行性能调优。
我猜得,不知道对不对!,我也是新手

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
这个问题可能会涉及到很多因素,比如编译器,操作系统,线程模型,内存管理等等。下面简单列举一些可能影响 JNI 在不同操作系统下性能的因素:

  1. 编译器和代码优化

不同操作系统下,可能会使用不同的编译器,并且这些编译器可能采用不同的优化策略。在不同的编译器下,同样的代码可能会被优化成不同的汇编代码,从而影响JNI性能。

  1. 内存模型和垃圾回收

JNI 和 Java 代码之间需要频繁地传递数据,这个数据传递可能涉及到内存管理和垃圾回收。不同操作系统下内存模型可能不同,这也可能影响 JNI 的性能。此外,Linux 在处理垃圾回收方面相对于 Windows 会更具优势,因为 Linux 内核提供了更高级别的虚拟内存管理和页表机制。

  1. 线程模型

JNI 在 Windows 系统下,线程模型为 Win32 线程模型,而在 Linux 下是 POSIX 线程模型。这两种线程模型有不同的实现方式,可能导致JNI在不同系统下性能表现不一致。

  1. CPU架构和指令集

JNI代码在 CPU 上运行,每个 CPU 架构和指令集都会对 JNI 执行效率产生不同的影响。比如,SSE2 指令集可以使得浮点运算在 x86 架构下更加高效。

总之,对于 JNI 在不同操作系统下性能表现不一致的具体原因,需要进一步了解你的代码和系统环境。不过,可以尝试几个方向进行优化:

  1. 调整编译器和代码优化设置,以便更好地适应特定操作系统的环境。
  2. 注意内存管理和垃圾回收机制对JNI执行效率的影响,尽可能使用本地内存和手动内存管理来避免垃圾回收等问题。
  3. 了解线程模型和支持库的不同,并调整 JNI 代码以适应特定操作系统的环境。
  4. 针对 CPU 架构和指令集进行优化。

因为没有代码,我无法给出具体的优化建议。最好在工具或者 IDE 下面 profile 一下你的代码,找到性能瓶颈所在,并基于此做出优化。
如果我的回答解决了您的问题,请采纳!

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^