The difference between the code snippets seems to be the sequential vs. parallel execution.
Each CUDA operation will launch a kernel, which will create some overhead.
This overhead of the kernel launch is often not visible, if the actual workload on the GPU is large.
However, in your first example your workload is tiny (two scalar tensors are used), while you are triggering 100k CUDA operations, which would create a large overhead.
That being said, you should synchronize the code via torch.cuda.synchronize() before starting and stopping the timer. In your second example especially you might only profile the actual kernel launch, not the complete operation.