复制到android中的半精度F16分配(renderscript)

时间:2016-11-15 09:15:36

标签: android floating-point precision allocation renderscript

这里也问了没有运气(https://groups.google.com/forum/#!topic/android-developers/Rh_L9Jv_S8Q

我正在尝试使用halfhalf4等类型来弄清楚如何进行半精度。唯一的问题似乎是将数字从java转换为renderscript并返回。

Java代码:

private float[] input;
private float[] half_output;
private RenderScript mRS;
private ScriptC_mono mScript;
private final int dimen = 15;
...

//onCreate
input = new float[dimen * dimen * 3];      //later loaded from file 182.24 3.98 105.83 226.08 15.2 80.01...
half_output = new float[dimen * dimen * 3];
...

//function calling renderscript
mRS = RenderScript.create(this);
ScriptC_halfPrecision mScript = new ScriptC_halfPrecision(mRS);

Allocation input2 = Allocation.createSized(mRS, Element.F16(mRS), dimen * dimen * 3);
input2.copyFromUnchecked(input);            //copy float values to F16 allocation

Allocation halfIndex = Allocation.createSized(mRS, Element.F16(mRS), dimen * dimen);
Type.Builder half_output_type = new Type.Builder(mRS, Element.F16(mRS)).setX(dimen * dimen * 3);
Allocation output3 = Allocation.createTyped(mRS, half_output_type.create());

mScript.set_half_in(input2);
mScript.set_half_out(output3);
mScript.forEach_half_operation(halfIndex);

output3.copy1DRangeToUnchecked(0, dimen * dimen * 3, half_output);  //copy F16 allocation back to float array

Renderscript:

#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.hellocompute)

rs_allocation half_in;
rs_allocation half_out;

half __attribute__((kernel)) half_operation(uint32_t x) {
    half4 out = rsGetElementAt_half4(half_in, x);

    out.x /= 2.0;
    out.y /= 2.0;
    out.z /= 2.0;
    out.w /= 2.0;

    rsSetElementAt_half4(half_out, out, x);
}

我也试过这个而不是Java代码中显示的最后一行:

float temp_half[] = new float[1];
for (int i = 0; i < dimen * dimen * 3; ++i) {     //copy F16 allocation back to float array
    output3.copy1DRangeToUnchecked(i, 1, temp_half);
    half_output[i]=temp_half[0];
}

以上所有代码都适用于renderscript中的float4变量和java中的F32分配。 这显然是因为从renderscript float到java float没有问题。 但是尝试从java float(因为没有java half)到renderscript half并再次返回是非常困难的。 谁能告诉我怎么做?

上述两个版本的java代码都会在half_output数组中产生看似随机的值。 它们显然不是随机的,因为无论half_operation(uint32_t x)函数中的操作如何,每次运行它们时它们都是相同的值。 我尝试将out.x /= 2.0;(以及相应的y,z,w代码)更改为out.x /= 2000000.0;out.x *= 2000000.0; 而且每次运行时,half_output数组中的值都是相同的。

使用182.24 3.98 105.83 226.08 15.2 80.01...

的输入

使用这个java

output3.copy1DRangeToUnchecked(0, dimen * dimen * 3, half_output);  //copy F16 allocation back to float array

结果half_output为46657.44 27094.48 3891.45 965.1825 36223.44 14959.08...

使用这个java

float temp_half[] = new float[1];
for (int i = 0; i < dimen * dimen * 3; ++i) {     //copy F16 allocation back to float array
    output3.copy1DRangeToUnchecked(i, 1, temp_half);
    half_output[i]=temp_half[0];
}

结果half_output为2.3476E-41 2.5546E-41 6.2047E-41 2.5407E-41 1.9802E-41 2.4914E-41...

无论我将out.x /= 2.0;算法更改为。

,这些都是结果

1 个答案:

答案 0 :(得分:0)

问题是此副本不进行转换。它只会将您的源FP32值放入内存中,但是当您尝试将这些值解释为FP16时,它们将是不正确的。

input2.copyFromUnchecked(input);            //copy float values to F16 allocation

您可以将此问题的答案移植到renderscript:

32-bit to 16-bit Floating Point Conversion

如果您的输入没有denorms / infinity / nan / overflow / underflow,这似乎是一个很好的解决方案:

uint32_t x = *((uint32_t*)&f);
uint16_t h = ((x>>16)&0x8000)|((((x&0x7f800000)-0x38000000)>>13)&0x7c00)|((x>>13)&0x03ff);

真正的解决方案是将文件中的源值设置为fp16二进制格式。将它们读入java byte []数组,然后将副本复制到fp16输入分配中。然后当renderscript内核将它们解释为fp16时,你应该没有问题。