No it does not. The 0 byte executable involves first trying to execl() it, the OS returning ENOEXEC, and then spawning a shell.
Of course "spawning a shell" can be very cheap, as you are already in a shell, it's just fork() plus some initialization code (compared to fork() plus exec() for running a non-shell program)
Note that shells must try to interpret at least any text file for which the system returns ENOEXEC [1]
Oh it is not nearly as good as I thought then. I actually tested it and zero byte exec still make exec() returns ENOEXEC on Linux. So a small binary is definitively more versatile...
I suggest measuring; there's overhead to the failed exec() call certainly. It probably depends on a lot of other factors too (which, if any rc files are read in each mode, how fast your shell is to startup &c.).
So including the #! adds about 23% to the overhead of calling a shell script. Tested on Mint 19.1, bash.
EDIT: for reference, sourcing the scripts instead takes an average of 0.0842s for test1 and 0897s for test2. (and all test2 trials were still slower than all test1 trials), which isn't particuarly suprising.
To keep the comparison fair. My interactive shell is bash (which I assume is a more popular choice for interactive shell then sh), and the version without any shebang will use whatever the parent shell is. To make the comparison fair, I wanted the version with the shebang to be using the same shell.
Besides, /bin/sh would also require dereferencing a symlink (to /bin/dash), which also doesn't seem fair.
Of course "spawning a shell" can be very cheap, as you are already in a shell, it's just fork() plus some initialization code (compared to fork() plus exec() for running a non-shell program)
Note that shells must try to interpret at least any text file for which the system returns ENOEXEC [1]
1: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3... Read subsection 1.e.i.b