* @param shape desired output shape (can be larger than input shape, output * will be padded with -Infinity)
(shape: number[])
| 47 | * will be padded with -Infinity) |
| 48 | */ |
| 49 | constructor(shape: number[]) { |
| 50 | this.outputShape = shape; |
| 51 | |
| 52 | this.userCode = ` |
| 53 | void main() { |
| 54 | ivec2 coords = getOutputCoords(); |
| 55 | int batch = coords[0]; |
| 56 | int elemIdx = coords[1]; |
| 57 | |
| 58 | // We compare elements pair-wise within a group of size 2 * inc. |
| 59 | // The comparing rule for each group alternates between ascending |
| 60 | // and descending. Within each group, we compare each pair at |
| 61 | // positions i and i+inc. To decide whether an element at position i |
| 62 | // is x0 or x1, we mod it by 2 * inc, if the result is smaller than |
| 63 | // inc, it is in the first half of the group, we denote it as x0, |
| 64 | // otherwise we denote it as x1. |
| 65 | // For example, as shown in the Bitonic top K paper referenced above, |
| 66 | // Figure5(a) shows that element[1] is in the |
| 67 | // second half of the group when group size is 2, but it is in the |
| 68 | // first half of the group when group size is 4. |
| 69 | |
| 70 | bool isFirstInPair = imod(elemIdx, 2 * inc) < inc; |
| 71 | int i = isFirstInPair ? elemIdx : elemIdx - inc; |
| 72 | |
| 73 | int i0 = firstPass == 1 ? i : int(getIndices(batch, i)); |
| 74 | int i1 = firstPass == 1 ? i + inc : int(getIndices(batch, i + inc)); |
| 75 | float x0 = i0 < n ? getX(batch, i0) : negativeInf; |
| 76 | float x1 = i1 < n ? getX(batch, i1) : negativeInf; |
| 77 | |
| 78 | // Denotes which direction indices are in (ascending or descending). |
| 79 | bool reverse = imod(elemIdx, 2 * dir) >= dir; |
| 80 | bool isGreater = x0 > x1 || (x0 == x1 && i1 > i0); |
| 81 | if (reverse == isGreater) { // Elements in opposite order of direction |
| 82 | int iTemp = i0; |
| 83 | i0 = i1; |
| 84 | i1 = iTemp; |
| 85 | } |
| 86 | if (isFirstInPair) { |
| 87 | setOutput(float(i0)); |
| 88 | } else { |
| 89 | setOutput(float(i1)); |
| 90 | } |
| 91 | } |
| 92 | `; |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | export class MergeProgram implements GPGPUProgram { |
nothing calls this directly
no outgoing calls
no test coverage detected