amyxwgu:

Will this generate thousands of versions of the tensor x in the memory?


Not all at the same time. But with this, the optimizer.step is literally x -= lr * x.grad and the trick will break as soon as you have momentum etc.
I would have to ask: why do you want _version to be unchanged? To my mind, it should be updated – you are wanting to modify x after all. _version exists in order to let autograd detect when it needs something for the backward that has been changed after it has been recorded during the backward (i.e. is invalid for autograd purposes now).
The generic idiomatic thing to do IMO is
    # your stuff here
    optimizer.step()
    with torch.no_grad():
        x.copy_(projection(x))

Of course if you change projection to update x in-place, you can skip the copy_.
Best regards
Thomas