The implementation of true also makes a difference! Not quite an order of magnitude difference, though (except for the shell builtin).
method Hz comment
--------------------------------------------------------
empty file 500 an empty file gets passed to /bin/sh
dynamic libc 1000 "int main { return 0; }" -> gcc
static libc 1500 the same, but with "gcc -static"
assembly 2000 see below
bash builtin 150000 avoids hitting the kernel or filesystem
The empty file is the "traditional" implementation of true on Unix.
The assembly solution was my attempt at doing the littlest amount possible, because libc initialization still takes time:
.globl _start
_start:
movl $1, %eax # %eax = SYS_exit
xorl %ebx, %ebx # %ebx = 0 (exit status)
int $0x80