I think you would be seeing the same performance, if you start the timer before applying the manual x.copy() in every second iteration.
If I’m not mistaken, torch.as_tensor(x).to('cuda') would trigger a copy internally, as it would need to create a clone of the underlying numpy array data and push it to the device. Since your manual copy() operation already triggered this copy and you are starting the timer afterwards, it looks as if it would come for “free”.
After moving t0 right before the if create_copy condition, I get the same iteration speed.