簡體   English   中英

通過JNA傳遞從Java浮點數到C動態庫的指針

[英]Passing pointer to pointer to float from Java through JNA to a C dynamic library

Catboost提供了一個動態C庫 ,理論上可以從任何編程語言中使用。

我正在嘗試使用JNA通過Java調用它。

我在頭文件中定義的CalcModelPrediction函數遇到問題,如下所示:

EXPORT bool CalcModelPrediction(
    ModelCalcerHandle* calcer,
    size_t docCount,
    const float** floatFeatures, size_t floatFeaturesSize,
    const char*** catFeatures, size_t catFeaturesSize,
    double* result, size_t resultSize);

在Java中,我將接口函數定義如下:

public interface CatboostModel extends Library {
        public Pointer ModelCalcerCreate();
        public String GetErrorString();
        public boolean LoadFullModelFromFile(Pointer calcer, String filename);
        public boolean CalcModelPrediction(Pointer calcer, int docCount,
                PointerByReference floatFeatures, int floatFeaturesSize,
                PointerByReference catFeatures, int catFeaturesSize,
                Pointer result, int resultSize);
        public int GetFloatFeaturesCount(Pointer calcer);
        public int GetCatFeaturesCount(Pointer calcer);
    }

然后我這樣稱呼它:

CatboostModel catboost;
Pointer modelHandle;

catboost = Native.loadLibrary("catboostmodel", CatboostModel.class);
            modelHandle = catboost.ModelCalcerCreate();
if (!catboost.LoadFullModelFromFile(modelHandle, "catboost_test.model"))
{
    throw new RuntimeException("Cannot load Catboost model.");
}

final PointerByReference ppFloatFeatures = new PointerByReference();
final PointerByReference ppCatFeatures = new PointerByReference();
final Pointer pResult = new Memory(Native.getNativeSize(Double.TYPE));

float[] floatFeatures = {0.5f, 0.8f, 0.3f, 0.3f, 0.1f, 0.5f, 0.4f, 0.8f, 0.3f, 0.3f} ;
String[] catFeatures = {"1", "2", "3", "4"};
int catFeaturesLength = 0;
for (String s : catFeatures)
{
    catFeaturesLength += s.length() + 1;
}

try
{
    final Pointer pFloatFeatures = new Memory(floatFeatures.length * Native.getNativeSize(Float.TYPE));
    for (int dloop=0; dloop<floatFeatures.length; dloop++) {
        pFloatFeatures.setFloat(dloop * Native.getNativeSize(Float.TYPE), floatFeatures[dloop]);
    }
    ppFloatFeatures.setValue(pFloatFeatures);

    final Pointer pCatFeatures = new Memory(catFeaturesLength * Native.getNativeSize(Character.TYPE));
    long offset = 0;
    for (final String s : catFeatures) {
        pCatFeatures.setString(offset, s);
        pCatFeatures.setMemory(offset + s.length(), 1, (byte)(0));
        offset += s.length() + 1;
    }
    ppCatFeatures.setValue(pCatFeatures);

}
catch (Exception e)
{
    throw new RuntimeException("Couldn't initialize parameters for catboost");
}

try
{
    if (!catboost.CalcModelPrediction(
                modelHandle,
                1,
                ppFloatFeatures, 10,
                ppCatFeatures, 4,
                pResult, 1
                ))
    {
        throw new RuntimeException("No prediction made: " + catboost.GetErrorString());
    }
    else
    {
        double[] result = pResult.getDoubleArray(0, 1);
        log.info("Catboost prediction: " + String.valueOf(result[0]));
        Assert.assertFalse("ERROR: Result empty", result.length == 0);
    }
}
catch (Exception e)
{
    throw new RuntimeException("Prediction failed: " + e);
}

我試過將PointerPointerByReferencePointer[]傳遞給CalcModelPrediction函數,以代替float **floatFeatureschar ***catFeatures但是沒有任何效果。 我總是遇到分段錯誤,大概是當CalcModelPrediction函數試圖通過調用floatFeatures[0][0]catFeatures[0][0]來獲取floatFeaturescatFeatures的元素時。

