Sí, es más eficiente
La forma más fácil de probar es aumentar el conteo para decir 500000 y cronometrarlo:
> time bash s1.sh; time bash s2.sh
bash s1.sh 16,47s user 10,00s system 99% cpu 26,537 total
bash s2.sh 10,51s user 3,50s system 99% cpu 14,008 total
strace (1 )revela por qué (tenemos uno simple write
, en lugar de open
+5*fcntl
+2*dup
+2*close
+write
):
para for i in {1..1000}; do >>x echo "$i"; done
obtenemos:
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "997\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "998\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "999\n", 4) = 4
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
open("x", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
write(1, "1000\n", 5) = 5
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
mientras que para exec 3>&1 1>x
quedamos mucho más limpios
write(1, "995\n", 4) = 4
write(1, "996\n", 4) = 4
write(1, "997\n", 4) = 4
write(1, "998\n", 4) = 4
write(1, "999\n", 4) = 4
write(1, "1000\n", 5) = 5
Pero tenga en cuenta que la diferencia no se debe a "usar un FD", sino al lugar donde realiza la redirección. Por ejemplo, si tuviera que hacer for i in {1..1000}; do echo "$i"; done > x
, obtendría prácticamente el mismo rendimiento que su segundo ejemplo:
bash s3.sh 10,35s user 3,70s system 100% cpu 14,042 total
Предположительно, вы увидите ошибку «Нет свободного места на устройстве»:
# truncate -s 100M foobar.img
# mkfs.ext4 foobar.img
Creating filesystem with 102400 1k blocks and 25688 inodes
---> number of inodes determined at mkfs time ^^^^^
# mount -o loop foobar.img loop/
# touch loop/{1..25688}
touch: cannot touch 'loop/25678': No space left on device
touch: cannot touch 'loop/25679': No space left on device
touch: cannot touch 'loop/25680': No space left on device
И на практике вы достигнете этого предела намного раньше, чем «4 миллиарда файлов». Проверьте свои файловые системы с помощью df -h
и df -i
, чтобы узнать, сколько места осталось.
# df -h loop/
Filesystem Size Used Avail Use% Mounted on
/dev/loop0 93M 2.1M 84M 3% /dev/shm/loop
# df -i loop/
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/loop0 25688 25688 0 100% /dev/shm/loop
В этом примере, если размер ваших файлов в среднем меньше 4 КБ, пространство inode -заканчивается гораздо раньше, чем пространство хранилища -. Можно указать другое соотношение(mke2fs -N number-of-inodes
или -i bytes-per-inode
или -T usage-type
, как определено в/etc/mke2fs.conf
).