And also, what matters for performance is the size of the "hot" code. A lot of code, both on a monolithic kernel and on the userland code implementing system services for a microkernel, is going to be "cold" code which executes rarely. This includes initialization code, shutdown code, most of the error handling code, code to react to uncommon hardware events, and so on.
> If my program wants to read a file and write to the network, then the code to do that has to be brought to the CPU to run before my program can finish doing that, and it doesn't matter whether that is in the kernel or userland.
And given that the essential complexity is the same, it's very likely that the size of the relevant code is similar.
The reason microkernels typically benchmark on syscall (typically IPC) latency is because that is the only truly apples to oranges part that you can't compare between the two. Monolithic kernels have a single context switch, microkernels have two. But microkernels, being micro, are a lot friendlier to caches (less to load and flush), and especially so with dedicated code caches (which do not need to flush on context switch).
This makes no sense. The comparison that matters isn't kernel vs microkernel, its kernel vs microkernel plus all the userland code needed to implement system services. If my program wants to read a file and write to the network, then the code to do that has to be brought to the CPU to run before my program can finish doing that, and it doesn't matter whether that is in the kernel or userland.