Skip to content

Question: Why Does a JNI Method Operate Faster than a Native C++ Method #52

@Advay17

Description

@Advay17

As mentioned in #51, I am using both C++ and Java for a project. One of the things I have done is adapt Bullet's STL importer example to be a JNI method(yes, I could have done it through Java but I just wanted to try making my own JNI method to gain experience). From my testing, when I use it in native code it is very slow when importing one of the larger STL files I work with(80k ish triangles). It took roughly 1 minute. However, when I ran the same exact code as a part of the JNI, no alterations or anything, the file was imported properly in less than 5 seconds. Is the reason for this due to any optimizations performed on the Java side of Libbulletjme, or is it because of JNI specific optimizations inherent to Java itself?

Code for importer:

#include "STLImporter.h"
#include

btCollisionShape* lantern::LoadSTLAsCollisionShape(const std::string filePath, bool staticMesh, bool flipYZ){
//TODO: Condense the if and else stuff into one function using suppliers or smth
if(staticMesh){
btTriangleMesh meshInterface = new btTriangleMesh();
CommonFileIOInterface
fileIO = new b3BulletDefaultFileIO();
int fileHandle = fileIO->fileOpen(&(filePath.at(0)), "rb");

    int yIndex = (flipYZ) ? 2 : 1;
    int zIndex = (flipYZ) ? 1 : 2;

    if (fileHandle>=0)
    {
        int size = 0;
        size = fileIO->getFileSize(fileHandle);
        {
            if (size>=0)
            {
                char* memoryBuffer = new char[size + 1];
                int actualBytesRead = fileIO->fileRead(fileHandle, memoryBuffer, size);
                if (actualBytesRead != size)
                {
                    throw std::runtime_error("Error reading STL file: " + filePath + ". Actual file size does not match expected size.");
                }
                else
                {
                    int numTriangles = *(int*)&memoryBuffer[80];

                    if (numTriangles)
                    {
                        {
                            //perform a sanity check instead of crashing on invalid triangles/STL files
                            int expectedBinaryFileSize = numTriangles * 50 + 84;
                            if (expectedBinaryFileSize != size)
                            {
                                delete[] memoryBuffer;
                                fileIO->fileClose(fileHandle);
                                throw std::runtime_error(
		"Error reading STL file: " + filePath + ". Actual file size does not match expected size.");
                                return 0;
                            }
                        }
                        
                        int index = 0;
                        printf("Loading %d triangles...\n", numTriangles);
                        
                        auto startTime = std::chrono::steady_clock::now();
                        auto lastLogTime = startTime;
                        int trianglesProcessed = 0;
                        
                        for (int i = 0; i < numTriangles; i++)
                        {
                            char* curPtr = &memoryBuffer[84 + i * 50];
                            TempSTLTriangle tmp;
                            memcpy(&tmp, curPtr, sizeof(TempSTLTriangle));
                            meshInterface -> addTriangle(
                                 btVector3(
                                    tmp.vertex0[0],
                                    tmp.vertex0[yIndex],
                                    tmp.vertex0[zIndex]),
                                btVector3(
                                    tmp.vertex1[0],
                                    tmp.vertex1[yIndex],
                                    tmp.vertex1[zIndex]),
                                btVector3(
                                    tmp.vertex2[0],
                                    tmp.vertex2[yIndex],
                                    tmp.vertex2[zIndex]),
                                true);

                            trianglesProcessed++;
                            
                            // Log every 5 seconds
                            auto currentTime = std::chrono::steady_clock::now();
                            auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(currentTime - lastLogTime).count();
                            
                            if (elapsed >= 5)
                            {
                                double trianglesPerSecond = trianglesProcessed / (double)elapsed;
                                double percentComplete = (i + 1) * 100.0 / numTriangles;
                                printf("Progress: %d/%d triangles (%.1f%%) - %.2f triangles/sec\n", 
                                       i + 1, numTriangles, percentComplete, trianglesPerSecond);
                                lastLogTime = currentTime;
                                trianglesProcessed = 0;
                            }
                        }
                        
                        printf("Completed: %d/%d triangles (100.0%%)\n", numTriangles, numTriangles);
                    }
                }

                delete[] memoryBuffer;
            }
        }
    }
    fileIO->fileClose(fileHandle);
    delete fileIO;

    return new btBvhTriangleMeshShape(meshInterface, true);
}
else{
    btConvexHullShape* shape  = new btConvexHullShape();
    CommonFileIOInterface* fileIO = new b3BulletDefaultFileIO();
    int fileHandle = fileIO->fileOpen(&(filePath.at(0)), "rb");
    
    int yIndex = (flipYZ) ? 2 : 1;
    int zIndex = (flipYZ) ? 1 : 2;

    if (fileHandle>=0)
    {
        int size = 0;
        size = fileIO->getFileSize(fileHandle);
        {
            if (size>=0)
            {
                char* memoryBuffer = new char[size + 1];
                int actualBytesRead = fileIO->fileRead(fileHandle, memoryBuffer, size);
                if (actualBytesRead != size)
                {
                    throw std::runtime_error("Error reading STL file: " + filePath + ". Actual file size does not match expected size.");
                }
                else
                {
                    int numTriangles = *(int*)&memoryBuffer[80];

                    if (numTriangles)
                    {
                        {
                            //perform a sanity check instead of crashing on invalid triangles/STL files
                            int expectedBinaryFileSize = numTriangles * 50 + 84;
                            if (expectedBinaryFileSize != size)
                            {
                                delete[] memoryBuffer;
                                fileIO->fileClose(fileHandle);
                                return 0;
                            }
                        }
                        
                        int index = 0;
                        printf("Loading %d triangles...\n", numTriangles);
                        
                        auto startTime = std::chrono::steady_clock::now();
                        auto lastLogTime = startTime;
                        int trianglesProcessed = 0;
                        
                        for (int i = 0; i < numTriangles; i++)
                        {
                            char* curPtr = &memoryBuffer[84 + i * 50];
                            TempSTLTriangle tmp;
                            memcpy(&tmp, curPtr, sizeof(TempSTLTriangle));

                            shape -> addPoint(btVector3(
                                    tmp.vertex0[0],
                                    tmp.vertex0[yIndex],
                                    tmp.vertex0[zIndex]));
                            shape -> addPoint(btVector3(
                                tmp.vertex1[0],
                                tmp.vertex1[yIndex],
                                tmp.vertex1[zIndex]));
                            shape -> addPoint(btVector3(
                                tmp.vertex2[0],
                                tmp.vertex2[yIndex],
                                tmp.vertex2[zIndex]));

                            trianglesProcessed++;
                            
                            // Log every 5 seconds
                            auto currentTime = std::chrono::steady_clock::now();
                            auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(currentTime - lastLogTime).count();
                            
                            if (elapsed >= 5)
                            {
                                double trianglesPerSecond = trianglesProcessed / (double)elapsed;
                                double percentComplete = (i + 1) * 100.0 / numTriangles;
                                printf("Progress: %d/%d triangles (%.1f%%) - %.2f triangles/sec\n", 
                                       i + 1, numTriangles, percentComplete, trianglesPerSecond);
                                lastLogTime = currentTime;
                                trianglesProcessed = 0;
                            }
                        }
                        
                        printf("Completed: %d/%d triangles (100.0%%)\n", numTriangles, numTriangles);
                    }
                }

                delete[] memoryBuffer;
            }
        }
    }
    fileIO->fileClose(fileHandle);
    delete fileIO;

    return shape;
}

}

Code for JNI:

JNIEXPORT jlong JNICALL Java_com_vendor_lantern_util_importers_STLImporter_loadSTLAsCollisionShape(
JNIEnv *pEnv, jclass, jstring filename) {
jmeClasses::initJavaClasses(pEnv);
const char *nativeFilename = pEnv->GetStringUTFChars(filename, 0);
NULL_CHK(pEnv, nativeFilename, "The filename string is null.", 0)

btCollisionShape *shape = lantern::LoadSTLAsCollisionShape(nativeFilename,
		true);

pEnv->ReleaseStringUTFChars(filename, nativeFilename);
EXCEPTION_CHK(pEnv, 0);

return reinterpret_cast<jlong>(shape);

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions