А причина достаточно простая, жёсткий диск — это блочное устройство и работа с ним ведётся блоками данных. Если вы читаете или пишете (как в случае выше) 1 байт, вы всё равно записываете блок данных, который больше 1 байта, а может быть размером от 512 и более байт, которое кратно 512 байтам.
Причина не только в этом. При использовании мелких блоков происходит частое переключение между чтением и записью. Вот пример, где те же команды при записи в файл, находящийся в tmpfs, показывает похожую с жестким диском разницу:
$ mount | grep '/tmpfs'
tmpfs on /tmpfs type tmpfs (rw,relatime,size=10485760k,inode64)
$ time $(dd if=/dev/zero of=/tmpfs/test.raw bs=1 count=1M && sync)
1048576+0 records in
1048576+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.4183 s, 2.5 MB/s
real 0m0.429s
user 0m0.088s
sys 0m0.334s
$ time $(dd if=/dev/zero of=/tmpfs/test.raw bs=1M count=1 && sync)
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00184073 s, 570 MB/s
real 0m0.019s
user 0m0.000s
sys 0m0.010s
Причина не только в этом. При использовании мелких блоков происходит частое переключение между чтением и записью. Вот пример, где те же команды при записи в файл, находящийся в tmpfs, показывает похожую с жестким диском разницу: