# Gather the Winners

*Task 2 of 5 · [Top-K Selection](https://gpu.rocks/learn/top-k-selection-1ba56df3.md) · GPU.js Learn*

Ranks are the answer in an unusable shape: the ten you want are scattered somewhere
among 4,096 slots. What you want is packed — `top[0]` the biggest score,
`top[9]` the tenth biggest.

The obvious move is a **scatter**: element `i` writes itself into
`top[ranks[i]]`. Kernels cannot do that — a thread writes one cell, its own. So
turn it inside out, the way every scatter gets turned inside out. Instead of "where does my
value go?", output slot `j` asks **"who has rank `j`?"**
and goes looking. Ten threads, each scanning 4,096 ranks: a gather.

Exactly one element answers each slot — which is what last task's tie-break bought you.
(Turning a rank array into a packed result is a pattern in its own right, and the Stream
Compaction module develops it properly, with the prefix sum that makes it O(n) instead of
O(k·n). You do not need that here: `k` is ten.)

## Goal

**Goal:** fill ten output slots with the ten largest scores, largest
first — slot `j` holds the score of the element whose rank is `j`.

## Requirements

- `output: [10]` — one thread per result slot
- Scan all `this.constants.n` ranks for the one equal to `this.thread.x`
- Return that element's *score*, not its rank
- `top[0]` is the largest score and `top[9]` the tenth largest

## Hint 1 — which element is mine?

Thread `j` owns output slot `j`, and the element it wants
is the one whose rank happens to be `j`. There is no way to know where that
element sits, so look at all of them — a loop over the whole `ranks`
array.

## Hint 2 — the scan

```js
let best = 0;
for (let i = 0; i < this.constants.n; i++) {
  if (ranks[i] === this.thread.x) best = scores[i];
}
return best;
```

No `break` needed — exactly one `i` matches.

## Same idea elsewhere

Gather-by-rank is the back half of every GPU sort: compute a destination for each
element, then have each destination fetch its element — `thrust::gather`,
`cub::DeviceRadixSort`'s final scatter pass, a WebGPU compute shader indexing a
storage buffer. Production k-selection libraries (FAISS, RAFT's `select_k`) do
exactly this once the candidates are down to a manageable few.

## Starter code

```js
// Ranks in, a packed top-10 out. Slot j goes looking for rank j.
const gpu = new GPU({ mode });

// Last task's kernel, unchanged.
const rankScores = gpu.createKernel(function (scores) {
  const mine = scores[this.thread.x];
  let ahead = 0;
  for (let j = 0; j < this.constants.n; j++) {
    const other = scores[j];
    if (j < this.thread.x) {
      if (other >= mine) ahead++;
    } else if (other > mine) {
      ahead++;
    }
  }
  return ahead;
}, { output: [4096], constants: { n: 4096 } });

const pickTop = gpu.createKernel(function (scores, ranks) {
  let best = 0;
  for (let i = 0; i < this.constants.n; i++) {
    // TODO: every slot is fetching rank 0. Slot this.thread.x wants
    // the element whose rank is this.thread.x.
    if (ranks[i] === 0) best = scores[i];
  }
  return best;
}, { output: [10], constants: { n: 4096 } });

const ranks = await rankScores(scores);
const top = await pickTop(scores, ranks);
console.log('top 10:', top);
```

---

Interactive version: https://gpu.rocks/learn/top-k-selection-1ba56df3/2

[Previous task](https://gpu.rocks/learn/top-k-selection-1ba56df3/1.md) · [Next task](https://gpu.rocks/learn/top-k-selection-1ba56df3/3.md)
