流子 2023-03-11 19:23 采纳率: 25%
浏览 103
已结题

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 ( )
  • 写回答

5条回答 默认 最新

  • CodeBytes 2023-03-11 19:35
    关注

    该回答引用ChatGPT

    如有疑问,可以回复我!

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

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

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

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

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

    评论

报告相同问题?

问题事件

  • 专家修改了标签 3月18日
  • 已结题 (查看结题原因) 3月18日
  • 赞助了问题酬金15元 3月12日
  • 赞助了问题酬金15元 3月12日
  • 展开全部

悬赏问题

  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题
  • ¥15 matlab不知道怎么改,求解答!!
  • ¥15 永磁直线电机的电流环pi调不出来
  • ¥15 用stata实现聚类的代码
  • ¥15 请问paddlehub能支持移动端开发吗?在Android studio上该如何部署?
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改