time.time()
i)  time.time()  w/o  torch.cuda.synchronize()

shuffle time: 0.5993 s
inf_time: 0.1185 s

ii)  time.time()  w/o  torch.cuda.synchronize() both before and after an operation

shuffle time: 0.0018 s
inf_time: 0.031 s

time.perf_counter()
i) use  time.perf_counter()  w/  torch.cuda.synchronize()

shuffle time: 0.0650 s
inf time: 0.0587 s

ii) use  time.perf_counter()  w/  torch.cuda.synchronize() both before and after an operation

shuffle time: 0.0021 s
inf time: 0.0309 s

time.process_time()
i) use  time.process_time()  w/  torch.cuda.synchronize()

shuffle time: 0.0879 s
inf time: 0.0584 s

ii) use  time.process_time()  w/ torch.cuda.synchronize() both before and after an operation

shuffle time: 0.001879 s
inf time: 0.03107 s

torch.cuda.Event
i) use  torch.cuda.Event  &  torch.cuda.synchronize()

shuffle time: 2.72 ms
inf_time: 59.88 ms

Conclusions

When time profiling, we can use both time module in python and torch.cuda.Event.
What we remember is, we must use torch.cuda.synchronize() right before and after the operations to  be profiled.
Measurements of time and cuda.Event is some what different, but the ratio is consistent.