파일 설명자는 파일 쓰기에 최적화되어 있습니까?

파일 설명자는 파일 쓰기에 최적화되어 있습니까?

파일 설명자에 쓰는 것이 아니라 파일에 직접 명령을 인쇄하는 것과 동일합니까?

삽화

파일에 직접 쓰기:

for i in {1..1000}; do >>x echo "$i"; done

fd를 사용하십시오:

exec 3>&1 1>x
for i in {1..1000}; do echo "$i"; done
exec 1>&3 3>&-

후자가 더 효율적인가요?

답변1

exec루프 전에 파일을 여는 명령을 사용하는 것과 루프 내부에 리디렉션을 넣는 것의 주요 차이점은 전자는 파일 설명자를 한 번만 설정하면 되는 반면, 후자는 루프가 반복될 때마다 파일을 열고 닫는다는 것입니다.

한 번 수행하는 것이 더 효율적일 수 있지만 루프 내에서 외부 명령을 실행하는 경우 명령 시작에 따른 비용 차이가 사라질 수 있습니다. ( echo아마도 여기에 내장되어 있으므로 적용되지 않습니다)

출력이 일반 파일이 아닌 파일로 전송되는 경우(예: x명명된 파이프인 경우) 파일을 열고 닫는 동작이 다른 프로세스에 표시될 수 있으므로 동작에 차이가 있을 수 있습니다.


command에 의한 리디렉션과 command에 의한 리디렉션 사이에는 실제로 차이가 없습니다. exec둘 다 파일을 열고 파일 설명자 번호를 처리합니다.

이 두 가지는 open()파일이자 write()파일이므로 거의 동일해야 합니다. (단, 명령 실행 중에는 fd 1이 다르게 저장됩니다.)

for i in {1..1000}; do 
    >>x echo "$i"
done


for i in {1..1000}; do
    exec 3>&1 1>>x         # assuming fd 3 is available
    echo "$i"              # here, fd 3 is visible to the command
    exec 1>&3 3>&-
done

답변2

응, 그게 더 효율적이야

이를 테스트하는 가장 간단한 방법은 개수를 500000으로 늘리고 시간을 측정하는 것입니다.

> 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

writestrace(1)은 이유를 밝힙니다( 대신 간단한 open+5* fcntl+2* dup+2* close+ 가 있습니다 write):

우리는 다음 for i in {1..1000}; do >>x echo "$i"; done을 얻습니다:

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

그리고 exec 3>&1 1>x우리는 더 깨끗해질 것입니다

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

그러나 차이점은 "FD 사용" 때문이 아니라 리디렉션을 수행하는 위치에 따른 것입니다. 예를 들어 이렇게 하면 for i in {1..1000}; do echo "$i"; done > x 두 번째 예와 거의 동일한 성능을 얻을 수 있습니다.

bash s3.sh  10,35s user 3,70s system 100% cpu 14,042 total

답변3

이 스레드에 몇 가지 새로운 정보를 요약하고 추가하기 위해 효율성에 따라 정렬된 네 가지 방법을 비교했습니다. 두 가지 테스트 시리즈를 기반으로 시간 측정(사용자 + 시스템)으로 100만 반복의 효율성을 추정합니다.

  1. 이 두 가지는 거의 동일합니다.
    • 단순 >루프 리디렉션(시간:100%)
    • exec전체 루프는 한 번 사용됩니다(시간:~100%)
  2. 각 반복 에 대해 >>(시간:200% - 250%)
  3. 각 반복 에 대해 exec(시간:340% - 480%)

결론은 이렇습니다.

하나 있다작은exec간단한 리디렉션과 함께 사용합니다 (예 >>: . (단순한 것이 더 저렴함). 단일 명령 실행 수준에서는 나타나지 않지만 반복 횟수가 증가할수록 차이가 눈에 띕니다. 명령의 실행 가중치가 섀도우로 리디렉션되지만 ikkachu가 다른 답변에서 알 수 있듯이 차이점.

관련 정보