This is just the normal cost of ProcessExist, nothing to do with exception handling. ProcessGetPath allows a process name and therefore relies on ProcessExist to identify the process, before attempting to retrieve the path.
ProcessExist enumerates all processes to find a match. When you pass a PID, it does this to verify that the PID is valid. On v1, this is the only method Process Exist uses, and it is always this slow. v2 has been optimized to directly open the process and verify that it is still running, but it cannot do this if it doesn't have permission to open the process, so it falls back to the older method.
When
ProcessExist(pid) fails with ERROR_ACCESS_DENIED, it cannot assume that the PID is valid because:
// OpenProcess can succeed for erroneous PID values; e.g. values of 1501 to 1503 can
// open the process with ID 1500. This is likely due to the undocumented fact that
// PIDs are a multiple of four:
https://devblogs.microsoft.com/oldnewthing/20080228-00/?p=23283
...
// OpenProcess can succeed for a process which has already exited if another process
// still has an open handle to it. So check whether it's still running:
In other words, ERROR_ACCESS_DENIED could also be returned for an invalid PID, or in a case where the process has a handle open, but the process itself has exited.
So when ProcessExist fails to open the process, it falls back to the old method of enumerating all processes. Another reason for falling back to enumeration when you give it a number is that process names can consist solely of digits, although ProcessExist might not be able to tell you the PID of such a process if its name coincides with a valid PID.
ProcessGetPath could skip the enumeration when given a PID, but in that case it could give an "access denied" error when you've really passed an invalid PID.