- Loading...
...
Here is a simple example where we want to produce an output array of the squared value of a sequence of integers. But the logic that computes the output array index will cause an ArrayIndexOutOfBoundsException about halfway through the range. You can build and run this using the instructions on Standalone Sumatra Stream API Offload Demo assuming you are using the latest graal and sumatra trunks.
Try running with -Dcom.amd.sumatra.offload.immediate=false (for normal JDK stream parallel operation) and -Dcom.amd.sumatra.offload.immediate=true (so the lambda will be offloaded to the GPU). Note that the stack trace shows the same trace lines through the context of the lambda itself. (Lines further up the stack will be dependent on the internal mechanism used to run the lambda across the range).
Note that on any run some output array slots contain their original -1 value, indicating the workitem for that entry did not run. The set of workitems that did not run may be different for the GPU vs. CPU cases, in fact it may be different for any two GPU runs, or any two CPU runs. The semantics for an exception on a stream operation is that the first exception is reported and pending new workitems will not run. Since the lambda is executing in parallel across the range, the set of workitems that might not run because of the exception is implementation-dependent. As a further experiment, you could try removing the .parallel() call from the forEach invocation, and see yet another output array configuration for a non-parallel run.
package simpledeopt;
import java.util.stream.IntStream;
import java.util.Arrays;
public class SimpleDeopt {
public static void main(String[] args) {
final int length = 20;
int[] output = new int[length];
Arrays.fill(output, -1);
try {
// offloadable since it is parallel
// will trigger exception halfway thru the range
IntStream.range(0, length).parallel().forEach(p -> {
int outIndex = (p == length/2 ? p + length : p);
writeIntArray(output, outIndex, p * p);
});
} catch (Exception e) {
e.printStackTrace();
}
// Print results - not offloadable since it is not parallel
IntStream.range(0, length).forEach(p -> {
System.out.println(p + ", " + output[p]);
});
}
static void writeIntArray(int[] ary, int index, int val) {
ary[index] = val;
}
}
We use the graal compiler to generate the hsail code. The graal compiler has a mature infrastructure for supporting deoptimization and still achieving good code quality. See http://design.cs.iastate.edu/vmil/2013/papers/p04-Duboscq.pdf. Basically the compiler nicely keeps track of the deoptimization state at each deopt point, and from that we can tell what HSAIL registers need to be saved, which registers contain oops, etc.
...