[英]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. 我试过将Pointer
, PointerByReference
和Pointer[]
传递给CalcModelPrediction
函数,以代替float **floatFeatures
和char ***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]
来获取floatFeatures
和catFeatures
的元素时。
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
然后仅调用*floatFeatures
的CalcModelPredictionFlat
函数在传递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). 之后,我像这样填充floatFeatures
和catFeatures
参数(此处为一些虚拟数据)。 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.