Why is programmed I/O not suitable for high-speed data transfer?
Synchronous vs. asynchronous can mean a couple of different things.
With respect to fast communication, &quot;synchronous&quot; often means you send the data over a bus that includes a clock. In this case, you get faster transfers because data on the bus is synchronized to that clock. Quite a few older designs use some sort of handshake instead, so each word transferred across the bus requires a data transfer in one direction followed by a confirmation of some sort in the other direction before the next word is sent across the bus. Here, &quot;synchronous&quot; transfers avoid the overhead of that hand-shaking. Particularly for transfers over long distances (where the bus can add a lot of latency) you can gain a lot of speed by starting a second transfer across the bus long before a previous one has completed.
But with respect to programmed I/O, &quot;synchronous&quot; mostly means that the CPU is synchronized with each byte/word being transferred. So, if you're going to transfer a megabyte of data across a byte-wide bus, the CPU has to execute 1,048,576 individual port-write instructions. The CPU executes a port-write instruction, and right then one byte gets transferred. Read a byte from memory, write a byte to the port, and repeat.
This has a couple of problems. The big one is that unless the data transfer is pretty slow, it tends to occupy a lot of CPU time, and burn a lot of energy doing a relatively trivial thing. And nobody wants an expensive CPU tied up for seconds at a time, doing nothing but tending to a single transfer.
To avoid that, you can do interrupt-driven transfers. The peripheral interrupts the processor when it needs attention. The processor saves its state, services the peripheral, then restores the state and goes back to what it was going before. But saving and restoring state can add a lot of overhead there, so if the transfer is very fast, you're still wasting quite a lot of CPU time/energy without accomplishing much for it.
So, for fast transfers you generally want the bus itself to be synchronous, so you'll either have a clock signal alongside the data (e.g., memory channels) or you'll have some sort of self-clocking encoding, so you basically mix the clock and data together, but the far end can do some sort of clock recovery (e.g., Manchester encoding).
At the same time, you generally want the transfer to be asynchronous from the CPU. This typically means that instead of the CPU reading a byte, writing it to a port, and repeating, the CPU will send the peripheral a small command packet that basically says &quot;transfer the 4 kilobytes starting at address X, then the 4 kilobytes starting at address Y, then ..., and when you're done with all of that, give me an interrupt saying the transfer is done.&quot;1
This lets the CPU initiate a transfer that may take a long time, then move on to doing other things, and only worry about the transfer again when quite a lot of work has been done. So instead of saving and restoring state once every byte, it now has to save and restore state once every, say, megabyte or two, so the overhead of saving and restoring state stays low enough that it has no meaningful impact on the rest of the system.


In case you're wondering why it would be 4 kilobyte chunks rather than just saying: &quot;transfer a megabyte starting at address X&quot;: most CPUs use virtual addressing, so what software sees as a megabyte of data at contiguous virtual addresses will typically be composed of 4 kilobyte pages scattered more or less arbitrarily throughout memory. But the peripheral deals in physical addresses, so the CPU translates the virtual addresses to physical addresses, and gives the physical addresses to the peripheral. The pages might be larger than 4K, but regardless of exact size, it still has to give the peripheral the physical addresses of the individual pages.