所以問題是,將Java中的多維數組通過JNA傳遞到C的正確方法是什么,在多維數組中它可以被視為指向值的指針?

有趣的是,僅float **floatFeatures然后僅調用*floatFeaturesCalcModelPredictionFlat函數在傳遞PointerByReference時工作得很好。

更新-5.5.2018

第1部分

在嘗試通過稍微修改原始的Catboost .cpp和.h文件並重新編譯libcatboost.so庫來調試segfault之后,我發現該segfault是由於我將C中的size_t映射到Java中的int所致。 修復此問題后,我在Java中的接口函數如下所示:

public interface CatboostModel extends Library {
        public boolean LoadFullModelFromFile(Pointer calcer, String filename);
        public boolean CalcModelPrediction(Pointer calcer, size_t docCount,
                Pointer[] floatFeatures, size_t floatFeaturesSize,
                String[] catFeatures, size_t catFeaturesSize,
                Pointer result, size_t resultSize);
    }

其中size_t類的定義如下:

public static class size_t extends IntegerType {                                           
    public size_t() { this(0); }                                                           
    public size_t(long value) { super(Native.SIZE_T_SIZE, value); }                       
}

第2部分:深入研究Catboost代碼,我注意到**floatFeatures被行訪問,例如floatFeatures[i]***catFeatures被行和列訪問,例如catFetures[i][catFeatureIdx]

在將Java中的floatFeatures更改為Pointer數組之后,我的代碼開始使用經過訓練的沒有分類特征的模型,即catFeatures長度為零。

但是,此技巧catFeatures用於通過雙下標運算符[i][catFeatureidx]訪問的[i][catFeatureidx] 因此,現在,我修改了原始的Catboost代碼,使其可以接受char **catFeatures字符串數組。 在Java接口函數中,我設置了String[] catFeatures 現在,我可以一次對一個元素進行預測,這並不理想。

我設法使其與原始的Catboost代碼和libcatboost.so

Java接口函數是這樣定義的。 請注意,為了模擬浮點值和字符串的2D數組(或指向指針的指針),我使用Pointer[]類型。

public interface CatboostModel extends Library {
        public boolean LoadFullModelFromFile(Pointer calcer, String filename);
        public boolean CalcModelPrediction(Pointer calcer, size_t docCount,
                Pointer[] floatFeatures, size_t floatFeaturesSize,
                Pointer[] catFeatures, size_t catFeaturesSize,
                Pointer result, size_t resultSize);
    }

之后,我像這樣填充floatFeaturescatFeatures參數(此處為一些虛擬數據)。 請注意,對於字符串,我使用的是JNA的StringArray

float[] floatFeatures = {0.4f, 0.8f, 0.3f, 0.3f, 0.1f, 0.5f, 0.4f, 0.8f, 0.3f, 0.3f} ;
String[] catFeatures = {"1", "2", "3", "4"};

final Pointer pFloatFeatures = new Memory(floatFeatures.length * Native.getNativeSize(Float.TYPE));
final Pointer[] ppFloatFeatures = new Pointer[2];
for (int dloop=0; dloop<10; dloop++) {
    pFloatFeatures.setFloat(dloop * Native.getNativeSize(Float.TYPE), floatFeatures[dloop]);
}
ppFloatFeatures[0] = pFloatFeatures;
ppFloatFeatures[1] = pFloatFeatures;

final Pointer[] ppCatFeatures = new Pointer[catFeatures.length];
final Pointer pCatFeatures = new StringArray(catFeatures);
ppCatFeatures[0] = pCatFeatures;
ppCatFeatures[1] = pCatFeatures;

最后,我將這些參數傳遞給Catboost:

if (!catboost.CalcModelPrediction(
                modelHandle,
                new size_t(2L),
                ppFloatFeatures, new size_t((long)floatFeatures.length),
                ppCatFeatures, new size_t((long)catFeatures.length),
                pResult, new size_t(2L)
                ))
{
    throw new RuntimeException("No prediction made: " + catboost.GetErrorString());
}

要獲得預測,我們可以執行以下操作:

double[] result = pResult.getDoubleArray(0, 2);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM