This problem is called stream compaction and there is a wealth of research on it. The best methods use prefix scan. They first efficiently compute the index in the output array of each element that satisfies the predicate and then they gather them in one linear operation.

Also, I can tell that you are a good writer. You didn't need the LLM to "polish" your text.

There is clearly LLM-prose involved, but it's pretty well done. Here's one example: "The reallocations were real, but they were never the bottleneck."

LLMs love this pattern. Whether one put it into this text, or the author soaked it up and now used it himself, who knows. But it is one of the few things in the post that gives me the ick.

And then, there's the verbosity.

If I had to guess, an LLM was involved, but the author did a good job with manual writing and editing, too.

If your objects are large, I can see why you would compute indices first. But why do that for floats?

Because what makes stream compaction challenging is the loop carried dependency: the location you write to in a given iteration depends on the locations you wrote to in previous iterations. By first creating a lookup table of source -> destination locations you remove the dependency. Then you can apply extremely efficient parallel methods.