简体   繁体   English

通过JNA传递从Java浮点数到C动态库的指针

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

Catboost offers a dynamic C library , which theoretically can be used from any programming language. Catboost提供了一个动态C库 ,理论上可以从任何编程语言中使用。

I'm trying to call it through Java using JNA. 我正在尝试使用JNA通过Java调用它。

I'm having a problem with the CalcModelPrediction function, defined in the header file as follows: 我在头文件中定义的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);

In Java, I've defined the interface function as follows: 在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);
    }

and then I'm calling it like this: 然后我这样称呼它:

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);
}

I've tried passing Pointer , PointerByReference and Pointer[] to the CalcModelPrediction function in place of float **floatFeatures and char ***catFeatures but nothing worked. 我试过将PointerPointerByReferencePointer[]传递给CalcModelPrediction函数,以代替float **floatFeatureschar ***catFeatures但是没有任何效果。 I always get a segmentation fault, presumably when the CalcModelPrediction function attempts to get elements of floatFeatures and catFeatures by calling floatFeatures[0][0] and catFeatures[0][0] . 我总是遇到分段错误,大概是当CalcModelPrediction函数试图通过调用floatFeatures[0][0]catFeatures[0][0]来获取floatFeaturescatFeatures的元素时。

So the question is, what's the right way of passing a multi-dimensional array from Java through JNA into C, where it could be treated as a pointer to a pointer to a value? 所以问题是,将Java中的多维数组通过JNA传递到C的正确方法是什么,在多维数组中它可以被视为指向值的指针?

An interesting thing is that the CalcModelPredictionFlat function that accepts only float **floatFeatures and then simply calls *floatFeatures , works perfectly fine when passing PointerByReference . 有趣的是,仅float **floatFeatures然后仅调用*floatFeaturesCalcModelPredictionFlat函数在传递PointerByReference时工作得很好。

UPDATE - 5.5.2018 更新-5.5.2018

Part 1 第1部分

After trying to debug the segfault by slightly modifying the original Catboost .cpp and .h files and recompiling the libcatboost.so library, I found out that the segfault was due to me mapping size_t in C to int in Java. 在尝试通过稍微修改原始的Catboost .cpp和.h文件并重新编译libcatboost.so库来调试segfault之后,我发现该segfault是由于我将C中的size_t映射到Java中的int所致。 After fixing this, my interface function in Java looks like this: 修复此问题后,我在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);
    }

Where the size_t Class is defined as follows: 其中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); }                       
}

Part 2 Looking more into the Catboost code, I've noticed that **floatFeatures are being accessed by rows, like floatFeatures[i] while ***catFeatures are accessed by rows and columns, like catFetures[i][catFeatureIdx] . 第2部分:深入研究Catboost代码,我注意到**floatFeatures被行访问,例如floatFeatures[i]***catFeatures被行和列访问,例如catFetures[i][catFeatureIdx]

After changing floatFeatures in Java to an array of Pointer , my code started to work with the model trained without categorical features, ie catFeatures length is zero. 在将Java中的floatFeatures更改为Pointer数组之后,我的代码开始使用经过训练的没有分类特征的模型,即catFeatures长度为零。

This trick, however, didn't work with catFeatures that are accessed through a double subscript operator [i][catFeatureidx] . 但是,此技巧catFeatures用于通过双下标运算符[i][catFeatureidx]访问的[i][catFeatureidx] So for now, I modified the original Catboost code so that it would accept char **catFeatures - an array of strings. 因此,现在,我修改了原始的Catboost代码,使其可以接受char **catFeatures字符串数组。 In Java interface function, I set String[] catFeatures . 在Java接口函数中,我设置了String[] catFeatures Now I can make predictions for one element at a time, which is not ideal. 现在,我可以一次对一个元素进行预测,这并不理想。

I've managed to make it all work with the original Catboost code and libcatboost.so . 我设法使其与原始的Catboost代码和libcatboost.so

The Java interface function is defined like this. Java接口函数是这样定义的。 Note that in order to emulate a 2D array (or a pointer to a pointer) of float values and strings, I'm using Pointer[] type. 请注意,为了模拟浮点值和字符串的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);
    }

After that, I populate the floatFeatures and catFeatures parameters like this (some dummy data here). 之后,我像这样填充floatFeaturescatFeatures参数(此处为一些虚拟数据)。 Note that for strings I'm using JNA's StringArray . 请注意,对于字符串,我使用的是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;

Finally, I pass these parameters to Catboost: 最后,我将这些参数传递给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());
}

To get predictions we can do: 要获得预测,我们可以执行以下操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM